| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340 |
- <template>
- <div class="CourseDetail container-height">
- <Breadcrumb />
- <div>
- <div class="flex_1 bg_color_fff padding16 border_radius_16 box_shadow_card fit_content">
- <div class="gap10">
- <el-button type="primary">{{info.skillTagName}}</el-button>
- <div class="bold font_size30">{{info.courseCategoryName}}(第1期)</div>
- </div>
- <div class="gap5 mt10">
- <img :src="riliIcon" alt="" style="width: 16px; height: 16px;">
- <span class="font_size14">{{info.createTime}}</span>
- </div>
- </div>
- <div class="flex-between mt20">
- <div class="flex_1">
- <div class="video-player mr20">
- <VideoPlayer
- ref="videoPlayer"
- :src="currentVideoUrl"
- :poster="currentCourseCover"
- @play="onPlayerPlay"
- @pause="onPlayerPause"
- @ended="onPlayerEnded"
- @timeupdate="onPlayerTimeupdate"
- @loadedmetadata="onPlayerLoadedmetadata"
- @error="onPlayerError"
- @ready="onPlayerReady"
- />
- <!-- <img :src="currentCourse.cover" :alt="currentCourse.title">
- <div class="play-button">▶</div> -->
- </div>
- <div class="bg_color_fff padding16 border_radius_16 box_shadow_card mt20">
- <div class="flex-center-between border_bottom">
- <div class="gap10">
- <div class="gap5 cursor-pointer" @click="collectFn">
- <img :src="weishoucangIcon" alt="" style="width: 24px; height: 24px;">
- <span class="bold font_size14">{{$t('common.collect')}}</span>
- </div>
- <div class="gap5 cursor-pointer" @click="collectFn">
- <img :src="shoucangIcon" alt="" style="width: 24px; height: 24px;">
- <span class="bold font_size14">{{$t('common.unCollect')}}</span>
- </div>
- <div class="gap5 cursor-pointer" @click="copyCurrentUrl">
- <img :src="fenxiangIcon" alt="" style="width: 24px; height: 24px;">
- <span class="bold font_size14">{{$t('common.share')}}</span>
- </div>
- </div>
- <div class="addBtn flex-center gradient border_radius_10">
- <div class="gap10" @click="toAddNote">
- <img :src="addIcon" alt="" style="width:30px;height:30px">
- <span class="font_size18">120{{$t('common.notes')}}</span>
- </div>
- </div>
- </div>
- <div class="gray font_size16 mt10 line2" :title="info.courseIntro">
- {{info.courseIntro}}
- </div>
- </div>
- <div class="bg_color_fff padding16 border_radius_16 box_shadow_card mt20">
- <Pinglun :info="info"/>
- </div>
- </div>
- <div class="detail_right">
- <div class="bg_color_fff padding16 border_radius_16 box_shadow_card" v-show="!isShowNote">
- <CourseDirectory :info="info" />
- </div>
- <div class=" bg_color_fff padding16 border_radius_16 box_shadow_card mt10" v-show="!isShowNote">
- <OtherCourse />
- </div>
- <div class="isShowNote bg_color_fff padding16 border_radius_16 box_shadow_card mt10" v-show="isShowNote">
- <div class="CloseBold" @click="isShowNote=false">
- <el-icon size="18"><CloseBold /></el-icon>
- </div>
- <Xuxibiji :info="info" />
- </div>
- </div>
- </div>
- </div>
- </div>
- </template>
- <script setup>
- import riliIcon from '@/assets/imgs/rili.png'
- import weishoucangIcon from '@/assets/imgs/weishoucang.png'
- import shoucangIcon from '@/assets/imgs/shoucang.png'
- import fenxiangIcon from '@/assets/imgs/fenxiang.png'
- import addIcon from '@/assets/imgs/add.png'
- import OtherCourse from './components/OtherCourse.vue'
- import CourseDirectory from './components/CourseDirectory.vue'
- import VideoPlayer from '@/components/VideoPlayer.vue'
- import Pinglun from './components/pinglun.vue'
- import Xuxibiji from './components/Xuxibiji.vue'
- import DGTMessage from '@/utils/message'
- import { copyText } from '@/utils/util'
- // 引入api
- import { getCourseDetail,collect } from '@/api/course.js'
- import { useRouter, useRoute } from 'vue-router'
- const router = useRouter()
- const route = useRoute()
- console.log(router,route)
- import { ref, computed, reactive, onMounted } from 'vue'
- import { useAppStore } from '@/pinia/appStore'
- const appStore = useAppStore()
- import { useI18n } from 'vue-i18n'
- const { t } = useI18n()
- //获取参数
- const query = route.query;
- const courseId = ref(route.params.courseId || '');
- const info = ref({})
- // 视频相关
- // const currentVideoUrl = ref('http://baomiai.oss-cn-shanghai.aliyuncs.com/video/2025/12/30/123_20251226133117A047_20251230095248A001.mp4?response-content-disposition=inline&x-oss-date=20251230T015720Z&x-oss-expires=7199&x-oss-signature-version=OSS4-HMAC-SHA256&x-oss-credential=LTAI5tG5dEtkqwDGcU6AtaYE%2F20251230%2Fcn-shanghai%2Foss%2Faliyun_v4_request&x-oss-signature=f93c20421dcfdc6822cd03d42ae66f8a28acd2c4d25c79ce16e2312537eabaa5')
- // const currentVideoUrl = ref('http://jcxxpt.oss-cn-beijing.aliyuncs.com/common/2025/12/19/actmOvmq0xOBc8448561c73a066a523821c6f7ae4868_20251219094240A008.mp4')
- const currentVideoUrl = ref('')
- // const currentCourseCover = ref('http://jcxxpt.oss-cn-beijing.aliyuncs.com/common/2025/12/15/3120938e-8205-416e-aedc-8b25ea23cc94_20251125142306A001_20251215110507A001.png')
- const currentCourseCover = ref('')
- const currentPlayingVideoId = ref(null)
- const currentVideoDuration = ref(0)
- const currentPlayTime = ref(0)
- const videoPlayer = ref(null)
- onMounted(() => {
- getDetail();
- });
- const getDetail = async () => {
- getCourseDetail({id: courseId.value}).then(res => {
- if(res.code === 200){
- console.log(res)
- info.value = res.data || {};
- currentCourseCover.value = info.value.coverImageUrl || ''
- }
- })
- };
- // 打开添加对话框
- const isShowNote = ref(false)
- const toAddNote = () => {
- isShowNote.value = true
- };
- // 播放指定视频
- const playVideo = (video) => {
- if (!video) return
-
- // 更新当前播放的视频ID
- currentPlayingVideoId.value = video.id
-
- // 这里可以根据video.id从API获取实际的视频地址
- // 暂时使用模拟地址
- // const videoUrl = `https://example.com/videos/${video.id}.mp4`
-
- // 更新视频源
- // currentVideoUrl.value = videoUrl
-
- // 播放视频
- if (videoPlayer.value) {
- videoPlayer.value.play()
- }
- }
- // 播放器事件处理
- const onPlayerPlay = () => {
- console.log('视频开始播放')
- }
- const onPlayerPause = () => {
- console.log('视频暂停')
- }
- const onPlayerEnded = () => {
- console.log('视频播放结束')
-
- // 自动播放下一个视频
- playNextVideo()
- }
- const onPlayerTimeupdate = (time) => {
- currentPlayTime.value = time
- // 这里可以保存播放进度
- console.log('当前播放时间:', time)
- }
- const onPlayerLoadedmetadata = (duration) => {
- currentVideoDuration.value = duration
- console.log('视频时长:', duration)
- }
- const onPlayerError = (error) => {
- console.error('视频播放错误:', error)
- // DGTMessage.error('视频播放失败,请稍后再试')
- }
- const onPlayerReady = (player) => {
- console.log('播放器就绪', player)
- // 可以在这里进行高级操作
- }
- // 播放下一个视频
- const playNextVideo = () => {
- if (!currentCourseChapters.value || currentPlayingVideoId.value === null) return
-
- // 查找当前播放视频的位置
- let currentIndex = -1
- let chapterIndex = -1
-
- for (let i = 0; i < currentCourseChapters.value.length; i++) {
- const chapter = currentCourseChapters.value[i]
- const index = chapter.videos.findIndex(v => v.id === currentPlayingVideoId.value)
-
- if (index !== -1) {
- chapterIndex = i
- currentIndex = index
- break
- }
- }
-
- // 如果找到当前视频
- if (chapterIndex !== -1 && currentIndex !== -1) {
- const currentChapter = currentCourseChapters.value[chapterIndex]
-
- // 如果当前章节还有下一个视频
- if (currentIndex < currentChapter.videos.length - 1) {
- playVideo(currentChapter.videos[currentIndex + 1])
- }
- // 如果是当前章节最后一个视频,且有下一个章节
- else if (chapterIndex < currentCourseChapters.value.length - 1) {
- playVideo(currentCourseChapters.value[chapterIndex + 1].videos[0])
- }
- }
- };
- const collectFn = () => {
- collect({objectId: courseId.value}).then(res => {
- if(res.code === 200){
- info.value.isCollect = !info.value.isCollect
- let msg = info.value.isCollect ? t('common.unCollect') : t('common.collect')
- DGTMessage.success(msg+t('common.success'))
- }
- })
- };
- //负责当前页面地址
- // 复制当前页面地址
- const copyCurrentUrl = () => {
- copyText(window.location.href, t)
- }
- </script>
- <style scoped lang="scss">
- .CourseDetail{
- .addBtn{
- cursor: pointer;
- padding: 10px 20px;
- color: #fff;
- }
- .course-video {
- display: flex;
- gap: 20px;
- margin-bottom: 40px;
- // height: 500px; /* 设置容器高度,可根据需要调整 */
-
- @media (max-width: 768px) {
- flex-direction: column;
- }
-
- .video-player {
- flex: 2;
- position: relative;
- // height: 500px;
-
- img {
- width: 100%;
- height: auto;
- border-radius: 8px;
- }
-
- .play-button {
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- width: 80px;
- height: 80px;
- background: rgba(0,0,0,0.5);
- color: white;
- border-radius: 50%;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 30px;
- cursor: pointer;
- }
- }
-
- .video-info {
- flex: 1;
-
- h2 {
- font-size: 1.8rem;
- margin-bottom: 15px;
- }
-
- .course-meta {
- margin-bottom: 15px;
- color: #666;
-
- span {
- display: block;
- margin-bottom: 5px;
- }
- }
-
- .course-desc {
- margin-bottom: 20px;
- line-height: 1.6;
- }
- }
- }
- .isShowNote{
- position: relative;
- .CloseBold{
- position: absolute;
- top: 16px;
- right: 16px;
- cursor: pointer;
- }
- }
-
- }
- </style>
|