hookehuyr

feat(课程详情页): 重构课程详情页布局和交互逻辑

- 将课程介绍移至标签页并设为默认标签
- 新增主讲教师标签页展示讲师信息
- 优化课程大纲交互,增加点击跳转功能
- 调整底部操作按钮逻辑,区分购买和查看课程状态
- 移除冗余代码,简化页面结构
1 <template> 1 <template>
2 <AppLayout :rightContent="rightContent" :has-title="false"> 2 <AppLayout :rightContent="rightContent" :has-title="false">
3 - <div class="pb-24"> 3 + <div class="pb-24 mb-6">
4 <!-- Course Image --> 4 <!-- Course Image -->
5 <div class="mb-4"> 5 <div class="mb-4">
6 <img :src="course?.cover || 'https://cdn.ipadbiz.cn/mlaj/images/default_block.png'" :alt="course?.title" 6 <img :src="course?.cover || 'https://cdn.ipadbiz.cn/mlaj/images/default_block.png'" :alt="course?.title"
...@@ -34,10 +34,10 @@ ...@@ -34,10 +34,10 @@
34 <!-- Course Main Content --> 34 <!-- Course Main Content -->
35 <div class="px-4"> 35 <div class="px-4">
36 <!-- Course Details --> 36 <!-- Course Details -->
37 - <FrostedGlass class="mb-4 p-4 rounded-xl" v-if="course?.introduce"> 37 + <!-- <FrostedGlass class="mb-4 p-4 rounded-xl" v-if="course?.introduce">
38 <h3 class="text-lg font-bold text-gray-800 mb-3">本课程介绍</h3> 38 <h3 class="text-lg font-bold text-gray-800 mb-3">本课程介绍</h3>
39 <p v-html="course?.introduce" class="text-gray-700 whitespace-pre-line"></p> 39 <p v-html="course?.introduce" class="text-gray-700 whitespace-pre-line"></p>
40 - </FrostedGlass> 40 + </FrostedGlass> -->
41 41
42 <!-- Tab Navigation --> 42 <!-- Tab Navigation -->
43 <FrostedGlass class="mb-6 rounded-xl overflow-hidden"> 43 <FrostedGlass class="mb-6 rounded-xl overflow-hidden">
...@@ -56,13 +56,31 @@ ...@@ -56,13 +56,31 @@
56 56
57 <!-- Tab Content --> 57 <!-- Tab Content -->
58 <div class="p-4"> 58 <div class="p-4">
59 - <div v-if="activeTab === '课程特色'"> 59 + <!-- <div v-if="activeTab === '课程特色'">
60 <div v-html="course?.feature"></div> 60 <div v-html="course?.feature"></div>
61 + </div> -->
62 +
63 + <div v-if="activeTab === '课程介绍'">
64 + <p v-html="course?.introduce" class="text-gray-700 whitespace-pre-line"></p>
65 + </div>
66 +
67 + <div v-if="activeTab === '主讲教师'">
68 + <div v-for="(item, index) in lecturers" :key="index" class="flex items-center" style="margin-bottom: 1rem;">
69 + <div class="w-16 h-16 rounded-full overflow-hidden mr-4">
70 + <img :src="item?.photo || 'https://cdn.ipadbiz.cn/mlaj/images/default_block.png'" alt="lecturer"
71 + class="w-full h-full object-cover" @error="handleImageError" />
72 + </div>
73 + <div>
74 + <h4 class="font-bold text-gray-900">{{ item?.name }}</h4>
75 + <p class="text-sm text-gray-600">{{ item?.educational }}</p>
76 + <p class="text-xs text-gray-500 mt-1">{{ item?.introduce }}</p>
77 + </div>
78 + </div>
61 </div> 79 </div>
62 80
63 <div v-if="activeTab === '课程大纲'"> 81 <div v-if="activeTab === '课程大纲'">
64 <div class="space-y-4"> 82 <div class="space-y-4">
65 - <div v-for="(item, index) in displayedSchedule" :key="index" class="border-l-2 border-green-500 pl-3"> 83 + <div v-for="(item, index) in displayedSchedule" :key="index" class="border-l-2 border-green-500 pl-3" @click="goToStudyDetail(item)">
66 <h4 class="font-medium text-gray-800">{{ item.title }}</h4> 84 <h4 class="font-medium text-gray-800">{{ item.title }}</h4>
67 <p class="text-sm text-gray-600 mt-1">{{ item.duration }}分钟 · {{ item.schedule_time }}个小节</p> 85 <p class="text-sm text-gray-600 mt-1">{{ item.duration }}分钟 · {{ item.schedule_time }}个小节</p>
68 </div> 86 </div>
...@@ -76,22 +94,22 @@ ...@@ -76,22 +94,22 @@
76 </div> 94 </div>
77 </div> 95 </div>
78 96
79 - <div v-if="activeTab === '课程亮点'"> 97 + <!-- <div v-if="activeTab === '课程亮点'">
80 <div class="space-y-3 text-gray-700"> 98 <div class="space-y-3 text-gray-700">
81 <div v-html="course?.highlights"></div> 99 <div v-html="course?.highlights"></div>
82 </div> 100 </div>
83 - </div> 101 + </div> -->
84 102
85 - <div v-if="activeTab === '学习目标'"> 103 + <!-- <div v-if="activeTab === '学习目标'">
86 <div class="space-y-3 text-gray-700"> 104 <div class="space-y-3 text-gray-700">
87 <div v-html="course?.learning_goal"></div> 105 <div v-html="course?.learning_goal"></div>
88 </div> 106 </div>
89 - </div> 107 + </div> -->
90 </div> 108 </div>
91 </FrostedGlass> 109 </FrostedGlass>
92 110
93 <!-- lecturers Introduction --> 111 <!-- lecturers Introduction -->
94 - <FrostedGlass class="mb-6 p-4 rounded-xl" v-if="lecturers.length"> 112 + <!-- <FrostedGlass class="mb-6 p-4 rounded-xl" v-if="lecturers.length">
95 <h3 class="text-lg font-bold text-gray-800 mb-3">主讲老师</h3> 113 <h3 class="text-lg font-bold text-gray-800 mb-3">主讲老师</h3>
96 <div v-for="(item, index) in lecturers" :key="index" class="flex items-center" style="margin-bottom: 1rem;"> 114 <div v-for="(item, index) in lecturers" :key="index" class="flex items-center" style="margin-bottom: 1rem;">
97 <div class="w-16 h-16 rounded-full overflow-hidden mr-4"> 115 <div class="w-16 h-16 rounded-full overflow-hidden mr-4">
...@@ -104,11 +122,25 @@ ...@@ -104,11 +122,25 @@
104 <p class="text-xs text-gray-500 mt-1">{{ item?.introduce }}</p> 122 <p class="text-xs text-gray-500 mt-1">{{ item?.introduce }}</p>
105 </div> 123 </div>
106 </div> 124 </div>
107 - </FrostedGlass> 125 + </FrostedGlass> -->
108 126
109 <!-- Student Reviews --> 127 <!-- Student Reviews -->
110 <FrostedGlass class="mb-6 p-4 rounded-xl"> 128 <FrostedGlass class="mb-6 p-4 rounded-xl">
111 - <h3 class="text-lg font-bold text-gray-800 mb-3">学员评价</h3> 129 + <div class="flex justify-between items-center mb-3">
130 + <h3 class="text-lg font-bold text-gray-800">学员评价</h3>
131 + <!-- 立即评论按钮 - 仅在已购买但未评价时显示 -->
132 + <van-button
133 + v-if="isPurchased && !isReviewed"
134 + @click="showReviewPopup = true"
135 + size="small"
136 + round
137 + color="linear-gradient(to right, #3b82f6, #2563eb)"
138 + class="shadow-sm text-xs px-4 py-1.5 min-w-[80px] hover:shadow-md transition-all duration-200"
139 + >
140 + <van-icon name="edit" size="12" class="mr-1" />
141 + 立即评论
142 + </van-button>
143 + </div>
112 <div class="flex items-center mb-3"> 144 <div class="flex items-center mb-3">
113 <div class="flex items-center mr-2"> 145 <div class="flex items-center mr-2">
114 <van-rate v-model="commentScore" readonly allow-half color="#facc15" void-color="#e5e7eb" size="20" /> 146 <van-rate v-model="commentScore" readonly allow-half color="#facc15" void-color="#e5e7eb" size="20" />
...@@ -182,7 +214,7 @@ ...@@ -182,7 +214,7 @@
182 </button> 214 </button>
183 </div> 215 </div>
184 <div class="flex items-center"> 216 <div class="flex items-center">
185 - <div class="mr-2"> 217 + <div v-if="!course?.is_buy" class="mr-2">
186 <div class="text-red-500 font-bold">¥{{ course?.price || 0 }}</div> 218 <div class="text-red-500 font-bold">¥{{ course?.price || 0 }}</div>
187 <div class="text-xs text-gray-400 line-through"> 219 <div class="text-xs text-gray-400 line-through">
188 ¥{{ Math.round((course?.price || 0) * 1.2) }} 220 ¥{{ Math.round((course?.price || 0) * 1.2) }}
...@@ -192,13 +224,9 @@ ...@@ -192,13 +224,9 @@
192 color="linear-gradient(to right, #22c55e, #16a34a)" class="shadow-md"> 224 color="linear-gradient(to right, #22c55e, #16a34a)" class="shadow-md">
193 {{ course?.price !== '0.00' ? '立即' : '免费' }}购买 225 {{ course?.price !== '0.00' ? '立即' : '免费' }}购买
194 </van-button> 226 </van-button>
195 - <van-button v-else-if="!isReviewed" @click="showReviewPopup = true" round block 227 + <van-button v-else @click="router.push(`/studyCourse/${course?.id}`)" round block
196 - color="linear-gradient(to right, #3b82f6, #2563eb)" class="shadow-md"> 228 + color="linear-gradient(to right, #22c55e, #16a34a)" class="shadow-md">
197 - 立即评论 229 + 查看课程
198 - </van-button>
199 - <van-button v-else @click="router.push(`/courses/${course?.id}/reviews`)" round block
200 - color="linear-gradient(to right, #6b7280, #4b5563)" class="shadow-md">
201 - 查看评论
202 </van-button> 230 </van-button>
203 </div> 231 </div>
204 </div> 232 </div>
...@@ -235,7 +263,7 @@ const { currentUser } = useAuth() ...@@ -235,7 +263,7 @@ const { currentUser } = useAuth()
235 263
236 const course = ref(null) 264 const course = ref(null)
237 const lecturers = ref([]) 265 const lecturers = ref([])
238 -const activeTab = ref('课程特色') 266 +const activeTab = ref('课程介绍')
239 // 是否收藏状态 267 // 是否收藏状态
240 const isFavorite = ref(false) 268 const isFavorite = ref(false)
241 // 是否已购买状态 269 // 是否已购买状态
...@@ -273,10 +301,11 @@ const curriculumItems = computed(() => { ...@@ -273,10 +301,11 @@ const curriculumItems = computed(() => {
273 if (!course.value) return []; 301 if (!course.value) return [];
274 302
275 return [ 303 return [
276 - { title: '课程特色', active: activeTab.value === '课程特色', show: !!course.value.feature }, 304 + { title: '课程介绍', active: activeTab.value === '课程介绍', show: !!course.value.introduce },
305 + { title: '主讲教师', active: activeTab.value === '主讲教师', show: !!(lecturers.value && lecturers.value.length > 0) },
277 { title: '课程大纲', active: activeTab.value === '课程大纲', show: !!(course.value.schedule && course.value.schedule.length > 0) }, 306 { title: '课程大纲', active: activeTab.value === '课程大纲', show: !!(course.value.schedule && course.value.schedule.length > 0) },
278 - { title: '课程亮点', active: activeTab.value === '课程亮点', show: !!course.value.highlights }, 307 + // { title: '课程亮点', active: activeTab.value === '课程亮点', show: !!course.value.highlights },
279 - { title: '学习目标', active: activeTab.value === '学习目标', show: !!course.value.learning_goal }, 308 + // { title: '学习目标', active: activeTab.value === '学习目标', show: !!course.value.learning_goal },
280 ].filter(item => item.show); 309 ].filter(item => item.show);
281 }); 310 });
282 311
...@@ -409,6 +438,17 @@ const displayedSchedule = computed(() => { ...@@ -409,6 +438,17 @@ const displayedSchedule = computed(() => {
409 const toggleSchedule = () => { 438 const toggleSchedule = () => {
410 isScheduleExpanded.value = !isScheduleExpanded.value 439 isScheduleExpanded.value = !isScheduleExpanded.value
411 } 440 }
441 +
442 +// 跳转课程大纲
443 +const goToStudyDetail = (item) => {
444 + console.warn(course.value);
445 + // 如果没有购买过, 禁止操作
446 + if (!course.value.is_buy) {
447 + return;
448 + }
449 + // 跳转详情
450 + router.push(`/studyDetail/${item.id}`)
451 +}
412 </script> 452 </script>
413 453
414 <style scoped> 454 <style scoped>
......