feat(课程详情页): 重构课程详情页布局和交互逻辑
- 将课程介绍移至标签页并设为默认标签 - 新增主讲教师标签页展示讲师信息 - 优化课程大纲交互,增加点击跳转功能 - 调整底部操作按钮逻辑,区分购买和查看课程状态 - 移除冗余代码,简化页面结构
Showing
1 changed file
with
64 additions
and
24 deletions
| 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> | ... | ... |
-
Please register or login to post a comment