StudyCoursePage.vue
6.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
<!--
* @Date: 2024-01-17
* @Description: 课程详情页面
-->
<template>
<div class="study-course-page bg-gradient-to-b from-green-50/70 to-white/90 min-h-screen">
<div v-if="course" class="flex flex-col h-screen">
<!-- 固定区域:课程封面和标签页 -->
<div class="fixed top-0 left-0 right-0 z-10 top-wrapper bg-white">
<!-- 课程封面区域 -->
<van-image
class="w-full aspect-video object-cover"
:src="course?.coverImage"
:alt="course?.title"
/>
<div class="p-4">
<h1 class="text-black text-xl font-bold mb-2">{{ course?.title }}</h1>
<div class="flex items-center text-gray-500 text-sm">
<span>已更新 20期</span>
<span class="mx-2">|</span>
<span>116人订阅</span>
</div>
</div>
<div class="h-2 bg-gray-100"></div>
<!-- 标签页区域 -->
<div class="py-3 bg-white">
<van-tabs v-model:active="activeTab" sticky animated swipeable shrink @change="handleTabChange">
<van-tab title="详情" name="detail">
</van-tab>
<van-tab title="目录" name="catalog">
</van-tab>
<van-tab title="课程互动" name="interaction">
</van-tab>
</van-tabs>
</div>
</div>
<!-- 滚动区域:详情、目录和互动内容 -->
<div class="overflow-y-auto flex-1" :style="{paddingTop: topWrapperHeight, height: 'calc(100vh - ' + topWrapperHeight + ')'}">
<!-- 详情区域 -->
<div id="detail" class="py-4 px-4">
<div class="text-gray-700 text-sm leading-relaxed" v-html="course?.description"></div>
<van-empty description="暂无详情" />
</div>
<div class="h-2 bg-gray-100"></div>
<!-- 目录区域 -->
<div id="catalog" class="py-4">
<div class="space-y-4">
<div v-for="(lesson, index) in course?.lessons" :key="index" class="bg-white p-4 cursor-pointer hover:bg-gray-50 transition-colors border-b border-gray-200 relative">
<div v-if="lesson.progress > 0 && lesson.progress < 100" class="absolute top-2 right-2 px-2 py-1 bg-green-100 text-green-600 text-xs rounded">上次看到</div>
<div class="text-black text-base font-medium mb-2">{{ lesson.title }}</div>
<div class="flex items-center text-sm text-gray-500">
<span>视频</span>
<span>2024-10-22</span>
<span class="mx-2">|</span>
<span>1897次学习</span>
<span class="mx-2">|</span>
<span>已学习{{ lesson.progress }}%</span>
</div>
</div>
</div>
</div>
<div class="h-2 bg-gray-100"></div>
<!-- 互动区域 -->
<div id="interaction" class="py-4 px-4">
<div class="bg-white rounded-lg p-4 mb-4 cursor-pointer">
<div class="flex items-center justify-between">
<div class="flex items-center gap-3">
<van-icon size="3rem" name="calendar-o" class="text-xl text-gray-600" />
<div>
<div class="text-base font-medium">打卡</div>
<div class="text-sm text-gray-500">关联7个打卡</div>
</div>
</div>
<van-icon name="arrow" class="text-gray-400" />
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, nextTick, onUnmounted } from 'vue';
import { useTitle } from '@vueuse/core';
// 页面标题
useTitle('课程详情');
// 当前激活的标签页
const activeTab = ref('detail');
const topWrapperHeight = ref(0);
// 处理滚动事件
const handleScroll = () => {
const detailElement = document.getElementById('detail');
const catalogElement = document.getElementById('catalog');
const interactionElement = document.getElementById('interaction');
if (!detailElement || !catalogElement || !interactionElement) return;
const scrollTop = window.scrollY;
const catalogOffset = catalogElement.offsetTop - parseInt(topWrapperHeight.value);
const interactionOffset = interactionElement.offsetTop - parseInt(topWrapperHeight.value);
// 根据滚动位置更新activeTab
if (scrollTop >= interactionOffset) {
activeTab.value = 'interaction';
} else if (scrollTop >= catalogOffset) {
activeTab.value = 'catalog';
} else {
activeTab.value = 'detail';
}
};
// 处理标签页切换
const handleTabChange = (name) => {
nextTick(() => {
const element = document.getElementById(name);
if (element) {
const topOffset = element.offsetTop - parseInt(topWrapperHeight.value);
window.scrollTo({
top: topOffset,
behavior: 'smooth'
});
}
});
};
onMounted(() => {
nextTick(() => {
const topWrapper = document.querySelector('.top-wrapper');
if (topWrapper) {
topWrapperHeight.value = topWrapper.clientHeight + 'px';
}
// 添加滚动监听
window.addEventListener('scroll', handleScroll);
});
});
// 在组件卸载时移除滚动监听
onUnmounted(() => {
window.removeEventListener('scroll', handleScroll);
});
// 课程数据
const course = ref({
title: '开学礼·止的智慧·心法老师·20241001',
coverImage: 'https://fastly.jsdelivr.net/npm/@vant/assets/cat.jpeg',
updateTime: '2024.01.17',
viewCount: 1897,
description: '这是一门关于心法的课程,帮助学员掌握止的智慧...',
lessons: [
{
title: '第一课:止的基础',
duration: '45分钟',
progress: 100
},
{
title: '第二课:止的技巧',
duration: '50分钟',
progress: 60
},
{
title: '第三课:止的应用',
duration: '40分钟',
progress: 0
}
]
});
</script>
<style scoped>
.study-course-page {
min-height: 100vh;
}
.course-header {
height: auto;
}
.course-tabs {
background-color: #fff;
}
.course-catalog {
background-color: #fff;
}
</style>