Toggle navigation
Toggle navigation
This project
Loading...
Sign in
Hooke
/
mlaj
Go to a project
Toggle navigation
Toggle navigation pinning
Projects
Groups
Snippets
Help
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Snippets
Network
Create a new issue
Builds
Commits
Issue Boards
Authored by
hookehuyr
2026-01-22 18:43:38 +0800
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
5b3fcd7839f458d02b1a1e9b55b571aa09139819
5b3fcd78
1 parent
5ba86dee
feat(课程页面): 添加课程目录滚动位置记忆功能
实现从课程详情页返回时恢复滚动位置的功能 添加数据属性标记课程目录项 新增滚动状态存储和恢复相关方法
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
93 additions
and
1 deletions
src/views/profile/StudyCoursePage.vue
src/views/profile/StudyCoursePage.vue
View file @
5b3fcd7
...
...
@@ -56,7 +56,7 @@
<div class="text-gray-700 text-sm leading-relaxed" v-html="course?.learning_goal"></div>
<br />
</div>
<van-empty v-
if="!course?.feature && !course?.highlights && course?.learning_goal"
description="暂无详情" />
<van-empty v-
else
description="暂无详情" />
</div>
<div class="h-2 bg-gray-100"></div>
...
...
@@ -64,6 +64,7 @@
<div id="catalog" class="py-4">
<div v-if="course_lessons.length" class="space-y-4">
<div v-for="(lesson, index) in course_lessons" :key="index"
:data-lesson-id="lesson.id"
@click="goToStudyDetail(lesson.id)"
class="bg-white p-4 cursor-pointer hover:bg-gray-50 transition-colors border-b border-gray-200 relative">
<div class="text-black text-base font-medium mb-2">{{ lesson.title }}</div>
...
...
@@ -257,6 +258,94 @@ const course_type_maps = ref({
const task_list = ref([]);
const timeout_task_list = ref([]);
/**
* 生成课程滚动状态存储键
* @returns {string} 滚动状态存储键
* 注释:用于在会话存储中保存和恢复课程目录滚动位置
*/
const get_scroll_store_key = () => {
const course_id = router.currentRoute.value.params.id || '';
return `study_course_scroll_${course_id}`;
};
/**
* 判断是否需要恢复滚动位置
* @returns {boolean} 是否需要恢复滚动
* 注释:根据历史状态判断是否从课程详情页返回
*/
const should_restore_scroll = () => {
const forward = window.history && window.history.state ? window.history.state.forward : null;
if (!forward) return false;
return String(forward).includes('studyDetail');
};
/**
* 保存当前滚动位置
* @param {string} lesson_id - 当前课程目录项ID
* 注释:在用户滚动到目录项时调用,记录滚动位置和目录项ID
*/
const save_scroll_state = (lesson_id) => {
const key = get_scroll_store_key();
const payload = {
scroll_y: window.scrollY || 0,
lesson_id: lesson_id || '',
saved_at: Date.now(),
};
sessionStorage.setItem(key, JSON.stringify(payload));
};
/**
* 滚动到指定目录项
* @param {string} lesson_id - 目标目录项ID
* @returns {boolean} 是否成功滚动
* 注释:根据目录项ID找到元素并滚动到其位置,考虑顶部导航栏高度
*/
const scroll_to_lesson = (lesson_id) => {
if (!lesson_id) return false;
const el = document.querySelector(`[data-lesson-id="${lesson_id}"]`);
if (!el) return false;
const rect = el.getBoundingClientRect();
const buffer = 10;
const offset_top = (tabs_wrapper_height.value || 0) + buffer;
const top_offset = Math.max(0, rect.top + window.scrollY - offset_top);
window.scrollTo({ top: top_offset, left: 0, behavior: 'auto' });
return true;
};
/**
* 恢复滚动位置
* @returns {void}
* 注释:在组件挂载时调用,根据存储状态滚动到上次记录位置
*/
const restore_scroll_state = async () => {
if (!should_restore_scroll()) return;
const key = get_scroll_store_key();
const raw = sessionStorage.getItem(key);
if (!raw) return;
let state = null;
try {
state = JSON.parse(raw);
} catch (e) {
sessionStorage.removeItem(key);
return;
}
await nextTick();
await new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve)));
const restored = scroll_to_lesson(state.lesson_id);
if (!restored && typeof state.scroll_y === 'number') {
window.scrollTo({ top: state.scroll_y, left: 0, behavior: 'auto' });
}
sessionStorage.removeItem(key);
};
onMounted(async () => {
/**
* 组件挂载时获取课程详情
...
...
@@ -292,6 +381,8 @@ onMounted(async () => {
// 初始化标签容器宽度监听
initTabsContainerWidth();
handleScroll();
// 恢复滚动位置
restore_scroll_state();
});
// 在组件卸载时移除监听器
...
...
@@ -393,6 +484,7 @@ const handleTabChange = (name) => {
};
const goToStudyDetail = (lessonId) => {
save_scroll_state(lessonId);
router.push(`/studyDetail/${lessonId}`);
};
...
...
Please
register
or
login
to post a comment