hookehuyr

feat(学习详情): 添加学习详情页面及相关功能

- 新增学习详情页面,包括视频播放、评论功能
- 在课程列表中添加点击跳转至学习详情的功能
- 更新路由配置以支持学习详情页面
- 在VideoPlayer组件中暴露play方法以支持外部调用
......@@ -31,8 +31,8 @@ declare module 'vue' {
VanCell: typeof import('vant/es')['Cell']
VanCellGroup: typeof import('vant/es')['CellGroup']
VanCheckbox: typeof import('vant/es')['Checkbox']
VanCol: typeof import('vant/es')['Col']
VanDatePicker: typeof import('vant/es')['DatePicker']
VanEmpty: typeof import('vant/es')['Empty']
VanField: typeof import('vant/es')['Field']
VanForm: typeof import('vant/es')['Form']
VanIcon: typeof import('vant/es')['Icon']
......@@ -43,7 +43,6 @@ declare module 'vue' {
VanPopup: typeof import('vant/es')['Popup']
VanProgress: typeof import('vant/es')['Progress']
VanRate: typeof import('vant/es')['Rate']
VanRow: typeof import('vant/es')['Row']
VanTab: typeof import('vant/es')['Tab']
VanTabs: typeof import('vant/es')['Tabs']
VanToast: typeof import('vant/es')['Toast']
......
......@@ -6,7 +6,7 @@
<div class="course-list">
<van-list v-model:loading="loading" :finished="finished" finished-text="没有更多了" @load="onLoad">
<div v-for="course in courses" :key="course.id"
class="course-item bg-white p-3 rounded-lg overflow-hidden flex">
class="course-item bg-white p-3 rounded-lg overflow-hidden flex cursor-pointer" @click="router.push(`/studyDetail/${course.id}`)">
<div class="relative w-[120px] h-full flex-shrink-0">
<van-image :src="course.thumbnail" width="120" height="100%" fit="cover" class="item-cover">
<div class="absolute bg-white/80 px-2 py-0.5 text-xs rounded"
......@@ -37,6 +37,9 @@
<script setup>
import { ref } from "vue";
import { useRouter } from "vue-router";
const router = useRouter();
// 接收课程列表数据
const props = defineProps({
......
......@@ -82,7 +82,7 @@ const handleMounted = (payload) => {
// 监听视频播放状态
player.value.on('play', () => {
// 播放时隐藏controls
player.value.controlBar.hide();
// player.value.controlBar.hide();
});
player.value.on('pause', () => {
......@@ -122,6 +122,11 @@ defineExpose({
player.value.pause();
}
},
play() {
if (player.value) {
player.value.play();
}
},
});
</script>
......
/*
* @Date: 2025-03-20 20:36:36
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-04-08 12:45:20
* @LastEditTime: 2025-04-08 13:39:00
* @FilePath: /mlaj/src/router/routes.js
* @Description: 路由地址映射配置
*/
......@@ -205,5 +205,12 @@ export const routes = [
title: '学习页面',
}
},
{
path: '/studyDetail/:id',
component: () => import('@/views/study/studyDetailPage.vue'),
meta: {
title: '学习详情页面',
}
},
...checkinRoutes,
]
......
<!--
* @Date: 2025-04-07
* @Description: 学习详情页面
-->
<template>
<div class="study-detail-page bg-gradient-to-b from-green-50/70 to-white/90 min-h-screen pb-20">
<div v-if="course" class="flex flex-col">
<!-- 视频播放区域 -->
<div class="w-full bg-black relative">
<!-- 视频封面和播放按钮 -->
<div v-if="!isPlaying" class="relative w-full" style="aspect-ratio: 16/9;">
<img :src="course.cover" :alt="course.title" class="w-full h-full object-cover" />
<div class="absolute inset-0 flex items-center justify-center cursor-pointer" @click="startPlay">
<div
class="w-24 h-24 rounded-full bg-black/50 flex items-center justify-center hover:bg-black/70 transition-colors">
<font-awesome-icon icon="circle-play" class="text-5xl text-white" style="font-size: 3rem;" />
</div>
</div>
</div>
<!-- 视频播放器 -->
<VideoPlayer v-show="isPlaying" ref="videoPlayerRef" :video-url="course.videoUrl" :autoplay="false"
@onPlay="handleVideoPlay" @onPause="handleVideoPause" />
</div>
<!-- 标签页区域 -->
<div class="px-4 py-3">
<van-tabs v-model:active="activeTab" sticky animated swipeable>
<!-- 介绍标签页 -->
<van-tab title="介绍" name="intro">
<div class="py-4">
<h1 class="text-xl font-bold mb-2">{{ course.title }}</h1>
<div class="text-gray-500 text-sm">
{{ course.studyCount || 0 }}次学习
</div>
</div>
</van-tab>
<!-- 评论标签页 -->
<van-tab title="评论" :title-style="{ 'min-width': '50%' }" name="comments">
<div class="py-4 space-y-4">
<div v-for="comment in comments" :key="comment.id"
class="bg-white rounded-lg p-4 shadow-sm">
<div class="flex items-center mb-2">
<img :src="comment.avatar" class="w-8 h-8 rounded-full mr-2" />
<div>
<div class="font-medium">{{ comment.username }}</div>
<div class="text-gray-500 text-xs">{{ comment.time }}</div>
</div>
</div>
<div class="text-gray-700">{{ comment.content }}</div>
</div>
</div>
</van-tab>
</van-tabs>
</div>
<!-- 底部操作栏 -->
<div class="fixed bottom-0 left-0 right-0 bg-white border-t px-4 py-2 flex items-center space-x-4">
<van-button icon="bars" class="flex-none" @click="showCatalog = true">课程目录</van-button>
<div class="flex-grow">
<van-field v-model="newComment" placeholder="说点什么吧~" class="bg-gray-100 rounded-full" />
</div>
<van-button type="primary" size="small" @click="submitComment">发送</van-button>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, nextTick } from 'vue';
import { useRoute } from 'vue-router';
import { useTitle } from '@vueuse/core';
import VideoPlayer from '@/components/ui/VideoPlayer.vue';
const route = useRoute();
const course = ref(null);
const activeTab = ref('intro');
const newComment = ref('');
const showCatalog = ref(false);
const isPlaying = ref(false);
const videoPlayerRef = ref(null);
// 开始播放视频
const startPlay = async () => {
isPlaying.value = true;
await nextTick();
if (videoPlayerRef.value) {
videoPlayerRef.value.play();
}
};
// 处理视频播放状态
const handleVideoPlay = () => {
isPlaying.value = true;
};
const handleVideoPause = () => {
// 保持视频播放器可见,只在初始状态显示封面
};
// 评论列表
const comments = ref([
{
id: 1,
username: '欢乐马',
avatar: 'https://fastly.jsdelivr.net/npm/@vant/assets/cat.jpeg',
content: '教育的顶级传承,是你用什么样的心,传承智慧',
time: '2024-12-04 18:51'
},
{
id: 2,
username: '欢乐马',
avatar: 'https://fastly.jsdelivr.net/npm/@vant/assets/cat.jpeg',
content: '不要用战术上的勤奋,掩盖战略上的懒惰',
time: '2024-12-04 08:01'
},
{
id: 3,
username: '欢乐马',
avatar: 'https://fastly.jsdelivr.net/npm/@vant/assets/cat.jpeg',
content: '和老师积极互动,整个课堂像为你而讲',
time: '2024-12-04 07:54'
}
]);
// 设置页面标题
useTitle('学习详情');
// 获取课程详情
onMounted(() => {
const courseId = route.params.id;
if (courseId) {
// TODO: 这里需要替换为实际的API调用
// 临时使用模拟数据
course.value = {
id: courseId,
title: '开学礼·止的智慧·心法老师·20241001(上)',
videoUrl: 'https://sf1-cdn-tos.huoshanstatic.com/obj/media-fe/xgplayer_doc_video/mp4/xgplayer-demo-360p.mp4',
progress: 35,
studyTime: 3600,
type: '视频课程',
studyCount: 1896,
cover: 'https://fastly.jsdelivr.net/npm/@vant/assets/cat.jpeg'
};
}
})
// 提交评论
const submitComment = () => {
if (!newComment.value.trim()) return;
comments.value.unshift({
id: Date.now(),
username: '当前用户',
avatar: 'https://fastly.jsdelivr.net/npm/@vant/assets/cat.jpeg',
content: newComment.value,
time: new Date().toLocaleString()
});
newComment.value = '';
};
</script>