detail.vue 53 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840
  1. <template>
  2. <view class="polyv_detail">
  3. <uni-nav-bar left-icon="back" :statusBar="true" fixed="true" :title="detail.courseName || '课程详情'"
  4. @clickLeft="clickLeft"></uni-nav-bar>
  5. <view id="top">
  6. <view class="video_box" v-if="!playVid">
  7. <image :src="$method.splitImgHost(goodsData.coverUrl)" mode="widthFix" style="width: 100%; height: 421rpx">
  8. </image>
  9. </view>
  10. <view v-else>
  11. <my-player ref="player" :playVid="playVid" :autoplay="autoplay" :allowSeek="isAllowSeek"
  12. :playbackRate="playbackRate" :videoCurrentTime="videoCurrentTime || 0" @playing="playing" @pause="pause"
  13. @ended="ended" @loadedmetadata="loadedmetadata" @timeupdate="timeupdate" @playerError="playerError"></my-player>
  14. </view>
  15. <view class="course_name">
  16. <view class="course_titles">
  17. <view class="video_t1" :class="{ one: !goodsData.buyNote }">{{
  18. detail.courseName
  19. }}</view>
  20. <view class="notice_wrap" v-if="goodsData.buyNote">
  21. <view class="video_t1_t" @click="studyNotice"> 学员须知 </view>
  22. </view>
  23. <view class="toggle_course" v-if="goodsTeacher.length > 1" @click="changeCourses()">
  24. <image class="img" src="/pages3/static/imgs/toggle.png" mode="widthFix"></image>
  25. <view class="toggle_name">切换课程</view>
  26. <view class="numbers">共{{ goodsTeacher.length }}门</view>
  27. </view>
  28. </view>
  29. </view>
  30. <u-line color="#D6D6DB" />
  31. <view>
  32. <view>
  33. <u-tabs :item-width="itemWidth()" :list="list" font-size="32" bar-width="24" :current="current" @change="change"
  34. active-color="#007AFF"></u-tabs>
  35. </view>
  36. </view>
  37. <u-line color="#D6D6DB" />
  38. </view>
  39. <view class="box" :class="{ first_ml: current == 0 }">
  40. <scroll-view class="box_in" scroll-y="true">
  41. <!--目录 -->
  42. <view v-show="current == 0">
  43. <view class="top__header" v-if="livingItem" @click="goLive(livingItem)">
  44. <image class="img" src="/pages3/static/imgs/live.png" mode="widthFix"></image>
  45. <view class="note">正在直播中</view>
  46. <view class="title">{{ livingItem.sectionName }}</view>
  47. </view>
  48. <course-tree v-if="sectionItem.id || sectionItem.sectionId" :sectionMaxNum="goodsData.sectionMaxNum"
  49. :isRebuild="false" :isBuy="true" @isHaverebuild="isHaverebuild = true"></course-tree>
  50. </view>
  51. <!--讲义 -->
  52. <view v-if="current == 1">
  53. <handouts-box :handoutsId="goodsData.handoutsId"></handouts-box>
  54. </view>
  55. <!--笔记 -->
  56. <view v-if="current == 2">
  57. <note-Box @jumpNote="jumpNote"></note-Box>
  58. </view>
  59. <!--答疑 -->
  60. <view v-if="current == 3">
  61. <answer-box :userId="userInfo ? userInfo.userId : 0"></answer-box>
  62. </view>
  63. <!--重修目录 -->
  64. <view v-if="current == 4">
  65. <course-tree v-if="sectionItem.id || sectionItem.sectionId" :sectionMaxNum="goodsData.sectionMaxNum"
  66. :isRebuild="true" :isBuy="true"></course-tree>
  67. </view>
  68. </scroll-view>
  69. </view>
  70. <u-popup v-model="noticeShow" class="notice_modal" mode="center" border-radius="28" width="650rpx" height="622rpx"
  71. :mask-close-able="false">
  72. <view class="content">
  73. <view class="title">学员须知</view>
  74. <scroll-view scroll-y="true">
  75. <view class="text" v-html="goodsData.buyNote && goodsData.buyNote.replace(/\n|\r\n/g, '<br>')
  76. ">
  77. </view>
  78. </scroll-view>
  79. <view class="had_read" :class="{ gray: CountTo > 0 }" @click="noticeConfirm()">
  80. <text v-if="CountTo > 0">请阅读学员须知,{{ " " + CountTo + "s" }}后可关闭</text>
  81. <text v-else>我已阅读学员须知</text>
  82. </view>
  83. </view>
  84. </u-popup>
  85. <!-- 倒计时提交 -->
  86. <u-popup v-model="noticeShow1" class="notice_modal" mode="center" border-radius="28" width="650rpx" height="262rpx"
  87. :mask-close-able="false">
  88. <view class="content">
  89. <view class="title">提示</view>
  90. <view class="had_read">
  91. <text v-if="CountTo1 >= 0">视频学习时长不达标,请等待</text>
  92. <text v-if="CountTo1 >= 0">{{ " " + CountTo1 + "s" }}</text>
  93. </view>
  94. </view>
  95. </u-popup>
  96. <u-modal v-model="showMark" title="提示" @confirm="markConfirm" @cancel="toBack" confirm-text="复制学习网址"
  97. :show-cancel-button="true" cancel-text="关闭">
  98. <view class="slot-content">
  99. <view>您的学习账号已经开通,请按照步骤操作,进行学习。</view>
  100. <view>1.复制学习地址:{{ markContent }}</view>
  101. <view>2.在【浏览器中】打开复制的学习网址</view>
  102. <view>3.打开学习网址后,选择【个人用户】进行登录</view>
  103. <view>(1)账号:您个人的身份证号码</view>
  104. <view>(2)密码:身份证号码,再加111111</view>
  105. </view>
  106. </u-modal>
  107. <!-- 切换课程弹窗 -->
  108. <u-popup v-model="toggleCourseShow" mode="bottom" border-radius="40" :mask-close-able="false">
  109. <view class="popup_box">
  110. <view class="check_head">
  111. <view class="headers">
  112. <view class="grade">切换课程</view>
  113. <u-icon name="close" color="#9C9C9C" size="40" @click="closePop()"></u-icon>
  114. </view>
  115. <view class="coruse_num">共{{ goodsTeacher.length }}门</view>
  116. <view class="menuSel">
  117. <scroll-view class="sub_sliper" scroll-x="true">
  118. <view v-for="(item, index) in subList" :key="index" style="margin-right: 50rpx; display: inline-block">
  119. <view class="r_t1" :class="{ nactive: subIndex == index }" @click="cMenu(item, index)">
  120. {{ item.subjectName }}
  121. </view>
  122. </view>
  123. </scroll-view>
  124. </view>
  125. </view>
  126. <view class="check_con">
  127. <scroll-view scroll-y="true" style="height: 700rpx">
  128. <view v-for="(courseItem, gTindex) in goodsTeacher" :key="gTindex">
  129. <view v-for="(item, index) in courseItem.courseList" :key="index" v-show="item.subjectId === newActiveSubjectId || !newActiveSubjectId
  130. ">
  131. <view class="course_items" v-if="item.show && item.show == 1">
  132. <view class="course_lefts">
  133. <view class="course_title">
  134. {{ item.courseName }}
  135. </view>
  136. <view v-if="courseItem.teaList && courseItem.teaList.length > 0" class="teacher_names">
  137. <view v-for="(tea, tindex) in courseItem.teaList" :key="tindex" class="names">{{ tea.aliasName }}
  138. </view>
  139. </view>
  140. <view class="course_pros">
  141. 学习进度
  142. <text>
  143. {{ item.stuAllNum + item.recordNum }}/{{
  144. item.secAllNum + item.examNum
  145. }}</text>
  146. </view>
  147. </view>
  148. <view class="course_rights" @click="jump(item, gTindex, 'jump')">
  149. <view class="cicles">
  150. <u-icon name="arrow-right" color="#498AFE" size="20"></u-icon>
  151. </view>
  152. <view class="intoStudy">进入学习</view>
  153. </view>
  154. </view>
  155. </view>
  156. </view>
  157. </scroll-view>
  158. </view>
  159. </view>
  160. </u-popup>
  161. <!-- 摄像头 -->
  162. <Popup-camera @submitPhoto="submitPhoto" ref="camera" />
  163. </view>
  164. </template>
  165. <script>
  166. import handoutsBox from "@/components/course/handoutsBox.vue";
  167. import courseTree from "@/components/course/courseTree.vue";
  168. import PopupCamera from "../../components/popup/camera.vue";
  169. import myPlayer from "@/components/myPlayer/polyvPlayer.vue";
  170. import noteBox from "@/components/course/noteBox.vue";
  171. import answerBox from "@/components/course/answerBox.vue";
  172. import { mapGetters, mapMutations } from "vuex";
  173. import { lockAction } from "../../utils/lock";
  174. export default {
  175. components: {
  176. handoutsBox,
  177. myPlayer,
  178. noteBox,
  179. answerBox,
  180. courseTree,
  181. PopupCamera,
  182. },
  183. data() {
  184. return {
  185. markContent: "http://admin.zhujianpeixun.com/",
  186. showMark: false,
  187. lockTimer: null,
  188. orderGoodsId: 0,
  189. noticeShow: false,
  190. enableAutoRotation: true,
  191. showSet: false,
  192. courseId: 0,
  193. current: 0,
  194. vid: "",
  195. goodsId: 0,
  196. goodsData: {},
  197. showCamera: false,
  198. goodsPlayConfig: null,
  199. autoplay: false,
  200. isAllowSeek: "no",
  201. playbackRate: [1.0],
  202. timer: null,
  203. goodsPhotographConfig: null,
  204. playTime: 0, // 播放时刻
  205. currentTime: 0,
  206. avatarUrl: "",
  207. ossAvatarUrl: "",
  208. studyDuration: 0, // 当前视频时长
  209. gradeId: 0,
  210. chapterId: 0,
  211. moduleId: 0,
  212. videoCurrentTime: 0,
  213. photoIndex: 0,
  214. isTaking: true, //是否正在拍照
  215. needSeek: false, //第一次播放是否需要跳转
  216. photoNum: 0,
  217. photoList: [], //拍照的时间点
  218. photoConfig: false, //是否配置好拍照次数
  219. photoHistoryList: [], //已拍照历史的下标点
  220. sectionItem: {},
  221. uploadLock: false, //上传图片
  222. isRebuild: false, //视频是否从重修目录点击
  223. clearTimer: null,
  224. livingItem: "",
  225. option: null,
  226. toggleCourseShow: false, // 切换课程弹窗
  227. courseList: [], // 课程列表
  228. subList: [],
  229. subIndex: 0,
  230. goodsTeacher: [],
  231. teacherList: [],
  232. teacherIndex: 0,
  233. newActiveSubjectId: "", //当前选中ID
  234. compareFaceData: 0, // 拍照匹配相似度
  235. prendreAutoCarme: false, // 是否发起授权相机
  236. studyTimer: null, // 学习记录定时器
  237. CountTo: 0, // 倒计时
  238. CountTo1: 0,
  239. menuAllList: [],
  240. curPlayIndex: 0, // 正在播放的节的下标
  241. faceUrl: "",
  242. erJianErZao: false,
  243. jjShiGongYuan: false,
  244. pauseTime: 0,
  245. pauseTimer: null,
  246. isReach: false,
  247. noticeShow1: false,
  248. refPlv: null,
  249. isCache: false,
  250. isHaverebuild: false,
  251. isPlaying: false,
  252. beforeHideIsPlaying: false,
  253. };
  254. },
  255. computed: {
  256. ...mapGetters(["userInfo", "playSectionId", "config"]),
  257. playSecIsLearn() {
  258. return this.sectionItem.learning != 1;
  259. },
  260. orderNum() {
  261. return this.goodsData.goodsLearningOrder;
  262. },
  263. list() {
  264. let list = [
  265. {
  266. name: "目录",
  267. },
  268. {
  269. name: "讲义",
  270. },
  271. {
  272. name: "笔记",
  273. },
  274. {
  275. name: "答疑",
  276. },
  277. ];
  278. if (this.isHaverebuild) {
  279. list.push({ name: "重修目录" });
  280. }
  281. return list;
  282. },
  283. params: function () {
  284. return (keys = ["orderGoodsId", "goodsId", "courseId"]) => {
  285. let params = {};
  286. for (const key of keys) {
  287. params[key] = this[key];
  288. }
  289. return params;
  290. };
  291. },
  292. detail() {
  293. if (!this.courseId || !this.courseList.length) {
  294. return {};
  295. }
  296. return this.courseList.find((e) => e.courseId == this.courseId);
  297. },
  298. sectionId() {
  299. return this.sectionItem.sectionId || this.sectionItem.id;
  300. },
  301. playVid() {
  302. return this.sectionItem.recordingUrl;
  303. },
  304. isPlayRebuild() {
  305. return this.sectionItem.rebuild != 1;
  306. },
  307. },
  308. async onLoad(option) {
  309. console.log("🚀 ~ file: detail.vue:433 ~ onLoad ~ option:", option);
  310. if (option.isOther) {
  311. this.showMark = true;
  312. return;
  313. }
  314. this.option = option;
  315. let { skipPort, id, goodsId, orderGoodsId, gradeId, informId } = option;
  316. this.orderGoodsId = Number(orderGoodsId) || "";
  317. this.courseId = Number(id) || "";
  318. this.goodsId = Number(goodsId);
  319. this.gradeId = Number(gradeId);
  320. if (skipPort) {
  321. await this.$method.skipLogin(skipPort);
  322. }
  323. if (this.$method.isGoLogin()) {
  324. return;
  325. }
  326. // 公众号模板消息的数据埋点
  327. informId && this.clickOfficial(informId);
  328. this.init();
  329. },
  330. async onShow() {
  331. console.log("🚀 ~ file: detail.vue:455 ~ onShow ~ onShow:", this.isCache);
  332. if (this.isCache) {
  333. this.isCache = false;
  334. // #ifdef H5
  335. location.reload();
  336. // #endif
  337. // #ifdef MP-WEIXIN
  338. this.init();
  339. // #endif
  340. }
  341. if (this.beforeHideIsPlaying) {
  342. // 二建二造提示弹出来
  343. if (this.playSecIsLearn && this.erJianErZao && this.pauseTimer == null) {
  344. return;
  345. }
  346. this.refPlv.resumeVideo();
  347. }
  348. },
  349. onUnload() {
  350. console.log("onUnloadonUnloadonUnloadonUnload");
  351. this.originUnload();
  352. clearInterval(this.lockTimer);
  353. },
  354. onHide() {
  355. this.beforeHideIsPlaying = this.isPlaying;
  356. if (this.isPlaying) {
  357. this.refPlv.playPause();
  358. }
  359. this.originUnload();
  360. },
  361. mounted() { },
  362. methods: {
  363. ...mapMutations(["updateChapterOpen", "updateLiveLast"]),
  364. async init() {
  365. // #ifdef MP-WEIXIN
  366. this.isAllowSeek = "no";
  367. // #endif
  368. // #ifdef H5
  369. this.isAllowSeek = "on";
  370. // #endif
  371. // 锁
  372. lockAction();
  373. uni.addInterceptor("navigateTo", {
  374. success() {
  375. this.isCache = true;
  376. },
  377. });
  378. this.lockTimer = setInterval(lockAction, 10000);
  379. await this.isCanLearn()
  380. this.courseCourseList();
  381. },
  382. // 七大员是否能进入学习
  383. async qCheckIsCanLearn() {
  384. let res = await this.$api.qCheckIsCanLearn(this.orderGoodsId);
  385. if (res.data.code !== 200) {
  386. uni.showModal({
  387. showCancel: false,
  388. title: "提示",
  389. content: res.data.msg,
  390. success: (resultst) => {
  391. uni.navigateBack();
  392. },
  393. });
  394. return Promise.reject();
  395. }
  396. let res1 = await this.$api.syncSevenPublicClass({
  397. orderGoodsId: this.orderGoodsId,
  398. });
  399. if (res1.data.code !== 200) {
  400. uni.showModal({
  401. showCancel: false,
  402. title: "提示",
  403. content: "无法进入学习!",
  404. success: (resultst) => {
  405. uni.navigateBack();
  406. },
  407. });
  408. return Promise.reject();
  409. }
  410. },
  411. // 新增微信公众号模板消息点击数据
  412. clickOfficial(informId) {
  413. this.$http({
  414. url: "/data/click",
  415. method: "post",
  416. data: { informId },
  417. });
  418. },
  419. // 点击课程目录
  420. cMenu(item, index) {
  421. this.subIndex = index;
  422. this.newActiveSubjectId = item.subjectId;
  423. },
  424. courseCourseList() {
  425. this.courseList = [];
  426. this.photoConfig = false;
  427. this.$api
  428. .courseCourseList({
  429. pageNum: 1,
  430. pageSize: 200,
  431. ...this.params(["goodsId", "orderGoodsId", "gradeId"]),
  432. })
  433. .then((res) => {
  434. if (res.data.code == 200) {
  435. this.courseList = res.data.rows;
  436. if (res.data.total > 1) {
  437. // 科目
  438. this.subList = [{ subjectId: 0, subjectName: "所有" }];
  439. this.courseList.forEach((item) => {
  440. if (!this.subList.find((e) => e.subjectId == item.subjectId)) {
  441. this.subList.push(item);
  442. }
  443. });
  444. this.getUserWatchLast();
  445. } else {
  446. this.originOnShow();
  447. this.originMounted();
  448. }
  449. }
  450. });
  451. },
  452. // 查询用户最后一次看的录播的信息
  453. getUserWatchLast() {
  454. this.$http({
  455. url: "/study/record/getUserWatchLast",
  456. method: "get",
  457. data: {
  458. orderGoodsId: this.orderGoodsId,
  459. },
  460. }).then((res) => {
  461. if (res.data.code == 200 && res.data.data) {
  462. this.courseId = res.data.data.courseId;
  463. }
  464. if (!this.courseId) {
  465. this.courseId = this.courseList[0].courseId;
  466. }
  467. this.originOnShow();
  468. this.originMounted();
  469. //获取商品双师资模板
  470. this.getCourseTeacher(this.courseList);
  471. });
  472. },
  473. getCourseTeacher(rows) {
  474. this.goodsTeacher = [];
  475. //获取商品双师资模板
  476. this.$api
  477. .courseTeacherList({
  478. goodsId: this.goodsId,
  479. })
  480. .then((res1) => {
  481. if (res1.data.data && res1.data.data.length > 0) {
  482. //课程老师模板
  483. let teacherTel = res1.data.data;
  484. //商品课程
  485. let courses = rows;
  486. teacherTel.forEach((tea) => {
  487. let dataList = [];
  488. let teacherList = [];
  489. courses.forEach((item) => {
  490. let data = tea.courseList.filter(
  491. (x) => x.courseId == item.courseId
  492. );
  493. if (data && data.length > 0) {
  494. dataList.push(item);
  495. teacherList = tea.courseList;
  496. }
  497. });
  498. let result = {
  499. teaList: teacherList,
  500. courseList: dataList,
  501. };
  502. this.goodsTeacher.push(result);
  503. });
  504. if (this.goodsTeacher && this.goodsTeacher.length > 0) {
  505. let courseIds = [];
  506. this.goodsTeacher.forEach((item) => {
  507. item.courseList.forEach((course) => {
  508. courseIds.push(course.courseId);
  509. });
  510. });
  511. if (courseIds.length > 0) {
  512. courses.forEach((item) => {
  513. if (!courseIds.includes(item.courseId)) {
  514. let data = {
  515. teaList: [],
  516. courseList: [],
  517. };
  518. data.courseList.push(item);
  519. this.goodsTeacher.push(data);
  520. }
  521. });
  522. }
  523. this.goodsTeacher.forEach((item) => {
  524. if (item.courseList && item.courseList.length > 0) {
  525. item.courseList[0].show = 1;
  526. }
  527. });
  528. }
  529. } else {
  530. //没有双师资模板
  531. rows.forEach((item) => {
  532. item.show = 1;
  533. let data = {
  534. teaList: [],
  535. courseList: [],
  536. };
  537. data.courseList.push(item);
  538. this.goodsTeacher.push(data);
  539. });
  540. }
  541. this.goodsTeacher.forEach((item) => {
  542. if (item.courseList.some((x) => x.courseId == this.courseId)) {
  543. this.teacherList = item.teaList;
  544. }
  545. });
  546. });
  547. },
  548. erJianErZaoPauseTip() {
  549. if (this.playSecIsLearn && this.erJianErZao) {
  550. if (this.pauseTimer) {
  551. return;
  552. }
  553. this.pauseTime = Date.now();
  554. console.log("开启定时器");
  555. this.pauseTimer = setInterval(() => {
  556. console.log("暂停时间", Date.now() - this.pauseTime, this.pauseTime);
  557. if (Date.now() - this.pauseTime > 20 * 1000) {
  558. // 5 * 60 * 1000
  559. let text = "暂停";
  560. if (this.showCamera) {
  561. text = "拍照停留";
  562. this.closeCamera();
  563. }
  564. uni.showModal({
  565. title: "提示",
  566. showCancel: false,
  567. content: `检测${text}时间过长,刷新当前页面`,
  568. cancelText: "取消",
  569. confirmText: "确定",
  570. success: (res) => {
  571. if (res.confirm) {
  572. // #ifdef H5
  573. location.reload();
  574. // #endif
  575. // #ifdef MP-WEIXIN
  576. this.courseCourseList();
  577. // #endif
  578. }
  579. },
  580. });
  581. this.clearPauseTimer();
  582. }
  583. }, 5000);
  584. }
  585. },
  586. // 原来onshow里面的内容
  587. async originOnShow() {
  588. await this.studyRecordMenuAllList();
  589. // 消息过来 定位某个节
  590. if (this.option.noteSecond) {
  591. this.jumpNote({
  592. sectionType: 1,
  593. ...this.option,
  594. });
  595. return;
  596. }
  597. this.studyRecordQueryLiveLast();
  598. },
  599. // 原来的mouted内容
  600. originMounted() {
  601. uni.$on("getSection", (item) => {
  602. if (!this.refPlv) {
  603. this.refPlv = this.$refs.player;
  604. }
  605. this.postStudyRecord(0);
  606. //清除直播
  607. this.studyTimer && clearInterval(this.studyTimer);
  608. this.photoConfig = false;
  609. this.moduleId = item.moduleId || null;
  610. this.chapterId = item.chapterId || null;
  611. this.playVideo(item);
  612. });
  613. uni.$on("isRebuild", (item) => {
  614. this.isRebuild = item;
  615. });
  616. this.updateChapterOpen(true);
  617. },
  618. clearPauseTimer() {
  619. if (this.pauseTimer) {
  620. this.pauseTime = 0;
  621. clearInterval(this.pauseTimer);
  622. this.pauseTimer = null;
  623. }
  624. },
  625. // 原来onUnload里面的内容
  626. originUnload() {
  627. if (this.sectionId > 0 && this.isPlaying) {
  628. //退出提交记录
  629. this.postStudyRecord();
  630. }
  631. //清除正在播放的节ID
  632. // this.$store.commit('setPlayObj',null)
  633. this.$store.commit("setPlaySectionId", { playSectionId: 0 });
  634. //移除所有的事件监听器
  635. uni.$off();
  636. this.clearTimer && clearTimeout(this.clearTimer);
  637. if (this.studyTimer) {
  638. clearInterval(this.studyTimer);
  639. }
  640. this.clearPauseTimer();
  641. if (this.lockTimer) {
  642. clearInterval(this.lockTimer);
  643. this.$api
  644. .lockDelLock({
  645. action: "jxjy",
  646. uuid: this.$method.getUuid(),
  647. })
  648. .then((res) => {
  649. uni.hideLoading();
  650. });
  651. }
  652. },
  653. changeCourses() {
  654. this.toggleCourseShow = true;
  655. },
  656. closePop() {
  657. this.toggleCourseShow = false;
  658. },
  659. async activeFunc(item, index) {
  660. this.teacherIndex = index;
  661. let findResult = "";
  662. this.goodsTeacher.forEach((citem, index) => {
  663. citem.courseList.forEach((e, e_index) => {
  664. if (e.courseId == item.courseId) {
  665. findResult = e;
  666. }
  667. });
  668. });
  669. this.jump(findResult, 1);
  670. },
  671. // 进入学习
  672. async jump(item, index, type) {
  673. if (this.courseId === item.courseId) {
  674. this.toggleCourseShow = false;
  675. return;
  676. }
  677. if (this.orderNum == 2 && index != 0) {
  678. let prevItem = this.courseList[index - 1];
  679. if (
  680. prevItem.stuAllNum + prevItem.recordNum <
  681. prevItem.secAllNum + prevItem.examNum
  682. ) {
  683. uni.showToast({
  684. icon: "none",
  685. title: "请按顺序学完上一课再学习这一课",
  686. });
  687. return;
  688. }
  689. }
  690. // 打回 需重修
  691. if (item.rebuild === 0) {
  692. this.$navTo.togo("/pages2/learn/details", {
  693. id: item.courseId,
  694. ...this.params(["goodsId", "orderGoodsId", "gradeId"]),
  695. });
  696. return;
  697. }
  698. if (type) {
  699. this.teacherIndex = 0;
  700. this.goodsTeacher.forEach((citem) => {
  701. if (citem.courseList.some((x) => x.courseId == item.courseId)) {
  702. this.teacherList = citem.teaList;
  703. }
  704. });
  705. }
  706. this.sectionItem = {};
  707. this.toggleCourseShow = false;
  708. this.courseId = item.courseId;
  709. this.originOnShow();
  710. },
  711. clickLeft() {
  712. // uni.navigateBack()
  713. uni.switchTab({
  714. url: "/pages/learn/index",
  715. });
  716. },
  717. toBack(delta = 1) {
  718. uni.navigateBack({
  719. delta,
  720. });
  721. },
  722. markConfirm() {
  723. uni.setClipboardData({
  724. data: this.markContent,
  725. success: () => {
  726. setTimeout(this.toBack, 1000);
  727. },
  728. });
  729. },
  730. noticeConfirm() {
  731. if (this.CountTo <= 0) {
  732. this.noticeShow = false;
  733. if (this.autoplay) {
  734. this.refPlv && this.refPlv.resumeVideo();
  735. }
  736. this.$api.baseHandoutTip({
  737. orderGoodsId: this.orderGoodsId,
  738. });
  739. }
  740. },
  741. baseHandoutTipList() {
  742. this.$api
  743. .baseHandoutTipList({
  744. orderGoodsId: this.orderGoodsId,
  745. })
  746. .then((res) => {
  747. if (res.data.rows.length == 0) {
  748. this.noticeShow = true;
  749. this.CountTo = 30;
  750. var timer = setInterval(() => {
  751. this.CountTo--;
  752. if (this.CountTo < 0) {
  753. clearInterval(timer);
  754. }
  755. }, 1000);
  756. }
  757. });
  758. },
  759. /**
  760. * 获取上次观看的直播
  761. */
  762. studyRecordGetLastLive() {
  763. this.$api
  764. .studyRecordGetLastLive({
  765. orderGoodsId: this.orderGoodsId,
  766. courseId: this.courseId,
  767. })
  768. .then((res) => {
  769. this.updateLiveLast(res.data.data);
  770. });
  771. },
  772. async initPlayVideo(sectionItem) {
  773. this.moduleId = sectionItem.moduleId;
  774. this.chapterId = sectionItem.chapterId;
  775. this.sectionItem = sectionItem;
  776. if (sectionItem.sectionType == 1) {
  777. //录播
  778. this.playVideo(sectionItem);
  779. } else if (sectionItem.sectionType == 2) {
  780. //直播
  781. this.studyRecordGetLastLive();
  782. } else if (sectionItem.sectionType == 3) {
  783. //回放
  784. this.playVideo(sectionItem);
  785. } else if (sectionItem.doType == 2) {
  786. uni.showModal({
  787. title: "温馨提示",
  788. content: "当前节视频已学完,是否进入考试?",
  789. success: (res) => {
  790. if (res.confirm) {
  791. this.toQuestionBank(sectionItem);
  792. }
  793. },
  794. });
  795. return;
  796. }
  797. },
  798. async submitPhoto(url, compareFaceData, cb) {
  799. this.ossAvatarUrl = url;
  800. this.compareFaceData = compareFaceData;
  801. this.postCoursePhotoRecord()
  802. .then(async (res) => {
  803. this.photoHistoryList[this.photoIndex] = true;
  804. await this.postStudyRecord();
  805. if (this.erJianErZao && this.isReach) {
  806. await this.postStudyRecord(1);
  807. cb();
  808. this.nextSection();
  809. return;
  810. }
  811. cb();
  812. this.refPlv.resumeVideo();
  813. this.enableAutoRotation = true;
  814. })
  815. .catch((err) => {
  816. uni.showToast({
  817. title: "上传接口报错,请重新拍照上传" + err,
  818. icon: "none",
  819. });
  820. cb(1);
  821. });
  822. },
  823. toQuestionBank(sectionItem) {
  824. uni.navigateTo({
  825. url:
  826. "/pages2/class/questionBank?courseId=" +
  827. this.courseId +
  828. "&gradeId=" +
  829. this.gradeId +
  830. "&isFromVideo=1&id=" +
  831. sectionItem.id +
  832. "&goodsid=" +
  833. this.goodsId +
  834. "&moduleId=" +
  835. (sectionItem.moduleId || 0) +
  836. "&chapterId=" +
  837. (sectionItem.chapterId || 0) +
  838. "&orderGoodsId=" +
  839. this.orderGoodsId +
  840. "&type=" +
  841. (sectionItem.type == 4 ? 1 : 3) +
  842. "&learning=" +
  843. sectionItem.studyStatus +
  844. "&isBackVideo=" +
  845. 1,
  846. });
  847. },
  848. studyRecordQueryLiveLast() {
  849. this.$api
  850. .studyRecordQueryLiveLast({
  851. gradeId: this.gradeId,
  852. orderGoodsId: this.orderGoodsId,
  853. courseId: this.courseId,
  854. })
  855. .then((res) => {
  856. let { data } = res.data;
  857. if (!data.sectionId) {
  858. data = this.menuAllList[0];
  859. }
  860. if (data.learning == 1 && this.orderNum == 2) {
  861. let next = this.menuAllList.find((e) => e.studyStatus != 1);
  862. next && (data = next);
  863. }
  864. this.initPlayVideo(data);
  865. });
  866. },
  867. goLive(item) {
  868. let moduleId = item.moduleId || 0;
  869. let chapterId = item.chapterId || 0;
  870. let sectionId = item.sectionId || item.menuId;
  871. let uuid = new Date().valueOf() + "";
  872. // buyCourse 是否购买课程:1是 0否
  873. let encode = encodeURIComponent(
  874. this.config.hostLive +
  875. "/pages/live/index?token=" +
  876. uni.getStorageSync("token") +
  877. "&userInfo=" +
  878. JSON.stringify(this.userInfo) +
  879. "&channelId=" +
  880. item.liveUrl +
  881. "&gradeId=" +
  882. this.gradeId +
  883. "&courseId=" +
  884. this.courseId +
  885. "&goodsId=" +
  886. this.goodsId +
  887. "&orderGoodsId=" +
  888. this.orderGoodsId +
  889. "&sectionId=" +
  890. sectionId +
  891. "&chapterId=" +
  892. chapterId +
  893. "&moduleId=" +
  894. moduleId +
  895. "&buyCourse=1" +
  896. "&ident=" +
  897. uuid
  898. );
  899. uni.navigateTo({
  900. url: `../../pages/webview/index?url=` + encode,
  901. });
  902. },
  903. studyRecordMenuAllList() {
  904. // study/record/menuAllList
  905. return this.$api
  906. .studMenuAllList({
  907. courseId: this.courseId,
  908. ...this.params(["goodsId", "orderGoodsId", "gradeId"]),
  909. })
  910. .then((res) => {
  911. let nowTime = Number(new Date().getTime() / 1000).toFixed(0);
  912. if (res.data.data) {
  913. this.menuAllList = res.data.data.filter(
  914. (e) => e.doType != 1 || (e.doType == 2 && e.studyStatus == 1)
  915. );
  916. this.livingItem = res.data.data.find(
  917. (item) =>
  918. item.liveStartTime <= nowTime && item.liveEndTime > nowTime
  919. );
  920. }
  921. return Promise.resolve();
  922. });
  923. },
  924. async getbaseprofiletplists() {
  925. let {
  926. data: { code, rows },
  927. } = await this.$api.getbaseprofiletplists({
  928. goodsId: this.goodsId,
  929. orderGoodsId: this.orderGoodsId,
  930. });
  931. if (code === 200 && rows.length && rows[0].keyValue) {
  932. let baseRes = await this.$api.getbaseprofiletpId(this.goodsId);
  933. if (baseRes.data.code === 200 && baseRes.data.data) {
  934. let {
  935. data: { code, data },
  936. } = await this.$api.getbaseprofiletpgetInfo({
  937. goodsId: this.goodsId,
  938. orderGoodsId: this.orderGoodsId,
  939. });
  940. if (
  941. code === 200 &&
  942. (!data || (data.status === 3 && data.changeStatus === 1))
  943. ) {
  944. uni.showModal({
  945. content: !data
  946. ? "请前往填写资料"
  947. : "资料审核不通过,请前往重新填写",
  948. cancelText: "返回",
  949. success: (resultst) => {
  950. if (resultst.confirm) {
  951. this.$navTo.togo("/pages2/verify/input", {
  952. id: this.goodsId,
  953. orderGoodsId: this.orderGoodsId,
  954. });
  955. }
  956. if (resultst.cancel) {
  957. uni.navigateBack();
  958. }
  959. },
  960. });
  961. return Promise.reject();
  962. } else if (data.status === 1 && JSON.parse(rows[0].keyValue2)[0]) {
  963. let {
  964. data: { code, data },
  965. } = await this.$api.getbaseprofileStampgetInfo({
  966. goodsId: this.goodsId,
  967. orderGoodsId: this.orderGoodsId,
  968. });
  969. if (
  970. code === 200 &&
  971. (!data || (data.status === 3 && data.changeStatus === 1))
  972. ) {
  973. uni.showModal({
  974. content: !data
  975. ? "请前往填写盖章资料"
  976. : "资料盖章审核不通过,请前往重新填写",
  977. cancelText: "返回",
  978. success: (resultst) => {
  979. if (resultst.confirm) {
  980. this.$navTo.togo("/pages2/verify/input2", {
  981. id: this.goodsId,
  982. orderGoodsId: this.orderGoodsId,
  983. });
  984. }
  985. if (resultst.cancel) {
  986. uni.navigateBack();
  987. }
  988. },
  989. });
  990. return Promise.reject();
  991. }
  992. }
  993. }
  994. }
  995. return Promise.resolve();
  996. },
  997. async isCanLearn() {
  998. return Promise.resolve();
  999. this.option.isQ !== "" && (await this.qCheckIsCanLearn());
  1000. await this.getbaseprofiletplists();
  1001. await this.getGradeInfo();
  1002. await this.getGoodsDetail();
  1003. },
  1004. /**
  1005. * 计算tabs宽度
  1006. */
  1007. itemWidth() {
  1008. return 100 / this.list.length + "%";
  1009. },
  1010. loadedmetadata(event) {
  1011. this.refPlv = this.$refs.player;
  1012. },
  1013. getPhotoLastRecord() {
  1014. let { chapterId, sectionId, moduleId } = this.sectionItem;
  1015. let data = {
  1016. ...this.params(),
  1017. sectionId: sectionId || 0,
  1018. gradeId: this.gradeId,
  1019. chapterId: chapterId || 0,
  1020. moduleId: moduleId || 0,
  1021. };
  1022. this.$api.getPhotoLastRecord(data).then((res) => {
  1023. if (res.data.code == 200) {
  1024. //清空历史数据
  1025. this.photoHistoryList = [];
  1026. this.photoList = [];
  1027. for (let i = 0; i < res.data.data.length; i++) {
  1028. //-2存储随机拍照数组
  1029. if (res.data.data[i].photoIndex == -2) {
  1030. this.photoList =
  1031. res.data.data[i].timeInterval &&
  1032. res.data.data[i].timeInterval.split(",");
  1033. } else {
  1034. this.photoHistoryList.push(res.data.data[i].photoIndex);
  1035. }
  1036. }
  1037. }
  1038. });
  1039. },
  1040. //postTime 只提交随机时间
  1041. postCoursePhotoRecord(postTime = false) {
  1042. return new Promise((resolve, reject) => {
  1043. let data = {
  1044. ...this.params(["orderGoodsId", "goodsId", "courseId", "gradeId"]),
  1045. photo: this.ossAvatarUrl,
  1046. sectionId: this.sectionId,
  1047. photoTime: this.playTime || 0,
  1048. photoIndex: postTime ? -2 : this.photoIndex, //从0算起,-2只提交随机时间
  1049. photoNum: this.photoNum,
  1050. chapterId: this.chapterId,
  1051. moduleId: this.moduleId,
  1052. timeInterval: postTime ? this.photoList.join(",") : "",
  1053. };
  1054. this.$api
  1055. .coursePhotoRecord(data)
  1056. .then((res) => {
  1057. if (res.data.code == 200) {
  1058. resolve();
  1059. } else {
  1060. reject();
  1061. }
  1062. })
  1063. .catch((err) => {
  1064. reject();
  1065. });
  1066. });
  1067. },
  1068. randomNum(minNum, maxNum) {
  1069. switch (arguments.length) {
  1070. case 1:
  1071. return parseInt(Math.random() * minNum + 1, 10);
  1072. break;
  1073. case 2:
  1074. return parseInt(Math.random() * (maxNum - minNum + 1) + minNum, 10);
  1075. break;
  1076. default:
  1077. return 0;
  1078. break;
  1079. }
  1080. },
  1081. //配置随机拍照时间
  1082. configPhoto(duration) {
  1083. if (this.photoConfig) {
  1084. return;
  1085. }
  1086. let totalVideoTime = this.refPlv.getDuration();
  1087. this.photoConfig = true;
  1088. if (this.erJianErZao) {
  1089. this.photoList = this.randomConfig(totalVideoTime, duration);
  1090. return;
  1091. }
  1092. //没有历史拍照间隔数据
  1093. if (!this.photoList || this.photoList.length == 0) {
  1094. // 46 * 60
  1095. this.photoList = this.commonConfig(
  1096. totalVideoTime,
  1097. this.jjShiGongYuan ? 10 : undefined
  1098. );
  1099. this.postCoursePhotoRecord(true); //提交随机拍照时间数组
  1100. }
  1101. console.log(this.photoList, "this.photoList");
  1102. },
  1103. // 二建随机拍摄时间
  1104. randomConfig(totalVideoTime, duration) {
  1105. this.photoHistoryList = [];
  1106. let photoList = [duration];
  1107. let pre = duration;
  1108. if (totalVideoTime > 300) {
  1109. while (pre <= totalVideoTime) {
  1110. pre += this.randomNum(780, 900);
  1111. pre <= totalVideoTime && photoList.push(pre);
  1112. }
  1113. if (totalVideoTime - 300 > photoList.slice(-1)[0]) {
  1114. photoList.push(this.randomNum(totalVideoTime - 180, totalVideoTime));
  1115. }
  1116. }
  1117. return photoList;
  1118. },
  1119. // 随机前后五分钟
  1120. commonConfig(totalVideoTime, fixS) {
  1121. let photoList = [0];
  1122. // 固定间隔时间取
  1123. if (fixS) {
  1124. let num = Math.ceil(totalVideoTime / fixS);
  1125. for (let i = 1; i < num; i++) {
  1126. photoList.push(i * fixS);
  1127. }
  1128. this.photoNum = num;
  1129. } else {
  1130. if (this.photoNum == 3) {
  1131. if (totalVideoTime >= 900) {
  1132. //大于15分钟
  1133. let centerTime = Math.floor(totalVideoTime / 2); //获取中间时间
  1134. let centerMinTime = centerTime - 300; //前后5分钟
  1135. let centerMaxTime = centerTime + 300;
  1136. let centerTakeTime = this.randomNum(centerMinTime, centerMaxTime);
  1137. photoList.push(centerTakeTime); //中间拍一张
  1138. let endMaxTime = totalVideoTime - 60;
  1139. let endMinTime = totalVideoTime - 300;
  1140. let endTakeTime = this.randomNum(endMinTime, endMaxTime);
  1141. photoList.push(endTakeTime); //最后拍一张
  1142. } else {
  1143. //小于15分钟
  1144. let centerTime = this.randomNum(
  1145. (1 / 3) * totalVideoTime,
  1146. (2 / 3) * totalVideoTime
  1147. );
  1148. photoList.push(centerTime);
  1149. let endTakeTime = this.randomNum(
  1150. (2 / 3) * totalVideoTime,
  1151. totalVideoTime
  1152. );
  1153. photoList.push(endTakeTime);
  1154. }
  1155. }
  1156. }
  1157. return photoList;
  1158. },
  1159. studyNotice() {
  1160. this.noticeShow = true;
  1161. },
  1162. //正常播放视频
  1163. async playVideo(item) {
  1164. this.sectionItem = item;
  1165. let { learning, videoCurrentTime, sectionId, recordingUrl } = item;
  1166. this.videoCurrentTime =
  1167. videoCurrentTime || (await this.getRecordLast(item));
  1168. // 往前播3秒
  1169. if (this.videoCurrentTime > 3) {
  1170. this.videoCurrentTime -= 3;
  1171. }
  1172. // 查找拍照历史
  1173. if ((this.photoNum > 0 || this.jjShiGongYuan) && learning != 1) {
  1174. await this.getPhotoLastRecord();
  1175. }
  1176. this.$store.commit("setPlaySectionId", {
  1177. playSectionId: sectionId,
  1178. });
  1179. if (this.refPlv) {
  1180. this.refPlv.changeVid({
  1181. vid: recordingUrl,
  1182. videoCurrentTime: this.videoCurrentTime,
  1183. });
  1184. }
  1185. },
  1186. getRecordLast(sectionItem) {
  1187. let { chapterId, sectionId, courseId, moduleId } = sectionItem;
  1188. return new Promise((resolve) => {
  1189. let data = {
  1190. ...this.params(),
  1191. gradeId:
  1192. this.gradeId || this.gradeId == 0 ? Number(this.gradeId) : null,
  1193. sectionId: sectionId || 0,
  1194. chapterId: chapterId || 0,
  1195. moduleId: moduleId || 0,
  1196. };
  1197. this.$api.recordLast(data).then((res) => {
  1198. resolve(res.data.data.videoCurrentTime);
  1199. });
  1200. });
  1201. },
  1202. jumpNote(item) {
  1203. this.$u.toast("即将跳到笔记位置");
  1204. if (this.sectionId != item.sectionId) {
  1205. this.initPlayVideo({
  1206. sectionType: 1,
  1207. ...item,
  1208. videoCurrentTime: Number(item.noteSecond),
  1209. });
  1210. } else {
  1211. this.refPlv.seekVideo(item.noteSecond);
  1212. }
  1213. },
  1214. getGradeInfo() {
  1215. // 即刻 1 待定2 日期3
  1216. return this.$api.goodsGradeInfo(this.gradeId).then((res) => {
  1217. if (res.data.code == 200) {
  1218. let { data } = res.data;
  1219. if (
  1220. data.learningStatus == 2 ||
  1221. (data.learningStatus == 3 &&
  1222. Number(data.learningTimeStart) > Number(new Date() / 1000))
  1223. ) {
  1224. uni.showModal({
  1225. showCancel: false,
  1226. confirmText: "确定",
  1227. content:
  1228. "当前课程正在申请中,正式开班后方可进行学习,请耐心等候!",
  1229. success: function (resultst) {
  1230. uni.navigateBack();
  1231. },
  1232. });
  1233. return Promise.reject();
  1234. }
  1235. }
  1236. });
  1237. },
  1238. postStudyRecord(status = 0, sectionId = this.sectionId) {
  1239. if (!this.refPlv) {
  1240. return;
  1241. }
  1242. let currentTime = this.refPlv.playCurrentTime();
  1243. let PlayDuration = this.refPlv.playVideoTime();
  1244. if (currentTime < 10 && !this.ossAvatarUrl) {
  1245. return;
  1246. }
  1247. let self = this;
  1248. let data = {
  1249. ...this.params([
  1250. "orderGoodsId",
  1251. "goodsId",
  1252. "courseId",
  1253. "erJianErZao",
  1254. "gradeId",
  1255. ]),
  1256. fromPlat: 1, //来源平台 1小程序 2网站
  1257. photo: self.ossAvatarUrl,
  1258. sectionId: sectionId || 0,
  1259. studyDuration: parseInt(
  1260. PlayDuration > 0 ? PlayDuration : self.studyDuration
  1261. ),
  1262. chapterId: this.chapterId || 0,
  1263. moduleId: this.moduleId || 0,
  1264. videoCurrentTime: parseInt(
  1265. currentTime > 0 ? currentTime : self.studyDuration
  1266. ),
  1267. };
  1268. if (this.ossAvatarUrl) {
  1269. data.similarity = this.compareFaceData; // 相似度
  1270. }
  1271. if (status > 0) {
  1272. data.status = status;
  1273. }
  1274. console.log(data, "记录参数");
  1275. return new Promise((resolve, reject) => {
  1276. this.$api
  1277. .studyRecord(data)
  1278. .then((res) => {
  1279. console.log(res, "记录返回");
  1280. let { code, msg } = res.data;
  1281. if (code == 200) {
  1282. if (status > 0) {
  1283. // 刷新数据
  1284. uni.$emit("playEnd");
  1285. this.studyRecordMenuAllList();
  1286. }
  1287. self.ossAvatarUrl = "";
  1288. } else if (code == 600) {
  1289. uni.showModal({
  1290. showCancel: false,
  1291. title: "提示",
  1292. content: msg,
  1293. success: (resultst) => {
  1294. uni.navigateBack();
  1295. },
  1296. });
  1297. } else if (code == 558) {
  1298. this.CountTo1 = msg.split(",")[1];
  1299. this.noticeShow1 = true;
  1300. var timer = setInterval(() => {
  1301. this.CountTo1--;
  1302. if (this.CountTo1 < 0) {
  1303. this.noticeShow1 = false;
  1304. clearInterval(timer);
  1305. this.postStudyRecord(1);
  1306. }
  1307. }, 1000);
  1308. reject("中断执行");
  1309. } else {
  1310. this.uploadLock = false;
  1311. uni.showToast({
  1312. icon: "none",
  1313. title: res.data.msg,
  1314. duration: 2000,
  1315. });
  1316. if (!this.erJianErZao && code == 559) {
  1317. this.isReach = true;
  1318. this.$refs["camera"].reTake();
  1319. }
  1320. if (code == 559 || code == 588) {
  1321. reject(res.data.msg);
  1322. }
  1323. }
  1324. resolve();
  1325. })
  1326. .catch((err) => {
  1327. this.studyRecordMenuAllList();
  1328. });
  1329. });
  1330. },
  1331. timeEvent(time) {
  1332. this.clearPauseTimer();
  1333. if (this.playSecIsLearn && (this.erJianErZao || this.photoNum > 0)) {
  1334. this.configPhoto(time);
  1335. this.isReach = false;
  1336. const index = this.photoList.findIndex((e) => e < time && e > time - 8);
  1337. if (index != -1 && !this.photoHistoryList[index]) {
  1338. this.photoIndex = index;
  1339. // 拍照
  1340. this.openCamera();
  1341. }
  1342. }
  1343. },
  1344. // 新增用户视频学习日志
  1345. studyLog() {
  1346. this.$http({
  1347. url: "/user/study/log",
  1348. method: "post",
  1349. data: {
  1350. ...this.params(),
  1351. moduleId: this.moduleId || 0,
  1352. chapterId: this.chapterId || 0,
  1353. sectionId: this.sectionId || 0,
  1354. fromPlat: 1, //来源平台 1小程序 2PC网站
  1355. goodsType: 1, // 商品类型 1视频2题库 3补考 4前培 5虚拟赠送题库 6直播
  1356. },
  1357. }).then((res) => { });
  1358. },
  1359. timeupdate(time) {
  1360. if (this.noticeShow) {
  1361. this.refPlv.playPause();
  1362. return;
  1363. }
  1364. this.playTime = time;
  1365. this.clearPauseTimer();
  1366. this.timeEvent(time);
  1367. },
  1368. playing() {
  1369. console.log("palying");
  1370. this.isPlaying = true;
  1371. this.studyLog();
  1372. if (!this.videoCurrentTime) {
  1373. this.postStudyRecord(0);
  1374. }
  1375. this.studyTimer && clearInterval(this.studyTimer);
  1376. this.studyTimer = setInterval(() => {
  1377. this.postStudyRecord(0);
  1378. }, 15000);
  1379. },
  1380. openCamera() {
  1381. this.showCamera = true;
  1382. this.$refs["camera"].openCamera();
  1383. this.refPlv.playPause();
  1384. this.refPlv.exitFullScreen();
  1385. },
  1386. closeCamera() {
  1387. this.showCamera = false;
  1388. this.$refs["camera"].closeCamera();
  1389. },
  1390. pause() {
  1391. this.isPlaying = false;
  1392. this.erJianErZaoPauseTip();
  1393. clearInterval(this.studyTimer);
  1394. },
  1395. async ended() {
  1396. console.log("🚀 ~ file: detail.vue:1519 ~ ended ~ ended:", "结束");
  1397. this.isPlaying = false;
  1398. uni.showToast({
  1399. icon: "none",
  1400. title: "播放完毕",
  1401. });
  1402. clearInterval(this.studyTimer);
  1403. await this.postStudyRecord(1);
  1404. this.nextSection();
  1405. },
  1406. playerError() {
  1407. console.log("播放错误");
  1408. },
  1409. //播放下一节
  1410. nextSection() {
  1411. console.log("播放下一节");
  1412. if (!this.menuAllList.length) {
  1413. return;
  1414. }
  1415. this.curPlayIndex = this.menuAllList.findIndex((item) => {
  1416. let i_sectionId = item.sectionId || 0;
  1417. let i_chapterId = item.chapterId || 0;
  1418. let i_moduleId = item.moduleId || 0;
  1419. return (
  1420. i_sectionId == this.sectionId &&
  1421. i_chapterId == this.chapterId &&
  1422. i_moduleId == this.moduleId
  1423. );
  1424. });
  1425. let data = this.menuAllList[this.curPlayIndex + 1];
  1426. if (!data) {
  1427. //第二个弹窗
  1428. uni.showModal({
  1429. title: "温馨提示",
  1430. content:
  1431. "恭喜您课程学习全部完成,教务会在1-3个工作日内完成学习初审,请耐心等待。",
  1432. showCancel: !this.erJianErZao,
  1433. success: (res) => {
  1434. if (res.confirm) {
  1435. uni.switchTab({
  1436. url: "/pages/learn/index",
  1437. });
  1438. }
  1439. },
  1440. });
  1441. } else {
  1442. if (data.doType == 2) {
  1443. if (data.studyStatus == 1) {
  1444. uni.showToast({
  1445. title: "试卷已合格!",
  1446. duration: 2000,
  1447. icon: "none",
  1448. });
  1449. return;
  1450. }
  1451. uni.showModal({
  1452. title: "温馨提示",
  1453. content: "当前节视频已学完,是否进入考试?",
  1454. success: (res) => {
  1455. if (res.confirm) {
  1456. this.toQuestionBank(data);
  1457. }
  1458. },
  1459. });
  1460. return;
  1461. }
  1462. uni.showModal({
  1463. title: "温馨提示",
  1464. content: "当前节视频已学完,继续学习下一节?",
  1465. success: async (res) => {
  1466. if (res.confirm) {
  1467. this.moduleId = data.moduleId;
  1468. this.chapterId = data.chapterId;
  1469. this.sectionId = data.sectionId;
  1470. if (data.sectionType == 1) {
  1471. //录播
  1472. this.$store.commit("setPlaySectionId", {
  1473. playSectionId: data.sectionId,
  1474. });
  1475. this.photoConfig = false;
  1476. this.sectionItem = data;
  1477. this.playVideo(data);
  1478. } else if (data.sectionType == 2) {
  1479. //直播
  1480. this.studyRecordGetLastLive();
  1481. } else if (data.sectionType == 3) {
  1482. //回放
  1483. this.$store.commit("setPlaySectionId", {
  1484. playSectionId: data.sectionId,
  1485. });
  1486. this.sectionItem = data;
  1487. this.playVideo(data);
  1488. }
  1489. let playNextId = `moduleId${data.moduleId}chapterId${data.chapterId}sectionId${data.sectionId}`;
  1490. this.$store.commit("updatePlayNextId", playNextId);
  1491. this.updateChapterOpen(true);
  1492. }
  1493. },
  1494. });
  1495. }
  1496. },
  1497. playError(e) {
  1498. console.log(e);
  1499. },
  1500. async userConfirmInfoDetail() {
  1501. let info = await this.$api.userConfirmInfoDetail({
  1502. orderGoodsId: this.orderGoodsId,
  1503. });
  1504. if (!info.data.data || info.data.data.pushInfo !== 1) {
  1505. uni.showModal({
  1506. showCancel: false,
  1507. title: "提示",
  1508. content: "开通信息推送不成功,无法进入学习!",
  1509. success: (resultst) => {
  1510. uni.navigateBack();
  1511. },
  1512. });
  1513. return Promise.reject();
  1514. }
  1515. },
  1516. checkFinishRequiredCourse() {
  1517. return this.$api
  1518. .checkFinishRequiredCourse({
  1519. businessId: this.goodsData.businessId,
  1520. goodsId: this.goodsId,
  1521. })
  1522. .then((res) => {
  1523. if (res.data.data > 0) {
  1524. uni.showModal({
  1525. showCancel: false,
  1526. confirmText: "确定",
  1527. content: "该业务层次下有未学完的商品,无法学习新商品!",
  1528. success: function (resultst) {
  1529. uni.navigateBack();
  1530. },
  1531. });
  1532. return Promise.reject();
  1533. }
  1534. return Promise.resolve();
  1535. });
  1536. },
  1537. async getGoodsDetail() {
  1538. let { data } = await this.$api.goodsDetail(this.goodsId);
  1539. this.goodsData = data.data;
  1540. this.erJianErZao = this.goodsData.erJianErZao;
  1541. this.jjShiGongYuan = this.goodsData.jjShiGongYuan;
  1542. this.erJianErZao && (await this.userConfirmInfoDetail());
  1543. this.option.periodWaitTime && (await this.checkFinishRequiredCourse());
  1544. this.goodsData.buyNote && this.baseHandoutTipList();
  1545. !this.gradeId && (this.gradeId = this.goodsData.gradeId);
  1546. if (this.goodsData.goodsPlayConfig) {
  1547. this.goodsPlayConfig = JSON.parse(this.goodsData.goodsPlayConfig);
  1548. if (this.goodsPlayConfig.autoPlay > 0) {
  1549. this.autoplay = true;
  1550. }
  1551. if (this.goodsPlayConfig.drag > 0 && !this.erJianErZao) {
  1552. // #ifdef MP-WEIXIN
  1553. this.isAllowSeek = "yes";
  1554. // #endif
  1555. // #ifdef H5
  1556. this.isAllowSeek = "off";
  1557. // #endif
  1558. }
  1559. if (this.goodsPlayConfig.speed > 0) {
  1560. this.playbackRate = [0.5, 0.8, 1.0, 1.25, 1.5, 2.0];
  1561. }
  1562. }
  1563. if (this.goodsData.goodsPhotographConfig) {
  1564. this.goodsPhotographConfig = JSON.parse(
  1565. this.goodsData.goodsPhotographConfig
  1566. );
  1567. if (this.goodsPhotographConfig.photoNum > 0) {
  1568. this.photoNum = this.goodsPhotographConfig.photoNum;
  1569. }
  1570. }
  1571. },
  1572. change(index) {
  1573. this.current = index;
  1574. },
  1575. paramsFn(
  1576. keys = ["orderGoodsId", "goodsId", "courseId", "gradeId", "sectionItem"]
  1577. ) {
  1578. return this.params(keys);
  1579. },
  1580. },
  1581. provide() {
  1582. return {
  1583. paramsFn: this.paramsFn,
  1584. };
  1585. },
  1586. };
  1587. </script>
  1588. <style lang="scss" scope>
  1589. @import "./css/detail.scss";
  1590. .top {
  1591. &__header {
  1592. position: relative;
  1593. width: 100%;
  1594. height: 150rpx;
  1595. padding: 24rpx 150rpx 24rpx 24rpx;
  1596. .img {
  1597. position: absolute;
  1598. left: 0;
  1599. top: 0;
  1600. width: 100%;
  1601. }
  1602. .note {
  1603. position: relative;
  1604. z-index: 10;
  1605. font-size: 24rpx;
  1606. font-family: PingFang SC;
  1607. font-weight: bold;
  1608. color: #efdbff;
  1609. }
  1610. .title {
  1611. position: relative;
  1612. z-index: 10;
  1613. font-size: 26rpx;
  1614. font-family: PingFang SC;
  1615. font-weight: bold;
  1616. color: #ffffff;
  1617. }
  1618. }
  1619. }
  1620. #top {
  1621. position: relative;
  1622. z-index: 99;
  1623. }
  1624. .polyv_detail {
  1625. display: flex;
  1626. flex-direction: column;
  1627. height: 100vh;
  1628. position: relative;
  1629. top: 0;
  1630. left: 0;
  1631. .pops {
  1632. position: absolute;
  1633. top: 0;
  1634. left: 0;
  1635. background: #ccc;
  1636. opacity: 0.5;
  1637. width: 100%;
  1638. height: 300rpx;
  1639. z-index: 9999;
  1640. }
  1641. .box {
  1642. flex: 1;
  1643. overflow: hidden;
  1644. margin: 16rpx 16rpx 100rpx 16rpx;
  1645. .box_in {
  1646. height: 100%;
  1647. }
  1648. }
  1649. .first_ml {
  1650. margin: 16rpx 16rpx 16rpx 16rpx;
  1651. }
  1652. }
  1653. .title {
  1654. font-size: 24rpx;
  1655. color: #999999;
  1656. }
  1657. page {
  1658. background: #eaeef1;
  1659. }
  1660. .ts {
  1661. font-size: 24rpx;
  1662. color: #999;
  1663. margin: 14rpx 0rpx;
  1664. padding-right: 29rpx;
  1665. padding-left: 34rpx;
  1666. }
  1667. .content {
  1668. padding: 24rpx;
  1669. text-align: left;
  1670. }
  1671. .sc_t {
  1672. font-size: 22rpx;
  1673. color: #000000;
  1674. }
  1675. .sc {
  1676. width: 29rpx;
  1677. height: 29rpx;
  1678. }
  1679. .buy {
  1680. width: 138rpx;
  1681. height: 48rpx;
  1682. line-height: 48rpx;
  1683. background: #32467b;
  1684. border-radius: 10rpx;
  1685. color: #ffffff;
  1686. font-size: 28rpx;
  1687. text-align: center;
  1688. vertical-align: middle;
  1689. position: absolute;
  1690. right: 30rpx;
  1691. }
  1692. .video_body {
  1693. padding-bottom: 96rpx;
  1694. }
  1695. .tj_box {
  1696. width: 50%;
  1697. display: inline-block;
  1698. text-align: center;
  1699. margin: 10rpx 0;
  1700. }
  1701. .t2 {
  1702. font-size: 24rpx;
  1703. font-family: PingFang SC;
  1704. color: #666666;
  1705. line-height: 36rpx;
  1706. margin: 15rpx;
  1707. }
  1708. .r_t2 {
  1709. width: 201rpx;
  1710. height: 49rpx;
  1711. background: rgba(22, 119, 255, 0.05);
  1712. border: 1rpx solid #32467b;
  1713. border-radius: 16rpx;
  1714. color: #666666;
  1715. font-size: 23rpx;
  1716. text-align: center;
  1717. display: flex;
  1718. align-items: center;
  1719. padding: 5rpx;
  1720. }
  1721. .scroll_box {
  1722. width: 100%;
  1723. height: 60rpx;
  1724. background: #ffffff;
  1725. box-shadow: 0rpx 0rpx 16rpx 4rpx rgba(145, 156, 178, 0.1);
  1726. white-space: nowrap;
  1727. overflow: hidden;
  1728. margin: 15rpx 0;
  1729. }
  1730. .r_sliper {
  1731. padding: 0 20rpx;
  1732. }
  1733. .top_line {
  1734. width: 6rpx;
  1735. height: 22rpx;
  1736. background: #32467b;
  1737. margin-right: 10rpx;
  1738. }
  1739. .video_t2 {
  1740. font-size: 24rpx;
  1741. font-family: PingFang SC;
  1742. font-weight: 500;
  1743. color: #666666;
  1744. }
  1745. .video_play {
  1746. position: absolute;
  1747. width: 95rpx;
  1748. height: 95rpx;
  1749. top: 0;
  1750. left: 0;
  1751. right: 0;
  1752. bottom: 0;
  1753. margin: auto;
  1754. }
  1755. .video_box {
  1756. position: relative;
  1757. }
  1758. .slot-content {
  1759. padding: 0 20rpx;
  1760. }
  1761. .notice_modal {
  1762. .content {
  1763. width: 100%;
  1764. height: 100%;
  1765. padding: 56rpx 56rpx 56rpx 64rpx;
  1766. .title {
  1767. color: #222;
  1768. line-height: 40rpx;
  1769. font-size: 36rpx;
  1770. text-align: center;
  1771. font-weight: bold;
  1772. margin-bottom: 24rpx;
  1773. }
  1774. .text {
  1775. height: 340rpx;
  1776. line-height: 40rpx;
  1777. text-indent: 2em;
  1778. font-size: 32rpx;
  1779. color: #222;
  1780. }
  1781. .had_read {
  1782. width: 100%;
  1783. height: 88rpx;
  1784. line-height: 88rpx;
  1785. text-align: center;
  1786. background: #3577e8;
  1787. border-radius: 240rpx;
  1788. font-size: 32rpx;
  1789. font-weight: 500;
  1790. color: #fff;
  1791. margin-top: 20rpx;
  1792. &.gray {
  1793. background: #bbbec5;
  1794. }
  1795. }
  1796. }
  1797. }
  1798. </style>