courseDetail.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. import {courseTypes, courseTypeTexts, htmlTypes, logicStatus} from "../../model/enum";
  2. import Api from "../../model/api";
  3. import {getDataSet, getEventParam, showLoading, toast} from "../../utils/utils";
  4. import Route from "../../model/route";
  5. import {wxToPromise} from "../../utils/wx";
  6. Page({
  7. data: {
  8. id: '',
  9. type: '',
  10. item: {},
  11. obj: {},
  12. plan: {},
  13. fileList: [],
  14. videoList: [],
  15. videoIndex: 0,
  16. courseTypeTexts: courseTypeTexts,
  17. courseTypesEnum: courseTypes,
  18. videoItem: {},
  19. scanTime: 0,
  20. //已被记录的视频播放时长
  21. nodeTime: 0,
  22. //课程状态 logicStatus.NO: 未学完 logicStatus.YES: 已学完
  23. lessStatus: logicStatus.NO,
  24. show: false,
  25. //视频实时播放时长(拖拽进度条,currentTime为拖拽位置的时长)
  26. currentTime: 0,
  27. scanOkTime: 0,
  28. videoContext: null,
  29. tempFilePath: '',
  30. //10s 本地缓存一次视频时长
  31. localRecordInterval: 10,
  32. //是否是从往期培训来的
  33. isPre: logicStatus.NO,
  34. },
  35. async onLoad(options) {
  36. let {id, detail, plan, type, isPre} = options;
  37. let obj = JSON.parse(detail);
  38. plan = JSON.parse(plan);
  39. wx.setNavigationBarTitle({title: courseTypeTexts[type]});
  40. const videoContext = wx.createVideoContext('myVideo3');
  41. this.setData({
  42. id, plan,
  43. obj, type,
  44. videoContext,
  45. isPre: Number(isPre)
  46. }, async () => {
  47. showLoading();
  48. await this.getData();
  49. wx.hideLoading()
  50. this.data.videoContext.seek(this.data.nodeTime)
  51. })
  52. },
  53. async getData() {
  54. const res = await Api.getCourseDetail(this.data.id, this.data.obj.eduStuId);
  55. if (this.data.obj.isOnline) {
  56. res.data.isQuestion = res.data.suitangUpper;
  57. } else {
  58. res.data.isQuestion = res.data.suitangLower;
  59. }
  60. let fileList = [];
  61. let videoItem = {};
  62. if (res.data.enclosureUrl) {
  63. if (this.data.type == courseTypes.RECORD) {
  64. videoItem = JSON.parse(res.data.enclosureUrl)[0];
  65. if (videoItem.verifyInterval) {
  66. let scanTime = videoItem.verifyInterval;
  67. this.setData({scanTime})
  68. }
  69. if (this.data.isPre == logicStatus.YES) {
  70. // 如果是往期培训来的
  71. this.setData({nodeTime: 0, lessStatus: logicStatus.NO, videoItem});
  72. return;
  73. }
  74. const less = await Api.getRecordedLesson({
  75. stuId: this.data.obj.eduStuId,
  76. scheduleId: this.data.id
  77. })
  78. if (less.data) {
  79. let lessStatus = less.data.status;
  80. let hasFulfilled = lessStatus == logicStatus.YES;
  81. let localRecordTime = this.getLessonRecordByLocal();
  82. // localRecordTime = hasFulfilled && localRecordTime >= videoItem.duration ? 0 : localRecordTime;
  83. let nodeTime = hasFulfilled ? 0 : Math.max(localRecordTime, less.data.nodeTime || 0);
  84. this.setData({nodeTime, lessStatus, videoItem});
  85. if (!hasFulfilled && localRecordTime > less.data.nodeTime) {
  86. //如果未完成课程学习 并且 本地记录的时长 > 数据库记录的时长(异常退出) 更新数据库记录
  87. this.changeProgress();
  88. }
  89. hasFulfilled && this.removeLessonRecordByLocal();
  90. // let lessStatus = less.data.status;
  91. // let nodeTime = lessStatus == logicStatus.YES ? 0 : less.data.nodeTime || 0;
  92. // this.setData({nodeTime, lessStatus, videoItem})
  93. } else {
  94. this.setData({nodeTime: 0, lessStatus: logicStatus.NO, videoItem})
  95. }
  96. } else {
  97. fileList = JSON.parse(res.data.enclosureUrl);
  98. }
  99. }
  100. let videoList = [];
  101. if (this.data.type == courseTypes.PLAYBACK && res.data.videoPlaybackUrl) {
  102. videoList = res.data.videoPlaybackUrl.split(",");
  103. }
  104. this.compare(res.data, this.data.plan);
  105. this.setData({
  106. item: res.data, fileList, videoList
  107. })
  108. },
  109. compare(newData, oldData) {
  110. if (!oldData) {
  111. return;
  112. }
  113. if (newData.courseProcess != oldData.courseProcess ||
  114. newData.courseStatus != oldData.courseStatus ||
  115. newData.liveStatus != oldData.liveStatus
  116. ) {
  117. const eventChannel = this.getOpenerEventChannel()
  118. eventChannel.emit('refresh');
  119. }
  120. },
  121. async downloadFile(e) {
  122. let url = getDataSet(e, "url");
  123. let end = url.substr(url.lastIndexOf(".") + 1);
  124. showLoading('下载中...')
  125. let filePath = "";
  126. if (this.data.tempFilePath) {
  127. filePath = this.data.tempFilePath;
  128. } else {
  129. const res = await wxToPromise("downloadFile", {url});
  130. if (res.statusCode === 200) {
  131. filePath = res.tempFilePath
  132. this.setData({tempFilePath: filePath})
  133. } else {
  134. toast(`下载文档失败,请稍后重试${res.statusCode}:${res.errMsg}`)
  135. }
  136. }
  137. try {
  138. await wx.openDocument({
  139. filePath
  140. })
  141. } catch (e) {
  142. console.log(e)
  143. toast(`打开文档失败,请稍后重试${e.msg}`)
  144. }
  145. wx.hideLoading();
  146. },
  147. toTeacher(e) {
  148. let id = getDataSet(e, "id");
  149. Route.toTeacher(id);
  150. },
  151. clickImg(e) {
  152. let id = this.data.item.id;
  153. let eId = this.data.obj.eduStuId;
  154. Route.toNews(htmlTypes.SEAT, id, "座位图", eId);
  155. },
  156. async refresh(e) {
  157. await this.getData();
  158. const eventChannel = this.getOpenerEventChannel()
  159. eventChannel.emit('refresh');
  160. },
  161. changeVideo(e) {
  162. let index = getDataSet(e, "index");
  163. this.setData({
  164. videoIndex: index,
  165. })
  166. },
  167. // 以下是录播课的逻辑算法 待分离
  168. async onUnload() {
  169. await this.changeProgress();
  170. },
  171. async onHide() {
  172. await this.changeProgress();
  173. },
  174. async changeProgress() {
  175. if (this.data.isPre == logicStatus.YES) {
  176. return;
  177. }
  178. if (this.data.type == courseTypes.RECORD) {
  179. const res = await Api.changeVideoProgress({
  180. stuId: this.data.obj.eduStuId,
  181. scheduleId: this.data.id,
  182. nodeTime: this.data.nodeTime
  183. });
  184. // 学习时长记录到本地缓存 start
  185. // 如果未完成视频学习 将学习记录缓存到本地
  186. this.data.lessStatus == logicStatus.NO && this.recordLessonByLocal(this.data.currentTime);
  187. // 学习时长记录到本地缓存 end
  188. if (res.data == logicStatus.YES) {
  189. await this.getData(false);
  190. const eventChannel = this.getOpenerEventChannel()
  191. eventChannel.emit('refresh');
  192. }
  193. }
  194. },
  195. async timeUpdate(e) {
  196. if (this.data.isPre == logicStatus.YES) {
  197. //往期培训不记录播放时长
  198. return;
  199. }
  200. let currentTime = parseInt(getEventParam(e, "currentTime"));
  201. let lessStatus = this.data.lessStatus;
  202. let unFulfilledFlag = lessStatus == logicStatus.NO;
  203. console.log("timeUpdate", currentTime, this.data.scanTime)
  204. if (this.data.scanTime && unFulfilledFlag) {//需要处理验证人脸情况
  205. let flag = currentTime % this.data.scanTime;
  206. if (flag == 0 && currentTime != this.data.scanOkTime &&
  207. currentTime >= this.data.nodeTime && currentTime != this.data.videoItem.duration) {
  208. console.log("暂停,弹出人脸识别")
  209. this.data.videoContext.exitFullScreen();
  210. this.data.videoContext.pause();
  211. this.setData({
  212. show: true,
  213. currentTime
  214. });
  215. }
  216. }
  217. if (currentTime - this.data.nodeTime <= 1) {
  218. // 播放时长和被记录的时长的时间差<=1秒
  219. if (currentTime - this.data.nodeTime >= 0) {
  220. this.data.nodeTime = currentTime;
  221. if (this.data.nodeTime == this.data.videoItem.duration) {
  222. if (unFulfilledFlag) {
  223. this.setData({
  224. lessStatus: logicStatus.YES
  225. });
  226. await wx.showModal({
  227. title: "恭喜你,视频已学完!",
  228. showCancel: false
  229. });
  230. await this.changeProgress();
  231. }
  232. }
  233. }
  234. // 学习时长记录到本地缓存 start
  235. if (unFulfilledFlag && currentTime % this.data.localRecordInterval === 0) {
  236. //未学完课程时 才将学习时长记录到本地
  237. this.recordLessonByLocal(currentTime);
  238. }
  239. this.setData({
  240. currentTime
  241. });
  242. // 学习时长记录到本地缓存 end
  243. } else {
  244. // 不可以快进的情况(课程未学完)
  245. if (unFulfilledFlag) {
  246. wx.showToast({
  247. title: '视频未学完,不可以快进!',
  248. icon: 'none'
  249. });
  250. this.data.videoContext.seek(this.data.nodeTime);
  251. }
  252. }
  253. },
  254. recordLessonByLocal(time) {
  255. console.log('缓存视频学习时长', time);
  256. wx.setStorageSync(`stu${this.data.obj.eduStuId}-course${this.data.id}`, time);
  257. },
  258. getLessonRecordByLocal() {
  259. return wx.getStorageSync(`stu${this.data.obj.eduStuId}-course${this.data.id}`) || 0;
  260. },
  261. removeLessonRecordByLocal() {
  262. //如果课程已学完,移除本地学习时长的记录
  263. wx.removeStorageSync(`stu${this.data.obj.eduStuId}-course${this.data.id}`);
  264. },
  265. scanOk(e) {
  266. this.data.scanOkTime = getEventParam(e, "currentTime");
  267. this.setData({
  268. show: false,
  269. });
  270. setTimeout(() => {
  271. this.data.videoContext.play();
  272. }, 500)
  273. },
  274. });