增加页面滚动复位功能
This commit is contained in:
+47
-6
@@ -67,19 +67,60 @@ const router = createRouter({
|
||||
scrollBehavior(to, from, savedPosition) {
|
||||
// 如果有保存的位置(浏览器前进/后退),则恢复到该位置
|
||||
if (savedPosition) {
|
||||
return savedPosition
|
||||
return new Promise((resolve) => {
|
||||
// 延迟一点时间确保页面加载完成
|
||||
setTimeout(() => {
|
||||
resolve(savedPosition)
|
||||
}, 100)
|
||||
})
|
||||
}
|
||||
|
||||
// 检查是否有保存的自定义滚动位置
|
||||
const savedScrollPosition = sessionStorage.getItem(`scroll_${to.fullPath}`)
|
||||
if (savedScrollPosition) {
|
||||
try {
|
||||
const position = JSON.parse(savedScrollPosition)
|
||||
// 清除保存的位置,避免重复使用
|
||||
sessionStorage.removeItem(`scroll_${to.fullPath}`)
|
||||
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve(position)
|
||||
}, 100)
|
||||
})
|
||||
} catch (error) {
|
||||
console.warn('解析保存的滚动位置失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 如果有锚点,则滚动到锚点位置
|
||||
if (to.hash) {
|
||||
return {
|
||||
el: to.hash,
|
||||
behavior: 'smooth'
|
||||
}
|
||||
return new Promise((resolve) => {
|
||||
// 使用 nextTick 确保 DOM 更新完成
|
||||
setTimeout(() => {
|
||||
const element = document.querySelector(to.hash)
|
||||
|
||||
if (element) {
|
||||
resolve({
|
||||
el: to.hash,
|
||||
behavior: 'smooth',
|
||||
top: 0 // 添加 top 偏移,确保元素完全可见
|
||||
})
|
||||
} else {
|
||||
// 如果元素不存在,滚动到顶部
|
||||
resolve({ top: 0 })
|
||||
}
|
||||
}, 100)
|
||||
})
|
||||
}
|
||||
|
||||
// 否则滚动到页面顶部
|
||||
return { top: 0 }
|
||||
return new Promise((resolve) => {
|
||||
// 延迟一点时间,确保页面加载完成
|
||||
setTimeout(() => {
|
||||
resolve({ top: 0 })
|
||||
}, 100)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
/**
|
||||
* 滚动位置管理工具
|
||||
* 用于在页面跳转时保存和恢复滚动位置
|
||||
*/
|
||||
|
||||
/**
|
||||
* 保存当前页面的滚动位置
|
||||
* @param path 页面路径,用于标识保存的位置
|
||||
*/
|
||||
export function saveScrollPosition(path?: string): void {
|
||||
const currentPath = path || window.location.pathname + window.location.search
|
||||
const scrollPosition = {
|
||||
top: window.pageYOffset || document.documentElement.scrollTop,
|
||||
left: window.pageXOffset || document.documentElement.scrollLeft
|
||||
}
|
||||
|
||||
sessionStorage.setItem(`scroll_${currentPath}`, JSON.stringify(scrollPosition))
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存指定页面的滚动位置
|
||||
* @param path 页面路径
|
||||
* @param position 滚动位置
|
||||
*/
|
||||
export function saveScrollPositionForPath(path: string, position: { top: number; left: number }): void {
|
||||
sessionStorage.setItem(`scroll_${path}`, JSON.stringify(position))
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取保存的滚动位置
|
||||
* @param path 页面路径
|
||||
* @returns 滚动位置或null
|
||||
*/
|
||||
export function getSavedScrollPosition(path?: string): { top: number; left: number } | null {
|
||||
const currentPath = path || window.location.pathname + window.location.search
|
||||
const saved = sessionStorage.getItem(`scroll_${currentPath}`)
|
||||
|
||||
if (saved) {
|
||||
try {
|
||||
return JSON.parse(saved)
|
||||
} catch (error) {
|
||||
console.warn('解析保存的滚动位置失败:', error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除保存的滚动位置
|
||||
* @param path 页面路径,如果不提供则清除当前页面的
|
||||
*/
|
||||
export function clearScrollPosition(path?: string): void {
|
||||
const currentPath = path || window.location.pathname + window.location.search
|
||||
sessionStorage.removeItem(`scroll_${currentPath}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除所有保存的滚动位置
|
||||
*/
|
||||
export function clearAllScrollPositions(): void {
|
||||
const keys = Object.keys(sessionStorage)
|
||||
keys.forEach(key => {
|
||||
if (key.startsWith('scroll_')) {
|
||||
sessionStorage.removeItem(key)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 滚动到指定位置
|
||||
* @param position 滚动位置
|
||||
* @param behavior 滚动行为
|
||||
*/
|
||||
export function scrollToPosition(
|
||||
position: { top: number; left: number },
|
||||
behavior: ScrollBehavior = 'auto'
|
||||
): void {
|
||||
window.scrollTo({
|
||||
top: position.top,
|
||||
left: position.left,
|
||||
behavior
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 在页面即将卸载时保存滚动位置
|
||||
* 通常用于 beforeunload 事件
|
||||
*/
|
||||
export function saveScrollPositionBeforeUnload(): void {
|
||||
saveScrollPosition()
|
||||
}
|
||||
|
||||
/**
|
||||
* 在页面加载完成后恢复滚动位置
|
||||
* 通常用于 mounted 生命周期
|
||||
*/
|
||||
export function restoreScrollPosition(path?: string): void {
|
||||
const savedPosition = getSavedScrollPosition(path)
|
||||
if (savedPosition) {
|
||||
// 延迟一点时间确保页面内容完全加载
|
||||
setTimeout(() => {
|
||||
scrollToPosition(savedPosition, 'auto')
|
||||
// 恢复后清除保存的位置
|
||||
clearScrollPosition(path)
|
||||
}, 100)
|
||||
}
|
||||
}
|
||||
@@ -172,6 +172,7 @@ import { useAuthStore } from '@/stores/auth';
|
||||
import artistService from '@/services/artist';
|
||||
import downloadService from '@/services/download';
|
||||
import { getImageProxyUrl } from '@/services/api';
|
||||
import { saveScrollPosition, restoreScrollPosition } from '@/utils/scrollManager';
|
||||
import type { Artist, Artwork } from '@/types';
|
||||
|
||||
import ArtworkCard from '@/components/artwork/ArtworkCard.vue';
|
||||
@@ -512,6 +513,9 @@ const getImageUrl = getImageProxyUrl;
|
||||
|
||||
// 点击作品
|
||||
const handleArtworkClick = (artwork: Artwork) => {
|
||||
// 保存当前页面的滚动位置
|
||||
saveScrollPosition(route.fullPath);
|
||||
|
||||
// 传递作者ID、作品类型和当前页面信息,用于导航
|
||||
router.push({
|
||||
path: `/artwork/${artwork.id}`,
|
||||
@@ -519,7 +523,8 @@ const handleArtworkClick = (artwork: Artwork) => {
|
||||
artistId: artist.value?.id.toString(),
|
||||
artworkType: artworkType.value,
|
||||
page: currentPage.value.toString(),
|
||||
returnUrl: route.fullPath
|
||||
returnUrl: route.fullPath,
|
||||
scrollTop: (window.scrollY || document.documentElement.scrollTop).toString()
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -602,6 +607,11 @@ onMounted(async () => {
|
||||
} else {
|
||||
await fetchArtworks(1);
|
||||
}
|
||||
|
||||
// 恢复滚动位置(延迟执行确保页面内容完全加载)
|
||||
setTimeout(() => {
|
||||
restoreScrollPosition(route.fullPath);
|
||||
}, 200);
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ import artworkService from '@/services/artwork';
|
||||
import artistService from '@/services/artist';
|
||||
import downloadService from '@/services/download';
|
||||
import { getApiBaseUrl, getImageProxyUrl } from '@/services/api';
|
||||
import { saveScrollPositionForPath } from '@/utils/scrollManager';
|
||||
import type { Artwork, DownloadTask } from '@/types';
|
||||
import ErrorMessage from '@/components/common/ErrorMessage.vue';
|
||||
import LoadingSpinner from '@/components/common/LoadingSpinner.vue';
|
||||
@@ -360,10 +361,24 @@ const navigateToNext = () => {
|
||||
|
||||
// 返回作者页面
|
||||
const goBackToArtist = () => {
|
||||
if (route.query.returnUrl) {
|
||||
router.push(route.query.returnUrl as string);
|
||||
} else if (route.query.artistId) {
|
||||
router.push(`/artist/${route.query.artistId}`);
|
||||
const returnUrl = route.query.returnUrl as string;
|
||||
const targetPath = returnUrl || `/artist/${route.query.artistId}`;
|
||||
|
||||
if (targetPath) {
|
||||
// 获取当前保存的滚动位置(如果有的话)
|
||||
const savedScrollKey = `scroll_${targetPath}`;
|
||||
const savedPosition = sessionStorage.getItem(savedScrollKey);
|
||||
|
||||
// 如果没有保存的滚动位置,设置一个默认位置(通常是之前访问时的位置)
|
||||
if (!savedPosition && route.query.scrollTop) {
|
||||
const scrollPosition = {
|
||||
top: parseInt(route.query.scrollTop as string) || 0,
|
||||
left: 0
|
||||
};
|
||||
saveScrollPositionForPath(targetPath, scrollPosition);
|
||||
}
|
||||
|
||||
router.push(targetPath);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -57,14 +57,16 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import { artworkService } from '@/services/artwork';
|
||||
import { getImageProxyUrl } from '@/services/api';
|
||||
import { saveScrollPosition, restoreScrollPosition } from '@/utils/scrollManager';
|
||||
import type { Artwork } from '@/types';
|
||||
|
||||
import ArtworkCard from '@/components/artwork/ArtworkCard.vue';
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
|
||||
// 响应式数据
|
||||
const artworks = ref<(Artwork & { loaded?: boolean; error?: boolean })[]>([]);
|
||||
@@ -138,7 +140,16 @@ const loadMore = () => {
|
||||
|
||||
// 处理作品点击
|
||||
const handleArtworkClick = (artwork: Artwork) => {
|
||||
router.push(`/artwork/${artwork.id}`);
|
||||
// 保存当前页面的滚动位置
|
||||
saveScrollPosition(route.fullPath);
|
||||
|
||||
router.push({
|
||||
path: `/artwork/${artwork.id}`,
|
||||
query: {
|
||||
returnUrl: route.fullPath,
|
||||
scrollTop: (window.scrollY || document.documentElement.scrollTop).toString()
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 清除错误
|
||||
@@ -147,8 +158,13 @@ const clearError = () => {
|
||||
};
|
||||
|
||||
// 页面加载时获取数据
|
||||
onMounted(() => {
|
||||
fetchBookmarks();
|
||||
onMounted(async () => {
|
||||
await fetchBookmarks();
|
||||
|
||||
// 恢复滚动位置
|
||||
setTimeout(() => {
|
||||
restoreScrollPosition(route.fullPath);
|
||||
}, 200);
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -75,6 +75,7 @@ import { useRoute, useRouter } from 'vue-router';
|
||||
import { useAuthStore } from '@/stores/auth';
|
||||
import rankingService from '@/services/ranking';
|
||||
import downloadService from '@/services/download';
|
||||
import { saveScrollPosition, restoreScrollPosition } from '@/utils/scrollManager';
|
||||
import type { Artwork } from '@/types';
|
||||
|
||||
import ArtworkCard from '@/components/artwork/ArtworkCard.vue';
|
||||
@@ -291,13 +292,17 @@ const goToPage = (page: number) => {
|
||||
|
||||
// 点击作品
|
||||
const handleArtworkClick = (artwork: Artwork) => {
|
||||
// 保存当前页面的滚动位置
|
||||
saveScrollPosition(route.fullPath);
|
||||
|
||||
router.push({
|
||||
path: `/artwork/${artwork.id}`,
|
||||
query: {
|
||||
rankingMode: currentMode.value,
|
||||
rankingType: currentType.value,
|
||||
page: currentPage.value.toString(),
|
||||
returnUrl: route.fullPath
|
||||
returnUrl: route.fullPath,
|
||||
scrollTop: (window.scrollY || document.documentElement.scrollTop).toString()
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -411,6 +416,11 @@ onMounted(async () => {
|
||||
} else {
|
||||
await fetchRankingData(1);
|
||||
}
|
||||
|
||||
// 恢复滚动位置
|
||||
setTimeout(() => {
|
||||
restoreScrollPosition(route.fullPath);
|
||||
}, 200);
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -197,10 +197,11 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch } from 'vue';
|
||||
import { ref, computed, watch, onMounted } from 'vue';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import { useAuthStore } from '@/stores/auth';
|
||||
import artworkService from '@/services/artwork';
|
||||
import { saveScrollPosition, restoreScrollPosition } from '@/utils/scrollManager';
|
||||
import type { Artwork, SearchParams } from '@/types';
|
||||
|
||||
import ArtworkCard from '@/components/artwork/ArtworkCard.vue';
|
||||
@@ -348,7 +349,16 @@ const goToPage = (page: number) => {
|
||||
};
|
||||
|
||||
const handleArtworkClick = (artwork: Artwork) => {
|
||||
router.push(`/artwork/${artwork.id}`);
|
||||
// 保存当前页面的滚动位置
|
||||
saveScrollPosition(route.fullPath);
|
||||
|
||||
router.push({
|
||||
path: `/artwork/${artwork.id}`,
|
||||
query: {
|
||||
returnUrl: route.fullPath,
|
||||
scrollTop: (window.scrollY || document.documentElement.scrollTop).toString()
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 作品ID搜索
|
||||
@@ -740,6 +750,13 @@ watch(() => route.query, () => {
|
||||
if (urlSort) searchSort.value = urlSort as 'date_desc' | 'date_asc' | 'popular_desc';
|
||||
if (urlDuration) searchDuration.value = urlDuration as 'all' | 'within_last_day' | 'within_last_week' | 'within_last_month';
|
||||
}, { immediate: true });
|
||||
|
||||
// 组件挂载时恢复滚动位置
|
||||
onMounted(() => {
|
||||
setTimeout(() => {
|
||||
restoreScrollPosition(route.fullPath);
|
||||
}, 200);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
Reference in New Issue
Block a user