videoDetails.vue 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. <template>
  2. <view class="page_body">
  3. <!-- 头部 -->
  4. <nav-bar height="96" title="视频详情" :onlyTitle="true" :showHeaderborder="true" @GoBack="GoBack"></nav-bar>
  5. <view class="page_content">
  6. <view class="video_content">
  7. <!-- duration:指定视频时长,单位为秒(s) initial-time:指定视频初始播放位置,单位为秒(s) -->
  8. <video
  9. id="myVideo"
  10. :title="state?.videoInfo?.courseName || ''"
  11. :initial-time="state?.videoInfo?.initialTime"
  12. :duration="state?.videoInfo?.duration"
  13. :show-mute-btn="true"
  14. :poster="state?.videoInfo?.videoCoverUrl"
  15. :src="state?.videoInfo?.attachmentUrl"
  16. style="width: 100%; height: 100%; object-fit: contain;"
  17. @timeupdate="videoTimeupdate"
  18. >
  19. </video>
  20. </view>
  21. <view class="video_title">{{state?.videoInfo?.courseName}}</view>
  22. <view class="video_progress">
  23. <text class="video_progress_text" v-if="state?.videoInfo?.courseType===0">必修任务:{{state.videoInfo.courseStatus===0?`剩余${state.videoInfo.remainingTime}`:'已结束'}}</text>
  24. <text class="video_progress_text">观看进度:{{state?.videoInfo?.progress}}</text>
  25. </view>
  26. <view class="video_class_name">
  27. <text class="class_name_item">{{state?.videoInfo?.teachCourseTypeName}}</text>
  28. <text class="class_name_item">{{state?.videoInfo?.teachCourseTypeClassName}}</text>
  29. <text class="class_name_item">{{state?.videoInfo?.createdBy}}</text>
  30. </view>
  31. <view class="video_time">
  32. <text class="time_item">{{state?.videoInfo?.courseType===0 ?'开始':'上传'}}时间:{{state?.videoInfo?.courseStartDatetime}}</text>
  33. <text class="time_item" v-if="state?.videoInfo?.courseType===0">结束时间:{{state.videoInfo.courseEndDatetime}}</text>
  34. </view>
  35. <view class="video_desc">
  36. 课程简介:{{state?.videoInfo?.courseContent}}
  37. </view>
  38. </view>
  39. </view>
  40. <!-- 提示框 -->
  41. <popup-dialog ref="popupDialogRef" popType="custom" title="试题弹出" :showDialogClose="false" :showCloseBtn="false" :showFootBorder="true" dialogWidth="702" @DialogConfirm="DialogConfirm">
  42. <view class="dialog_questions_input">
  43. <view class="questions_title">{{state?.question?.codeName}}:{{state?.question?.questionsName}}</view>
  44. <uni-easyinput type="textarea" v-model="value" :style="state.easyinputStyle" placeholder="请输入你的回答" placeholderStyle="font-weight: 400;font-size: 28rpx;color: #999999;"></uni-easyinput>
  45. </view>
  46. </popup-dialog>
  47. </template>
  48. <script setup>
  49. import uniEasyinput from '@/uni_modules/uni-easyinput/components/uni-easyinput/uni-easyinput.vue';
  50. import navBar from '@/components/nav-bar.vue';
  51. import popupDialog from '@/components/popup-dialog.vue';
  52. import { selectCourseDetail } from '@/reqApi/teacherStudy.js';
  53. import {
  54. onLoad,
  55. onUnload
  56. } from '@dcloudio/uni-app';
  57. import {
  58. reactive,
  59. ref
  60. } from 'vue';
  61. const state = reactive({
  62. id: '', //视频id
  63. videoInfo:{},
  64. currentTime:'',//播放进度时分秒
  65. question:null,//试题弹出信息
  66. easyinputStyle:{
  67. color: '#303133',
  68. borderColor: '#E9E9E9'
  69. }
  70. });
  71. //提示框
  72. const popupDialogRef = ref(null);
  73. const videoCtx = ref(null);//播放器
  74. onLoad((option) => {
  75. state.id = option.id;
  76. videoCtx.value = uni.createVideoContext('myVideo')
  77. GetCourseDetail();
  78. })
  79. //视频详情
  80. const GetCourseDetail = () => {
  81. selectCourseDetail(state.id).then(res => {
  82. if(res.code == 200){
  83. state.videoInfo = res.data;
  84. state.videoInfo.initialTime = timeToSecond(state.videoInfo.courseLearnTime || '');
  85. state.videoInfo.duration = timeToSecond(state.videoInfo.videoTime || '')
  86. }
  87. })
  88. }
  89. //计算秒数
  90. const timeToSecond = (timeStr) => {
  91. const timeArr = timeStr.split(':');
  92. const [h, m, s] = timeArr.map(item => Number(item));
  93. return h * 3600 + m * 60 + s;
  94. }
  95. //播放进度变化时触发,event.detail = {currentTime, duration} 。触发频率 250ms 一次
  96. const videoTimeupdate = (event) => {
  97. const currentTime = event.detail.currentTime;
  98. state.currentTime = formatSecondsToHms(currentTime);
  99. const questionsList = state?.videoInfo?.questionsList || [];
  100. const question = questionsList.find(item=>item.showTime == state.currentTime);//视频显示的时间
  101. state.question = question;//试题弹框信息
  102. if(question){
  103. videoCtx.value?.pause();//暂停播放
  104. popupDialogRef.value.OpenDialog();
  105. }
  106. console.log(state.currentTime,323)
  107. }
  108. // 数字补零工具
  109. const addZero = (num) => {
  110. return num < 10 ? '0' + num : String(num);
  111. }
  112. //转换时分秒
  113. const formatSecondsToHms = (seconds) => {
  114. let total = Math.floor(seconds);
  115. const h = Math.floor(total / 3600);
  116. const m = Math.floor((total % 3600) / 60);
  117. const s = total % 60;
  118. const hh = addZero(h);
  119. const mm = addZero(m);
  120. const ss = addZero(s);
  121. return `${hh}:${mm}:${ss}`;
  122. }
  123. //页面销毁时触发
  124. onUnload(()=>{
  125. })
  126. //返回
  127. const GoBack = () => {
  128. uni.navigateBack({
  129. delta: 1
  130. });
  131. }
  132. </script>
  133. <style scoped lang="scss">
  134. .video_content {
  135. width: 100%;
  136. border-radius: 8rpx;
  137. margin-top: 24rpx;
  138. height: 398rpx;
  139. overflow: hidden;
  140. }
  141. .video_title{
  142. width: 100%;
  143. margin-top: 32rpx;
  144. font-weight: 500;
  145. font-size: 36rpx;
  146. color: #333333;
  147. display: -webkit-box;
  148. -webkit-line-clamp: 2;
  149. -webkit-box-orient: vertical;
  150. overflow: hidden;
  151. text-overflow: ellipsis;
  152. }
  153. .video_progress{
  154. display: flex;
  155. justify-content: space-between;
  156. width: 100%;
  157. margin-top: 16rpx;
  158. .video_progress_text{
  159. font-weight: 400;
  160. font-size: 24rpx;
  161. color: #2E64FA;
  162. }
  163. }
  164. .video_class_name{
  165. display: flex;
  166. width: 100%;
  167. margin-top: 16rpx;
  168. gap: 16rpx 24rpx;
  169. flex-wrap: wrap;
  170. .class_name_item{
  171. font-weight: 400;
  172. font-size: 24rpx;
  173. color: #666666;
  174. }
  175. }
  176. .video_time{
  177. display: flex;
  178. width: 100%;
  179. margin-top: 16rpx;
  180. gap: 16rpx;
  181. flex-wrap: wrap;
  182. .time_item{
  183. font-weight: 400;
  184. font-size: 24rpx;
  185. color: #666666;
  186. }
  187. }
  188. .video_desc{
  189. display: flex;
  190. width: 100%;
  191. margin-top: 32rpx;
  192. flex-wrap: wrap;
  193. word-break: break-all;
  194. font-weight: 400;
  195. font-size: 24rpx;
  196. color: #999999;
  197. line-height: 44rpx;
  198. }
  199. .dialog_questions_input{
  200. padding: 40rpx 30rpx;
  201. display: flex;
  202. width: 100%;
  203. flex-direction: column;
  204. .questions_title{
  205. display: flex;
  206. width: 100%;
  207. font-weight: 400;
  208. font-size: 32rpx;
  209. color: #303133;
  210. text-align: left;
  211. word-break: break-all;
  212. }
  213. :deep(.uni-easyinput){
  214. margin-top: 20rpx;
  215. font-size: 28rpx;
  216. .is-input-border{
  217. border-radius: 8rpx;
  218. border-color: #E9E9E9 !important;
  219. }
  220. .uni-easyinput__content-textarea{
  221. height: 440rpx;
  222. min-height: 440rpx;
  223. font-size: 28rpx;
  224. margin:20rpx 24rpx 20rpx 14rpx;
  225. }
  226. .input-padding{
  227. padding-left: 10rpx;
  228. }
  229. }
  230. }
  231. </style>