hookehuyr

refactor(StudyDetailPage): 重构页面布局以优化用户体验

将视频播放和标签页区域固定,滚动区域独立,提升页面交互流畅性。添加标签页切换时的平滑滚动效果,优化用户浏览体验。
...@@ -4,54 +4,64 @@ ...@@ -4,54 +4,64 @@
4 --> 4 -->
5 <template> 5 <template>
6 <div class="study-detail-page bg-gradient-to-b from-green-50/70 to-white/90 min-h-screen pb-20"> 6 <div class="study-detail-page bg-gradient-to-b from-green-50/70 to-white/90 min-h-screen pb-20">
7 - <div v-if="course" class="flex flex-col"> 7 + <div v-if="course" class="flex flex-col h-screen">
8 - <!-- 视频播放区域 --> 8 + <!-- 固定区域:视频播放和标签页 -->
9 - <div class="w-full bg-black relative"> 9 + <div class="fixed top-0 left-0 right-0 z-10 top-wrapper">
10 - <!-- 视频封面和播放按钮 --> 10 + <!-- 视频播放区域 -->
11 - <div v-if="!isPlaying" class="relative w-full" style="aspect-ratio: 16/9;"> 11 + <div class="w-full bg-black relative">
12 - <img :src="course.cover" :alt="course.title" class="w-full h-full object-cover" /> 12 + <!-- 视频封面和播放按钮 -->
13 - <div class="absolute inset-0 flex items-center justify-center cursor-pointer" @click="startPlay"> 13 + <div v-if="!isPlaying" class="relative w-full" style="aspect-ratio: 16/9;">
14 - <div 14 + <img :src="course.cover" :alt="course.title" class="w-full h-full object-cover" />
15 - class="w-24 h-24 rounded-full bg-black/50 flex items-center justify-center hover:bg-black/70 transition-colors"> 15 + <div class="absolute inset-0 flex items-center justify-center cursor-pointer"
16 - <font-awesome-icon icon="circle-play" class="text-5xl text-white" style="font-size: 3rem;" /> 16 + @click="startPlay">
17 + <div
18 + class="w-24 h-24 rounded-full bg-black/50 flex items-center justify-center hover:bg-black/70 transition-colors">
19 + <font-awesome-icon icon="circle-play" class="text-5xl text-white"
20 + style="font-size: 3rem;" />
21 + </div>
17 </div> 22 </div>
18 </div> 23 </div>
24 + <!-- 视频播放器 -->
25 + <VideoPlayer v-show="isPlaying" ref="videoPlayerRef" :video-url="course.videoUrl" :autoplay="false"
26 + @onPlay="handleVideoPlay" @onPause="handleVideoPause" />
27 + </div>
28 + <!-- 标签页区域 -->
29 + <div class="px-4 py-3 bg-white">
30 + <van-tabs v-model:active="activeTab" sticky animated swipeable shrink @change="handleTabChange">
31 + <van-tab title="介绍" name="intro">
32 + </van-tab>
33 + <van-tab :title-style="{ 'min-width': '50%' }" name="comments">
34 + <template #title>评论(999)</template>
35 + </van-tab>
36 + </van-tabs>
19 </div> 37 </div>
20 - <!-- 视频播放器 -->
21 - <VideoPlayer v-show="isPlaying" ref="videoPlayerRef" :video-url="course.videoUrl" :autoplay="false"
22 - @onPlay="handleVideoPlay" @onPause="handleVideoPause" />
23 </div> 38 </div>
24 39
25 - <!-- 标签页区域 --> 40 + <!-- 滚动区域:介绍和评论内容 -->
26 - <div class="px-4 py-3"> 41 + <div class="overflow-y-auto flex-1" :style="{paddingTop: topWrapperHeight, height: 'calc(100vh - ' + topWrapperHeight + ')'}">
27 - <van-tabs v-model:active="activeTab" sticky animated swipeable> 42 + <div id="intro" class="py-4 px-4">
28 - <!-- 介绍标签页 --> 43 + <h1 class="text-lg font-bold mb-2">{{ course.title }}</h1>
29 - <van-tab title="介绍" name="intro"> 44 + <div class="text-gray-500 text-sm flex items-center gap-2">
30 - <div class="py-4"> 45 + <span>{{ course.date }}</span>
31 - <h1 class="text-xl font-bold mb-2">{{ course.title }}</h1> 46 + <span class="text-gray-300">|</span>
32 - <div class="text-gray-500 text-sm"> 47 + <span>{{ course.studyCount || 0 }}次学习</span>
33 - {{ course.studyCount || 0 }}次学习 48 + </div>
34 - </div> 49 + </div>
35 - </div> 50 +
36 - </van-tab> 51 + <div class="h-2 bg-gray-100"></div>
37 - 52 +
38 - <!-- 评论标签页 --> 53 + <div id="comment" class="py-4 px-4 space-y-4">
39 - <van-tab title="评论" :title-style="{ 'min-width': '50%' }" name="comments"> 54 + <div v-for="comment in comments" :key="comment.id" class="bg-white rounded-lg p-4 shadow-sm">
40 - <div class="py-4 space-y-4"> 55 + <div class="flex items-center mb-2">
41 - <div v-for="comment in comments" :key="comment.id" 56 + <img :src="comment.avatar" class="w-8 h-8 rounded-full mr-2" />
42 - class="bg-white rounded-lg p-4 shadow-sm"> 57 + <div>
43 - <div class="flex items-center mb-2"> 58 + <div class="font-medium">{{ comment.username }}</div>
44 - <img :src="comment.avatar" class="w-8 h-8 rounded-full mr-2" /> 59 + <div class="text-gray-500 text-xs">{{ comment.time }}</div>
45 - <div>
46 - <div class="font-medium">{{ comment.username }}</div>
47 - <div class="text-gray-500 text-xs">{{ comment.time }}</div>
48 - </div>
49 - </div>
50 - <div class="text-gray-700">{{ comment.content }}</div>
51 </div> 60 </div>
52 </div> 61 </div>
53 - </van-tab> 62 + <div class="text-gray-700">{{ comment.content }}</div>
54 - </van-tabs> 63 + </div>
64 + </div>
55 </div> 65 </div>
56 66
57 <!-- 底部操作栏 --> 67 <!-- 底部操作栏 -->
...@@ -126,9 +136,15 @@ const comments = ref([ ...@@ -126,9 +136,15 @@ const comments = ref([
126 // 设置页面标题 136 // 设置页面标题
127 useTitle('学习详情'); 137 useTitle('学习详情');
128 138
129 -// 获取课程详情 139 +const topWrapperHeight = ref(0);
130 140
131 onMounted(() => { 141 onMounted(() => {
142 + nextTick(() => {
143 + const topWrapper = document.querySelector('.top-wrapper');
144 + if (topWrapper) {
145 + topWrapperHeight.value = topWrapper.clientHeight + 'px';
146 + }
147 + })
132 const courseId = route.params.id; 148 const courseId = route.params.id;
133 if (courseId) { 149 if (courseId) {
134 // TODO: 这里需要替换为实际的API调用 150 // TODO: 这里需要替换为实际的API调用
...@@ -141,7 +157,8 @@ onMounted(() => { ...@@ -141,7 +157,8 @@ onMounted(() => {
141 studyTime: 3600, 157 studyTime: 3600,
142 type: '视频课程', 158 type: '视频课程',
143 studyCount: 1896, 159 studyCount: 1896,
144 - cover: 'https://fastly.jsdelivr.net/npm/@vant/assets/cat.jpeg' 160 + cover: 'https://fastly.jsdelivr.net/npm/@vant/assets/cat.jpeg',
161 + date: '2024-12-04'
145 }; 162 };
146 } 163 }
147 }) 164 })
...@@ -160,4 +177,20 @@ const submitComment = () => { ...@@ -160,4 +177,20 @@ const submitComment = () => {
160 177
161 newComment.value = ''; 178 newComment.value = '';
162 }; 179 };
180 +
181 +// 处理标签页切换
182 +const handleTabChange = (name) => {
183 + nextTick(() => {
184 + const element = document.getElementById(name === 'intro' ? 'intro' : 'comment');
185 + if (element && container) {
186 + const topOffset = element.offsetTop - parseInt(topWrapperHeight.value);
187 + window.scrollTo({
188 + top: topOffset,
189 + behavior: 'smooth'
190 + });
191 + }
192 + })
193 +};
194 +
195 +
163 </script> 196 </script>
......