123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210 |
- <template>
- <view>
- <uni-collapse class="chapter-collapse" accordion>
- <uni-collapse-item v-for="(zhangItem, zhangIndex) in props.options.zhangList" :key="zhangIndex"
- :title="zhangItem.name" :open="isCurrentZhang(zhangIndex)">
- <uni-collapse class="chapter-collapse" accordion>
- <uni-collapse-item v-for="(jieItem, jieIndex) in zhangItem.jieList" :key="'jie-'+jieIndex"
- :title="jieItem.name" :open="isCurrentJie(zhangIndex,jieIndex)">
- <view class="section-header">{{ jieItem.name }}</view>
- <view v-for="(video, vidIndex) in jieItem.kejianList" :key="'vid-'+video.kjId"
- class="video-item" @click="handleVideoClick(zhangIndex, jieIndex, vidIndex,video)">
- <view class="video-info">
- <text class="video-name"
- :class="{ active: isCurrentVideo(zhangIndex, jieIndex, vidIndex) }">
- {{ video.name }}
- </text>
- <view class="progress-container">
- <progress :percent="calcProgress(video)" stroke-width="4"
- :activeColor="video.curProcess >= 100 ? '#4cd964' : '#007aff'" />
- </view>
- </view>
- </view>
- </uni-collapse-item>
- </uni-collapse>
- </uni-collapse-item>
- </uni-collapse>
- </view>
- </template>
- <script setup>
- import {
- getKechengList,
- kejianInfo
- } from "@/api/course.js";
- import {
- onLoad,
- onReady
- } from "@dcloudio/uni-app"
- import {
- ref,
- reactive,
- defineProps,
- defineEmits,
- onMounted,
- watchEffect,
- defineExpose
- } from 'vue';
- const props = defineProps({
- options: {
- type: Object,
- default: () => {}
- },
- currentVideo: {
- type: Object,
- default: () => {}
- },
- })
- let currentPos = ref({
- zhangIndex: -1,
- jieIndex: -1,
- vidIndex: -1
- });
- const Emits = defineEmits(['kejianInfo', 'playNext']);
- // 初始化时定位第一个未完成视频
- onMounted(() => {
- loadProgressFromCache();
- findFirstUnfinished();
- });
- const loadProgressFromCache = () => {
- props.options.zhangList.forEach(zhang => {
- zhang.jieList.forEach(jie => {
- jie.kejianList.forEach(video => {
- const cached = uni.getStorageSync(`video_${video.kjId}`);
- if (cached) {
- video.curProcess = (cached / uni.getStorageSync(
- `duration_${video.kjId}`)) * 100 || 0;
- video.maxProcess = Math.max(video.maxProcess, video.curProcess);
- }
- });
- });
- });
- };
- // 查找第一个未完成视频
- const findFirstUnfinished = () => {
- for (let z = 0; z < props.options.zhangList.length; z++) {
- const zhang = props.options.zhangList[z];
- for (let j = 0; j < zhang.jieList.length; j++) {
- const jie = zhang.jieList[j];
- for (let v = 0; v < jie.kejianList.length; v++) {
- const video = jie.kejianList[v];
- if (video.curProcess < 100) {
- updateCurrentPosition(z, j, v, video);
- return;
- }
- }
- }
- }
- };
- // 判断是否当前章
- const isCurrentZhang = (zhangIndex) => {
- return currentPos.value.zhangIndex === zhangIndex;
- };
- // 判断是否当前节(需要同时匹配章和节)
- const isCurrentJie = (zhangIndex, jieIndex) => {
- return currentPos.value.zhangIndex === zhangIndex &&
- currentPos.value.jieIndex === jieIndex;
- };
- // 判断是否当前视频
- const isCurrentVideo = (z, j, v) => {
- return currentPos.value.zhangIndex === z &&
- currentPos.value.jieIndex === j &&
- currentPos.value.vidIndex === v;
- };
- // 计算进度
- const calcProgress = (video) => {
- return Math.min(video.curProcess, 100);
- };
- // 点击视频处理
- const handleVideoClick = (z, j, v, video) => {
- console.log('video', video);
- // currentPos.value = {
- // zhangIndex: z,
- // jieIndex: j,
- // vidIndex: v
- // };
- updateCurrentPosition(z, j, v, video);
- kejianInfo({
- kjId: video.kjId
- }).then(res => {
- console.log('res', res);
- Emits('kejianInfo', {
- ...res.data,
- kjId: video.kjId
- })
- })
- // 这里添加实际播放逻辑
- };
- // 更新播放位置
- const updateCurrentPosition = (z, j, v, video) => {
- currentPos.value = {
- zhangIndex: z,
- jieIndex: j,
- vidIndex: v
- };
- video.curProcess = uni.getStorageSync(`video_${video.kjId}`) /
- uni.getStorageSync(`duration_${video.kjId}`) * 100 || 0;
- };
- // 播放下一个视频
- let playNextVideo = () => {
- let {
- zhangIndex,
- jieIndex,
- vidIndex
- } = currentPos.value;
- const chapters = props.options.zhangList;
- // 1. 同节的下一个视频
- const currentSection = chapters[zhangIndex].jieList[jieIndex];
- if (vidIndex < currentSection.kejianList.length - 1) {
- vidIndex++;
- return playVideoAt(zhangIndex, jieIndex, vidIndex);
- }
- // 2. 同章的下一节
- const currentChapter = chapters[zhangIndex];
- if (jieIndex < currentChapter.jieList.length - 1) {
- for (let j = jieIndex + 1; j < currentChapter.jieList.length; j++) {
- if (currentChapter.jieList[j].kejianList.length > 0) {
- return playVideoAt(zhangIndex, j, 0);
- }
- }
- }
- // 3. 后续章节
- for (let z = zhangIndex + 1; z < chapters.length; z++) {
- for (let j = 0; j < chapters[z].jieList.length; j++) {
- if (chapters[z].jieList[j].kejianList.length > 0) {
- return playVideoAt(z, j, 0);
- }
- }
- }
- uni.showToast({
- title: '已播放所有课程',
- icon: 'none'
- });
- };
- // 辅助方法
- const playVideoAt = (z, j, v) => {
- const video = props.options.zhangList[z].jieList[j].kejianList[v];
- updateCurrentPosition(z, j, v, video);
- Emits('kejianInfo', {
- ...video,
- kjId: video.kjId
- });
- };
- defineExpose({
- playNextVideo
- });
- </script>
- <style lang="scss">
- </style>
|