kechengInfo.vue 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. <template>
  2. <view class="course-container">
  3. <!-- 视频播放器 -->
  4. <video v-if="currentVideo.url" :src="currentVideo.url" :id="videoId" controls @ended="handleVideoEnd"
  5. @timeupdate="handleTimeUpdate" @loadedmetadata="handleVideoLoaded">
  6. </video>
  7. <!-- 课程信息 -->
  8. <view class="course-header">
  9. <text class="course-title">测试课程1</text>
  10. <view class="course-meta">
  11. <text>讲师:张老师</text>
  12. <text>时长:3分42秒</text>
  13. <text>1人学习</text>
  14. </view>
  15. </view>
  16. <!-- 选项卡 -->
  17. <view class="course-tabs">
  18. <text :class="['tab-item', activeTab === '目录' ? 'active' : '']" @click="activeTab = '目录'">目录</text>
  19. <text :class="['tab-item', activeTab === '介绍' ? 'active' : '']" @click="activeTab = '介绍'">介绍</text>
  20. <text :class="['tab-item', activeTab === '评论' ? 'active' : '']" @click="activeTab = '评论'">评论</text>
  21. <text :class="['tab-item', activeTab === '考试' ? 'active' : '']" @click="activeTab = '考试'">考试</text>
  22. </view>
  23. <!-- 目录内容 -->
  24. <view v-if="activeTab == '目录'" class="course-content">
  25. <catalogue ref="catalogueRef" @kejianInfo='getKejianInfo' :options="kejianUserVo"
  26. :current-video="currentVideo"></catalogue>
  27. </view>
  28. <view v-if="activeTab == '介绍'" class="course-content">
  29. <view v-if="!kechengData.intro" class="s-kehceng-default">
  30. <view>
  31. <img :src="jieshaoImg">
  32. <text>还没有介绍</text>
  33. </view>
  34. </view>
  35. <view v-else v-html="kechengData.intro" class="kecheng-tab-jieshao"></view>
  36. </view>
  37. <view v-if="activeTab == '评论'" class="course-content">
  38. <view v-if="!kechengData.intro" class="s-kehceng-default">
  39. <view>
  40. <img :src="jieshaoImg">
  41. <text>还没有介绍</text>
  42. </view>
  43. </view>
  44. <view v-else v-html="kechengData.intro" class="kecheng-tab-jieshao"></view>
  45. </view>
  46. </view>
  47. </template>
  48. <script setup>
  49. import {
  50. ref,
  51. onMounted
  52. } from 'vue';
  53. import {
  54. onLoad,
  55. onReady,
  56. } from "@dcloudio/uni-app"
  57. import {
  58. kechengStart
  59. } from "@/api/course.js";
  60. import catalogue from './common/catalogue.vue';
  61. // 选项卡状态
  62. const activeTab = ref('');
  63. let kcId = ref('');
  64. let kejianUserVo = ref(null);
  65. let jieshaoImg = ref('/static/images/kecheng/kecheng-introduce.svg');
  66. let kechengData = ref(null);
  67. // 当前播放信息
  68. const currentVideo = ref({
  69. url: '',
  70. kjId: ''
  71. });
  72. const currentChapter = ref(0);
  73. const currentSection = ref(0);
  74. const catalogueRef = ref(null);
  75. const videoId = ref('courseVideo');
  76. let videoContext = null;
  77. onLoad((options) => {
  78. console.log('options', options);
  79. kcId.value = options.kcId
  80. });
  81. onMounted(() => {
  82. getKechengData()
  83. });
  84. function getKechengData() {
  85. kechengStart({
  86. kcId: kcId.value
  87. }).then(res => {
  88. console.log('res', res);
  89. activeTab.value = '目录'
  90. kejianUserVo.value = res.data.kejianUserVo
  91. kechengData.value = res.data
  92. })
  93. }
  94. function getKejianInfo(data) {
  95. console.log('data', data.url);
  96. currentVideo.value = {
  97. url: data.url,
  98. kjId: data.kjId
  99. };
  100. setTimeout(() => {
  101. if (!videoContext) {
  102. videoContext = uni.createVideoContext(videoId.value, this);
  103. }
  104. const cachedTime = uni.getStorageSync(`video_${data.kjId}`) || 0;
  105. videoContext.seek(cachedTime);
  106. }, 300);
  107. }
  108. const handleTimeUpdate = (e) => {
  109. const currentTime = e.detail.currentTime;
  110. uni.setStorageSync(`video_${currentVideo.value.kjId}`, currentTime);
  111. };
  112. const handleVideoEnd = () => {
  113. catalogueRef.value.playNextVideo();
  114. };
  115. const handleVideoLoaded = (e) => {
  116. uni.setStorageSync(`duration_${currentVideo.value.kjId}`, e.detail.duration);
  117. };
  118. </script>
  119. <style scoped>
  120. .course-container {
  121. display: flex;
  122. flex-direction: column;
  123. height: 100vh;
  124. }
  125. .course-header {
  126. padding: 20rpx;
  127. background: #f5f5f5;
  128. }
  129. .course-title {
  130. font-size: 36rpx;
  131. font-weight: bold;
  132. }
  133. .course-meta {
  134. margin-top: 10rpx;
  135. font-size: 24rpx;
  136. color: #666;
  137. }
  138. .course-tabs {
  139. display: flex;
  140. justify-content: space-around;
  141. padding: 20rpx;
  142. background: #fff;
  143. border-bottom: 1rpx solid #ddd;
  144. }
  145. .tab-item {
  146. font-size: 28rpx;
  147. color: #666;
  148. }
  149. .tab-item.active {
  150. color: #09BB07;
  151. font-weight: bold;
  152. }
  153. .course-content {
  154. flex: 1;
  155. overflow-y: auto;
  156. padding: 20rpx;
  157. }
  158. .chapter-list {
  159. background: #fff;
  160. }
  161. .chapter-item {
  162. margin-bottom: 20rpx;
  163. }
  164. .chapter-title {
  165. padding: 20rpx;
  166. background: #f5f5f5;
  167. display: flex;
  168. justify-content: space-between;
  169. border-bottom: 1rpx solid #ddd;
  170. }
  171. .section-list {
  172. background: #fff;
  173. }
  174. .section-item {
  175. padding: 20rpx;
  176. display: flex;
  177. justify-content: space-between;
  178. border-bottom: 1rpx solid #eee;
  179. }
  180. .section-info {
  181. flex: 1;
  182. }
  183. .section-title {
  184. display: block;
  185. font-size: 28rpx;
  186. color: #333;
  187. }
  188. .play-status {
  189. width: 180rpx;
  190. text-align: right;
  191. }
  192. .playing {
  193. color: #09BB07;
  194. font-size: 24rpx;
  195. }
  196. .progress {
  197. color: #666;
  198. font-size: 24rpx;
  199. }
  200. .unplayed {
  201. color: #999;
  202. font-size: 24rpx;
  203. }
  204. video {
  205. width: 100%;
  206. height: 300rpx;
  207. background: #000;
  208. }
  209. </style>