CourseTree.vue 40 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295
  1. <template>
  2. <div id="courseTree">
  3. <el-empty description="暂无课程" v-if="courseList.length === 0"></el-empty>
  4. <ul v-else class="courseTree">
  5. <li v-for="(item1, index1) in courseDataList" :key="index1">
  6. <div
  7. class="menu_box"
  8. @click="openMenu(item1)"
  9. :class="isActive(item1) ? 'active' : ''"
  10. >
  11. <div class="left level1">
  12. <el-tag
  13. size="mini"
  14. effect="dark"
  15. :type="getTypeStyle(item1)"
  16. v-if="item1.type == 3 || item1.type == -1"
  17. >{{ changeName(item1) }}</el-tag
  18. >
  19. <span v-else>
  20. {{ changeName(item1) }}
  21. </span>
  22. </div>
  23. <div class="center">
  24. {{ item1.name }}
  25. <el-tag v-if="item1.type == 2 && item1.commonSign == 1" size="mini"
  26. >公共章</el-tag
  27. >
  28. <p v-if="liveShowTimeData(item1)" class="live_style">
  29. <span>{{
  30. $tools.timestampToTime(item1.liveStartTime, (isDay = false))
  31. }}</span>
  32. -
  33. <span>{{
  34. $tools.timestampToTime(item1.liveEndTime, (isDay = false))
  35. }}</span>
  36. </p>
  37. </div>
  38. <div class="right">
  39. <template v-if="item1.type == 3">
  40. <img
  41. v-if="isActive(item1)"
  42. src="@/assets/learing.gif"
  43. alt=""
  44. class="activeImg_style"
  45. />
  46. <div class="during" v-if="item1.sectionType !== 2">
  47. {{ $tools.secondToDate(item1.durationTime) }}
  48. </div>
  49. <template v-if="BackSTATUS(item1)['name']">
  50. <el-tag
  51. size="mini"
  52. effect="light"
  53. :type="BackSTATUS(item1)['style']"
  54. >{{ BackSTATUS(item1)["name"] }}</el-tag
  55. >
  56. </template>
  57. <el-tag
  58. size="mini"
  59. effect="dark"
  60. v-if="item1.examList.length > 0"
  61. @click.stop="doExam(item1.examList[0], 2)"
  62. >习题</el-tag
  63. >
  64. </template>
  65. <template v-else-if="item1.type == -1">
  66. <template v-if="BackExamStatus(item1)['name']">
  67. <el-tag
  68. size="mini"
  69. effect="light"
  70. :type="BackExamStatus(item1)['style']"
  71. >{{ BackExamStatus(item1)["name"] }}</el-tag
  72. >
  73. </template>
  74. </template>
  75. <el-tag
  76. v-else
  77. v-show="allSectionList.length > 0"
  78. size="mini"
  79. effect="light"
  80. :type="getStudyStatus(item1)['style']"
  81. >{{ getStudyStatus(item1)["name"] }}</el-tag
  82. >
  83. </div>
  84. </div>
  85. <ul
  86. v-if="item1.children && item1.children.length > 0 && item1.showStatus"
  87. >
  88. <li v-for="(item2, index2) in item1.children" :key="index2">
  89. <div
  90. class="menu_box"
  91. @click="openMenu(item2)"
  92. :class="isActive(item2) ? 'active' : ''"
  93. >
  94. <div class="left level2">
  95. <el-tag
  96. size="mini"
  97. effect="dark"
  98. :type="getTypeStyle(item2)"
  99. v-if="item2.type == 3 || item2.type == -1"
  100. >{{ changeName(item2) }}</el-tag
  101. >
  102. <span v-else>
  103. {{ changeName(item2) }}
  104. </span>
  105. </div>
  106. <div class="center">
  107. {{ item2.name }}
  108. <el-tag
  109. v-if="item2.type == 2 && item2.commonSign == 1"
  110. size="mini"
  111. >公共章</el-tag
  112. >
  113. <p v-if="liveShowTimeData(item2)" class="live_style">
  114. <span>{{
  115. $tools.timestampToTime(item2.liveStartTime, (isDay = false))
  116. }}</span>
  117. -
  118. <span>{{
  119. $tools.timestampToTime(item2.liveEndTime, (isDay = false))
  120. }}</span>
  121. </p>
  122. </div>
  123. <div class="right">
  124. <template v-if="item2.type == 3">
  125. <img
  126. v-if="isActive(item2)"
  127. src="@/assets/learing.gif"
  128. alt=""
  129. class="activeImg_style"
  130. />
  131. <div class="during" v-if="item2.sectionType !== 2">
  132. {{ $tools.secondToDate(item2.durationTime) }}
  133. </div>
  134. <template v-if="BackSTATUS(item2)['name']">
  135. <el-tag
  136. size="mini"
  137. effect="light"
  138. :type="BackSTATUS(item2)['style']"
  139. >{{ BackSTATUS(item2)["name"] }}</el-tag
  140. >
  141. </template>
  142. <el-tag
  143. size="mini"
  144. effect="dark"
  145. v-if="item2.examList.length > 0"
  146. @click.stop="doExam(item2.examList[0], 2)"
  147. >习题</el-tag
  148. >
  149. </template>
  150. <template v-else-if="item2.type == -1">
  151. <template v-if="BackExamStatus(item2)['name']">
  152. <el-tag
  153. size="mini"
  154. effect="light"
  155. :type="BackExamStatus(item2)['style']"
  156. >{{ BackExamStatus(item2)["name"] }}</el-tag
  157. >
  158. </template>
  159. </template>
  160. <el-tag
  161. v-else
  162. v-show="allSectionList.length > 0"
  163. size="mini"
  164. effect="light"
  165. :type="getStudyStatus(item2)['style']"
  166. >{{ getStudyStatus(item2)["name"] }}</el-tag
  167. >
  168. </div>
  169. </div>
  170. <ul
  171. v-if="
  172. item2.children && item2.children.length > 0 && item2.showStatus
  173. "
  174. >
  175. <li v-for="(item3, index3) in item2.children" :key="index3">
  176. <div
  177. class="menu_box"
  178. @click="openMenu(item3)"
  179. :class="isActive(item3) ? 'active' : ''"
  180. >
  181. <div class="left level3">
  182. <el-tag
  183. size="mini"
  184. effect="dark"
  185. :type="getTypeStyle(item3)"
  186. v-if="item3.type == 3 || item3.type == -1"
  187. >{{ changeName(item3) }}</el-tag
  188. >
  189. <span v-else>
  190. {{ changeName(item3) }}
  191. </span>
  192. </div>
  193. <div class="center">
  194. {{ item3.name }}
  195. <p v-if="liveShowTimeData(item3)" class="live_style">
  196. <span>{{
  197. $tools.timestampToTime(
  198. item3.liveStartTime,
  199. (isDay = false)
  200. )
  201. }}</span>
  202. -
  203. <span>{{
  204. $tools.timestampToTime(
  205. item3.liveEndTime,
  206. (isDay = false)
  207. )
  208. }}</span>
  209. </p>
  210. </div>
  211. <div class="right">
  212. <template v-if="item3.type == 3">
  213. <img
  214. v-if="isActive(item3)"
  215. src="@/assets/learing.gif"
  216. alt=""
  217. class="activeImg_style"
  218. />
  219. <div class="during" v-if="item3.sectionType !== 2">
  220. {{ $tools.secondToDate(item3.durationTime) }}
  221. </div>
  222. <template v-if="BackSTATUS(item3)['name']">
  223. <el-tag
  224. size="mini"
  225. effect="light"
  226. :type="BackSTATUS(item3)['style']"
  227. >{{ BackSTATUS(item3)["name"] }}</el-tag
  228. >
  229. </template>
  230. <el-tag
  231. size="mini"
  232. effect="dark"
  233. v-if="item3.examList.length > 0"
  234. @click.stop="doExam(item3.examList[0], 2)"
  235. >习题</el-tag
  236. >
  237. </template>
  238. <template v-else-if="item3.type == -1">
  239. <template v-if="BackExamStatus(item3)['name']">
  240. <el-tag
  241. size="mini"
  242. effect="light"
  243. :type="BackExamStatus(item3)['style']"
  244. >{{ BackExamStatus(item3)["name"] }}</el-tag
  245. >
  246. </template>
  247. </template>
  248. <el-tag
  249. v-else
  250. size="mini"
  251. effect="dark"
  252. v-show="allSectionList.length > 0"
  253. >{{ getStudyStatus(item3)["name"] }}</el-tag
  254. >
  255. </div>
  256. </div>
  257. </li>
  258. </ul>
  259. </li>
  260. </ul>
  261. </li>
  262. </ul>
  263. </div>
  264. </template>
  265. <script>
  266. export default {
  267. inject: ["getGoodsData", "getBusinessData"],
  268. props: {
  269. rebuild: {
  270. type: Number,
  271. default: () => {
  272. return 0;
  273. }
  274. },
  275. activeSection: {
  276. type: Object,
  277. default: () => {
  278. return {};
  279. } //当前节数据
  280. }
  281. },
  282. data() {
  283. return {
  284. courseList: [], //商品的课程列表
  285. courseDataList: [], //课程内容
  286. allSectionList: [], //商品下所有节和试卷
  287. repeatShowTips: true, //是否显示跳转提示
  288. nowTime: 0 //当前时间
  289. };
  290. },
  291. computed: {
  292. goodsData() {
  293. return this.getGoodsData();
  294. },
  295. businessData() {
  296. return this.getBusinessData();
  297. },
  298. changeName: function() {
  299. return function(item) {
  300. var str = "";
  301. if (item.type == 3) {
  302. switch (item.sectionType) {
  303. case 1:
  304. str = "视频";
  305. break;
  306. case 2:
  307. str = "直播";
  308. break;
  309. case 3:
  310. str = "回放";
  311. break;
  312. default:
  313. break;
  314. }
  315. } else if (item.type == -1) {
  316. if (item.doType == 1) {
  317. str = "练习";
  318. } else {
  319. str = "考试";
  320. }
  321. } else {
  322. if (item.showStatus) {
  323. str = "▼";
  324. } else {
  325. str = "▶";
  326. }
  327. }
  328. return str;
  329. };
  330. },
  331. getTypeStyle: function() {
  332. return function(item) {
  333. if (item.type == 3) {
  334. if (item.sectionType == 2) {
  335. return "danger";
  336. }
  337. if (item.sectionType == 3) {
  338. return "warning";
  339. }
  340. } else {
  341. return "";
  342. }
  343. };
  344. },
  345. BackSTATUS: function() {
  346. return function(item) {
  347. if (item.sectionType == 1) {
  348. if (item.rebuild > 0) {
  349. return {
  350. name: "待重修",
  351. style: "danger"
  352. };
  353. } else if (item.learning == 1) {
  354. return {
  355. name: "已学完",
  356. style: "success"
  357. };
  358. }
  359. }
  360. if (item.sectionType == 2) {
  361. if (item.liveStartTime > this.nowTime) {
  362. console.log("asdasdasd");
  363. return {
  364. name: "待开播",
  365. style: "warning"
  366. };
  367. } else if (
  368. item.liveStartTime <= this.nowTime &&
  369. item.liveEndTime > this.nowTime
  370. ) {
  371. return {
  372. name: "直播中",
  373. style: "success"
  374. };
  375. } else if (item.liveEndTime < this.nowTime) {
  376. return {
  377. name: "已结束",
  378. style: "danger"
  379. };
  380. }
  381. }
  382. return {};
  383. };
  384. },
  385. BackExamStatus: function() {
  386. return function(item) {
  387. console.log(item, "item");
  388. if (item.rebuild > 0) {
  389. return {
  390. name: "待重测",
  391. style: "danger"
  392. };
  393. } else if (item.learning == 1) {
  394. return {
  395. name: "合格",
  396. style: "success"
  397. };
  398. } else if (item.learning == 0) {
  399. return {
  400. name: "不及格(需重考)",
  401. style: "danger"
  402. };
  403. }
  404. return {};
  405. };
  406. },
  407. isActive: function() {
  408. return function(item) {
  409. return (
  410. item.courseId == this.activeSection.courseId &&
  411. item.moduleId == this.activeSection.moduleId &&
  412. item.chapterId == this.activeSection.chapterId &&
  413. item.sectionId == this.activeSection.sectionId
  414. );
  415. };
  416. },
  417. getStudyStatus: function() {
  418. return function(item) {
  419. if (item.type == 1) {
  420. var STATUSARRAY = this.allSectionList.filter(i => {
  421. return i.moduleId == item.moduleId;
  422. });
  423. if (
  424. STATUSARRAY.findIndex(
  425. i =>
  426. i.moduleId == this.activeSection.moduleId &&
  427. i.chapterId == this.activeSection.chapterId &&
  428. i.sectionId == this.activeSection.sectionId &&
  429. i.studyStatus != 1
  430. ) !== -1
  431. ) {
  432. return {
  433. name: "学习中",
  434. style: "warning"
  435. };
  436. }
  437. }
  438. if (item.type == 2) {
  439. var STATUSARRAY = this.allSectionList.filter(i => {
  440. return i.moduleId == item.moduleId && i.chapterId == item.chapterId;
  441. });
  442. if (
  443. STATUSARRAY.findIndex(
  444. i =>
  445. i.moduleId == this.activeSection.moduleId &&
  446. i.chapterId == this.activeSection.chapterId &&
  447. i.sectionId == this.activeSection.sectionId &&
  448. i.studyStatus != 1
  449. ) !== -1
  450. ) {
  451. return {
  452. name: "学习中",
  453. style: "warning"
  454. };
  455. }
  456. }
  457. if (STATUSARRAY.every(i => i.studyStatus == 1)) {
  458. return {
  459. name: "已学完",
  460. style: "success"
  461. };
  462. } else {
  463. return {
  464. name: "待学习",
  465. style: ""
  466. };
  467. }
  468. };
  469. }
  470. },
  471. created() {
  472. this.init();
  473. this.nowTime = Number(new Date().getTime() / 1000).toFixed(0);
  474. },
  475. mounted() {
  476. this.$bus.$on("BackVideoFunc", () => {
  477. this.BackVideoFunc(); //已学完,重新定位
  478. });
  479. },
  480. methods: {
  481. // 新增用户视频学习日志
  482. studyLog(item, studyItem) {
  483. this.$axios({
  484. url: "/user/study/log",
  485. method: "post",
  486. data: {
  487. goodsId: this.goodsData.goodsId,
  488. orderGoodsId: this.goodsData.orderGoodsId,
  489. courseId: this.courseList[0].courseId,
  490. fromPlat: 2, //来源平台 1小程序 2PC网站
  491. goodsType: this.goodsData.goodsType // 商品类型 1视频2题库 3补考 4前培 5虚拟赠送题库 6直播
  492. }
  493. }).then(res => {
  494. console.log("直播的用户学习日志:", res);
  495. });
  496. },
  497. //是否显示直播时间范围
  498. liveShowTimeData(item) {
  499. if (item.type == 3 && item.sectionType == 2) {
  500. if (
  501. item.liveStartTime &&
  502. item.liveEndTime &&
  503. item.liveStartTime > this.nowTime
  504. ) {
  505. return true;
  506. } else {
  507. return false;
  508. }
  509. } else {
  510. return false;
  511. }
  512. },
  513. async init() {
  514. await this.getGoodsCourseList(); //获取商品课程列表
  515. await this.getCourseData(this.courseList[0].courseId); //获取课程内容
  516. await this.getAllSectionList(); //获取所有节列表
  517. this.studyLog(); // 新增用户视频学习日志
  518. /**播放逻辑 */
  519. if (!(this.rebuild > 0)) {
  520. this.playBackLogic();
  521. }
  522. },
  523. //获取商品课程列表
  524. getGoodsCourseList() {
  525. return new Promise((resolve, reject) => {
  526. this.$request
  527. .courseCourseList({
  528. orderGoodsId: this.goodsData.orderGoodsId,
  529. goodsId: this.goodsData.goodsId,
  530. gradeId: this.goodsData.gradeId
  531. })
  532. .then(res => {
  533. if (res.rows && res.rows.length > 0) {
  534. this.courseList = res.rows;
  535. resolve();
  536. } else {
  537. reject();
  538. }
  539. })
  540. .catch(() => {
  541. reject();
  542. });
  543. });
  544. },
  545. //获取课程内容
  546. getCourseData(id) {
  547. return new Promise(async (resolve, reject) => {
  548. const examRes = await this.$request.reSectionExamList({
  549. chapterId: 0,
  550. courseId: id,
  551. gradeId: this.goodsData.gradeId,
  552. orderGoodsId: this.goodsData.orderGoodsId
  553. }); //获取节关联练习试卷
  554. this.$request
  555. .reMenuList({
  556. courseId: id,
  557. gradeId: this.goodsData.gradeId,
  558. orderGoodsId: this.goodsData.orderGoodsId,
  559. rebuild: this.rebuild
  560. })
  561. .then(res => {
  562. this.courseDataList = res.rows.map(i => {
  563. return {
  564. level: 1,
  565. type: i.type,
  566. name: i.menuName,
  567. courseId: i.courseId,
  568. commonSign: i.commonSign || null,
  569. moduleId: i.type == 1 ? i.menuId : 0,
  570. chapterId: i.type == 2 ? i.menuId : 0,
  571. sectionId: i.type == 3 ? i.menuId : 0,
  572. sectionType: i.type == 3 ? i.sectionType : null,
  573. durationTime: i.type == 3 ? i.durationTime : null,
  574. recordingUrl: i.type == 3 ? i.recordingUrl : null,
  575. liveUrl: i.type == 3 ? i.liveUrl : null,
  576. liveStartTime: i.sectionType == 2 ? i.liveStartTime : null,
  577. liveEndTime: i.sectionType == 2 ? i.liveEndTime : null,
  578. showStatus: false, //展开状态
  579. children: null, //子列表
  580. learning: i.type == 3 ? i.learning : null,
  581. examList:
  582. i.type == 3
  583. ? examRes.data.filter(item => item.sectionId == i.menuId)
  584. : [] //关联试卷
  585. };
  586. });
  587. resolve();
  588. });
  589. });
  590. },
  591. //展开模块
  592. openModule(item, status = false, renewal = false) {
  593. if (item.children && item.children.length > 0) {
  594. item.showStatus = status ? true : !item.showStatus;
  595. }
  596. if (!(item.children && item.children.length > 0) || renewal) {
  597. return new Promise(resolve => {
  598. this.$request
  599. .reChapterList({
  600. moduleId: item.moduleId,
  601. gradeId: this.goodsData.gradeId,
  602. courseId: this.courseList[0].courseId,
  603. orderGoodsId: this.goodsData.orderGoodsId,
  604. rebuild: this.rebuild
  605. })
  606. .then(res => {
  607. this.$set(item, "showStatus", true);
  608. this.$set(
  609. item,
  610. "children",
  611. res.data.map(i => {
  612. if (i.id) {
  613. return {
  614. level: item.level + 1,
  615. type: 2,
  616. name: i.name,
  617. commonSign: i.commonSign || null,
  618. courseId: i.courseId,
  619. moduleId: i.moduleId,
  620. chapterId: i.chapterId,
  621. sectionId: 0,
  622. sectionType: null,
  623. durationTime: null,
  624. recordingUrl: null,
  625. showStatus: false, //展开状态
  626. children: null, //子列表
  627. examList: [] //关联试卷
  628. };
  629. } else {
  630. return Object.assign(i, {
  631. type: -1,
  632. chapterId: 0,
  633. examType: 3
  634. }); //examType:1章卷,2节卷,3模块卷
  635. }
  636. })
  637. );
  638. resolve(item.children);
  639. });
  640. });
  641. }
  642. },
  643. //展开章
  644. /**
  645. * status = true ? '强制展开'
  646. * renewal = true ? '重新获取数据'
  647. */
  648. openChapter(item, status = false, renewal = false) {
  649. if (item.children && item.children.length > 0) {
  650. item.showStatus = item.showStatus = status ? true : !item.showStatus;
  651. }
  652. if (!(item.children && item.children.length > 0) || renewal) {
  653. return new Promise(async resolve => {
  654. const examRes = await this.$request.reSectionExamList({
  655. moduleId: item.moduleId,
  656. chapterId: item.chapterId,
  657. courseId: item.courseId,
  658. gradeId: this.goodsData.gradeId,
  659. orderGoodsId: this.goodsData.orderGoodsId
  660. }); //获取节关联练习试卷
  661. this.$request
  662. .reSectionList({
  663. chapterId: item.chapterId,
  664. gradeId: this.goodsData.gradeId,
  665. courseId: item.courseId,
  666. rebuild: this.rebuild,
  667. moduleId: item.moduleId,
  668. orderGoodsId: this.goodsData.orderGoodsId
  669. })
  670. .then(res => {
  671. this.$set(item, "showStatus", true);
  672. this.$set(
  673. item,
  674. "children",
  675. res.data.map(i => {
  676. if (i.id) {
  677. return {
  678. level: item.level + 1,
  679. type: 3,
  680. name: i.name,
  681. courseId: item.courseId,
  682. moduleId: i.moduleId,
  683. chapterId: i.chapterId,
  684. sectionId: i.sectionId,
  685. sectionType: i.sectionType,
  686. durationTime: i.durationTime,
  687. recordingUrl: i.recordingUrl,
  688. liveUrl: i.liveUrl,
  689. liveStartTime:
  690. i.sectionType == 2 ? i.liveStartTime : null,
  691. liveEndTime: i.sectionType == 2 ? i.liveEndTime : null,
  692. learning: i.learning,
  693. rebuild: i.rebuild,
  694. showStatus: false, //展开状态
  695. children: null, //子列表
  696. examList:
  697. examRes.data.filter(
  698. item => item.sectionId == i.sectionId
  699. ) || [] //关联试卷
  700. };
  701. } else {
  702. return Object.assign(i, { type: -1, examType: 1 }); //examType:1章卷,2节卷,3模块卷
  703. }
  704. })
  705. );
  706. resolve();
  707. });
  708. });
  709. }
  710. },
  711. //获取所有节列表
  712. getAllSectionList() {
  713. return new Promise((resolve, reject) => {
  714. this.$request
  715. .studyrecordgoodsAllListWithExam({
  716. gradeId: this.goodsData.gradeId,
  717. goodsId: this.goodsData.goodsId,
  718. courseId: this.courseList[0].courseId,
  719. orderGoodsId: this.goodsData.orderGoodsId,
  720. rebuild: this.rebuild
  721. })
  722. .then(res => {
  723. let ary = res.data.map(i => {
  724. if (i.type === 3) {
  725. return {
  726. type: 3,
  727. name: i.sectionName,
  728. courseId: i.courseId,
  729. moduleId: i.moduleId,
  730. chapterId: i.chapterId,
  731. sectionId: i.sectionId,
  732. sectionType: i.sectionType,
  733. recordingUrl: i.recordingUrl,
  734. studyStatus: i.studyStatus,
  735. learning: i.studyStatus
  736. };
  737. } else {
  738. return Object.assign(i, {
  739. type: -1,
  740. typeId: i.examId,
  741. chapterId: i.chapterId || 0
  742. });
  743. }
  744. });
  745. this.allSectionList = ary.filter(i => i.sectionType != 2);
  746. resolve();
  747. })
  748. .catch(err => {
  749. if (err.code == 601) {
  750. //资料审核不通过,请前往重新填写
  751. this.$confirm(err.msg, "提示", {
  752. confirmButtonText: "确定",
  753. closeOnClickModal: false,
  754. closeOnPressEscape: false,
  755. distinguishCancelAndClose: false,
  756. showClose: false,
  757. showCancelButton: false
  758. })
  759. .then(_ => {
  760. //停止执行-退出页面
  761. this.$router.back(-1);
  762. })
  763. .catch(_ => {
  764. //停止执行-退出页面
  765. this.$router.back(-1);
  766. });
  767. }
  768. });
  769. });
  770. },
  771. //点击菜单
  772. async openMenu(item) {
  773. if (item.type == 1) {
  774. await this.openModule(item); //展开模块
  775. } else if (item.type == 2) {
  776. await this.openChapter(item); //展开章
  777. } else {
  778. try {
  779. await this.waitCheckStatus(item); //检查是否可以继续执行
  780. if (item.type == 3) {
  781. if (item.sectionType == 1) {
  782. this.watchSection(item);
  783. } else {
  784. this.watchJumpSection(item);
  785. }
  786. }
  787. if (item.type == -1) {
  788. this.doExam(item);
  789. }
  790. } catch (error) {
  791. console.log(error, "error");
  792. }
  793. }
  794. },
  795. //检查是否可以继续执行
  796. waitCheckStatus(item) {
  797. return new Promise(async (resolve, reject) => {
  798. if (
  799. item.type == 3 &&
  800. ((item.sectionType == 1 && !item.recordingUrl) ||
  801. (item.sectionType == 2 && !item.liveUrl) ||
  802. (item.sectionType == 3 && !item.recordingUrl))
  803. ) {
  804. this.$message({
  805. type: "warning",
  806. message: `暂无播放地址数据`
  807. });
  808. return reject();
  809. }
  810. if (item.type == -1 && item.doType != 1 && item.learning == 1) {
  811. this.$message.warning("考试已通过,请勿重复考试");
  812. return reject();
  813. }
  814. if (!this.orderTopTobottom(item)) {
  815. this.$message({
  816. type: "warning",
  817. message:
  818. item.type == -1
  819. ? "请学完视频课程再进行练习和测试"
  820. : "请按顺序学习视频课程"
  821. });
  822. return reject();
  823. }
  824. // 检查学习次数
  825. if (!(await this.exceedLearnNum(item))) {
  826. return reject();
  827. }
  828. resolve();
  829. });
  830. },
  831. //播放视频节
  832. watchSection(item) {
  833. if (this.isActive(item)) return;
  834. this.$emit("update:activeSection", item);
  835. setTimeout(() => {
  836. this.$bus.$emit("toPlay", item);
  837. }, 100);
  838. let query = {
  839. gradeId: this.goodsData.gradeId,
  840. orderGoodsId: this.goodsData.orderGoodsId,
  841. courseId: item.courseId,
  842. moduleId: item.moduleId || 0,
  843. chapterId: item.chapterId || 0,
  844. sectionId: item.sectionId || 0
  845. };
  846. this.$router.replace({ path: this.$route.path, query });
  847. },
  848. //回放或直播
  849. async watchJumpSection(item) {
  850. if (item.sectionType == 2) {
  851. let data = await this.studyRecordGetChannelBasicInfo(item.liveUrl);
  852. if (data.watchStatus == "end" || data.watchStatus == "playback") {
  853. this.$message({
  854. type: "warning",
  855. message: `直播已结束`
  856. });
  857. return;
  858. }
  859. if (data.watchStatus == "waiting") {
  860. this.$message({
  861. type: "warning",
  862. message: `直播未开始`
  863. });
  864. return;
  865. }
  866. }
  867. this.$confirm(
  868. `确定前往观看${
  869. item.sectionType == 2 ? " [直播] " : " [回放] "
  870. }${item.sectionName || item.name}?`,
  871. "提示",
  872. {
  873. confirmButtonText: "确定",
  874. cancelButtonText: "取消",
  875. type: "warning"
  876. }
  877. )
  878. .then(() => {
  879. var query = {
  880. goodsName: this.goodsData.name,
  881. goodsId: this.goodsData.goodsId,
  882. gradeId: this.goodsData.gradeId,
  883. orderGoodsId: this.goodsData.orderGoodsId,
  884. courseId: item.courseId,
  885. sectionId: item.sectionId,
  886. chapterId: item.chapterId,
  887. moduleId: item.moduleId,
  888. sectionType: item.sectionType,
  889. vid: item.recordingUrl // 回放vid
  890. };
  891. this.$router.push({
  892. path: "/living-room/" + item.liveUrl,
  893. query
  894. });
  895. })
  896. .catch(() => {});
  897. },
  898. //判断跳转试卷
  899. async doExam(item, type) {
  900. if (this.repeatShowTips) {
  901. this.$confirm("是否跳转做题页面?", "提示", {
  902. confirmButtonText: "确定",
  903. cancelButtonText: "取消",
  904. type: "warning"
  905. })
  906. .then(() => {
  907. this.jumpExam(item, type);
  908. })
  909. .catch(() => {});
  910. } else {
  911. this.repeatShowTips = true;
  912. this.jumpExam(item, type);
  913. }
  914. },
  915. //跳转试卷页面
  916. jumpExam(item, type) {
  917. this.$router.push({
  918. path: "/course-exam/" + this.goodsData.goodsId,
  919. query: {
  920. orderGoodsId: this.goodsData.orderGoodsId,
  921. gradeId: this.goodsData.gradeId,
  922. courseId: this.courseList[0].courseId,
  923. moduleId: item.moduleId || 0,
  924. chapterId: item.chapterId || 0,
  925. sectionId: item.sectionId || 0,
  926. examId: item.typeId,
  927. learning: item.learning,
  928. type: type || item.examType,
  929. nextStatus: "next" //是否继续播放课程
  930. }
  931. });
  932. },
  933. //查看直播状态
  934. studyRecordGetChannelBasicInfo(channelId) {
  935. return new Promise(resolve => {
  936. this.$request
  937. .studyRecordGetChannelBasicInfo({
  938. channelId
  939. })
  940. .then(res => {
  941. resolve(res.data);
  942. })
  943. .catch(err => {
  944. this.$message.error(err.msg);
  945. });
  946. });
  947. },
  948. //限制播放顺序
  949. orderTopTobottom(item) {
  950. if (this.businessData.goodsLearningOrder != 2 || item.sectionType == 2) {
  951. return true;
  952. }
  953. const Findex = this.allSectionList.findIndex(i => {
  954. if (item.type == 3) {
  955. return (
  956. i.type == item.type &&
  957. i.moduleId == item.moduleId &&
  958. i.chapterId == item.chapterId &&
  959. i.sectionId == item.sectionId
  960. );
  961. } else {
  962. return (
  963. i.type == item.type &&
  964. i.typeId == item.typeId &&
  965. i.moduleId == item.moduleId &&
  966. i.chapterId == item.chapterId
  967. );
  968. }
  969. });
  970. if (Findex == -1) {
  971. return true;
  972. } else {
  973. const Ary = this.allSectionList.slice(0, Findex);
  974. if (Ary.length == 0) {
  975. return true;
  976. } else {
  977. return Ary.every(i => i.studyStatus == 1 && !(i.rebuild > 0));
  978. }
  979. }
  980. },
  981. // 检查学习次数
  982. async exceedLearnNum(section) {
  983. let learnNum = await this.goodsTodayStudySectionNum();
  984. let hasLearn = await this.gradeCheckGoodsStudy(section);
  985. if (
  986. this.goodsData.sectionMaxNum > 0 &&
  987. learnNum >= this.goodsData.sectionMaxNum &&
  988. !hasLearn
  989. ) {
  990. this.$message({
  991. type: "warning",
  992. message: `每天最多学习${this.goodsData.sectionMaxNum}节`
  993. });
  994. return false;
  995. }
  996. return true;
  997. },
  998. goodsTodayStudySectionNum() {
  999. return new Promise(resolve => {
  1000. this.$request
  1001. .goodsTodayStudySectionNum({
  1002. goodsId: this.goodsData.goodsId,
  1003. orderGoodsId: this.goodsData.orderGoodsId,
  1004. gradeId: this.goodsData.gradeId
  1005. })
  1006. .then(res => {
  1007. resolve(res.data);
  1008. });
  1009. });
  1010. },
  1011. gradeCheckGoodsStudy(option) {
  1012. return new Promise(resolve => {
  1013. let data = {
  1014. type: option.type == -1 ? 2 : 1,
  1015. goodsId: this.goodsData.goodsId,
  1016. gradeId: this.goodsData.gradeId,
  1017. orderGoodsId: this.goodsData.orderGoodsId,
  1018. moduleId: option.moduleId || 0,
  1019. chapterId: option.chapterId || 0
  1020. };
  1021. if (option.type == -1) {
  1022. data.examId = option.typeId;
  1023. } else {
  1024. data.sectionId = option.sectionId;
  1025. }
  1026. this.$request.gradeCheckGoodsStudy(data).then(res => {
  1027. resolve(res.data);
  1028. });
  1029. });
  1030. },
  1031. bankRecordDoNum(section) {
  1032. return new Promise(resolve => {
  1033. this.$request
  1034. .bankRecordDoNum({
  1035. goodsId: this.goodsData.goodsId,
  1036. orderGoodsId: this.goodsData.orderGoodsId,
  1037. gradeId: this.goodsData.gradeId,
  1038. courseId: this.courseList[0].courseId,
  1039. moduleId: 0,
  1040. chapterId: section.chapterId,
  1041. examId: section.typeId
  1042. })
  1043. .then(res => {
  1044. resolve(res.data);
  1045. });
  1046. });
  1047. },
  1048. //播放逻辑
  1049. async playBackLogic() {
  1050. const { courseId, moduleId, chapterId, sectionId } = this.$route.query;
  1051. if (courseId >= 0 && moduleId >= 0 && chapterId >= 0 && sectionId >= 0) {
  1052. let data = this.allSectionList.find(
  1053. e =>
  1054. e.courseId == courseId &&
  1055. e.moduleId == moduleId &&
  1056. e.chapterId == chapterId &&
  1057. e.sectionId == sectionId
  1058. );
  1059. this.openMenu(data);
  1060. this.unfoldFunc(data); //展开定位列表
  1061. } else {
  1062. this.$request
  1063. .studyRecordQueryLiveLast({
  1064. gradeId: this.goodsData.gradeId,
  1065. orderGoodsId: this.goodsData.orderGoodsId,
  1066. courseId: this.courseList[0].courseId
  1067. })
  1068. .then(res => {
  1069. let data = res.data;
  1070. if (!data.sectionId) {
  1071. data = this.allSectionList[0];
  1072. } else {
  1073. data.type = 3;
  1074. }
  1075. if (
  1076. data.learning == 1 &&
  1077. this.businessData.goodsLearningOrder == 2
  1078. ) {
  1079. let next = this.allSectionList.find(e => e.studyStatus != 1);
  1080. next && (data = next);
  1081. }
  1082. this.openMenu(data);
  1083. this.unfoldFunc(data); //展开定位列表
  1084. });
  1085. }
  1086. },
  1087. //展开定位列表
  1088. unfoldFunc(item, status = false, Renewal = false) {
  1089. return new Promise(async resolve => {
  1090. var scrollTopFunc = () => {
  1091. var scrollTop = document.getElementsByClassName("activeImg_style")[0]
  1092. .offsetTop;
  1093. document.getElementsByClassName(
  1094. "courseTree"
  1095. )[0].scrollTop = scrollTop;
  1096. }; //滚动到当前播放位置
  1097. if (item.moduleId) {
  1098. let moduleChildren = await this.openModule(
  1099. this.courseDataList.find(i => i.moduleId == item.moduleId),
  1100. status,
  1101. Renewal
  1102. );
  1103. if (item.chapterId) {
  1104. await this.openChapter(
  1105. moduleChildren.find(i => i.chapterId == item.chapterId),
  1106. status,
  1107. Renewal
  1108. );
  1109. scrollTopFunc();
  1110. }
  1111. }
  1112. if (!item.moduleId && item.chapterId) {
  1113. await this.openChapter(
  1114. this.courseDataList.find(i => i.chapterId == item.chapterId),
  1115. status,
  1116. Renewal
  1117. );
  1118. scrollTopFunc();
  1119. }
  1120. resolve();
  1121. });
  1122. },
  1123. //已学完,重新定位
  1124. async BackVideoFunc() {
  1125. let hasNoStudyStatus = this.allSectionList.find(i => i.studyStatus == -1); //更新前是否存在未学完的课程
  1126. await this.getAllSectionList(); //获取所有节列表
  1127. await this.unfoldFunc(this.activeSection, true, true);
  1128. if (this.businessData.goodsLearningOrder == 2) {
  1129. var ary = this.allSectionList.find(i => i.studyStatus != 1);
  1130. } else {
  1131. var ary = null;
  1132. let index = this.allSectionList.findIndex(
  1133. i =>
  1134. i.courseId == this.activeSection.courseId &&
  1135. i.moduleId == this.activeSection.moduleId &&
  1136. i.chapterId == this.activeSection.chapterId &&
  1137. i.sectionId == this.activeSection.sectionId
  1138. );
  1139. this.$emit(
  1140. "update:activeSection",
  1141. Object.assign({}, this.allSectionList[index])
  1142. ); //重新赋值当前节数据
  1143. ary = this.allSectionList[index + 1];
  1144. if (ary && ary.studyStatus == 1) {
  1145. ary = null;
  1146. hasNoStudyStatus = null;
  1147. }
  1148. }
  1149. if (ary) {
  1150. if (ary.type != 3) {
  1151. this.$confirm(
  1152. "本章视频已学习完成,继续进行下一个考试?",
  1153. "温馨提示",
  1154. {
  1155. confirmButtonText: "确定",
  1156. cancelButtonText: "取消",
  1157. type: "warning"
  1158. }
  1159. )
  1160. .then(() => {
  1161. this.repeatShowTips = false; //是否显示跳转提示
  1162. this.openMenu(ary);
  1163. this.unfoldFunc(ary, true, true);
  1164. })
  1165. .catch(() => {});
  1166. } else {
  1167. this.$confirm("当前视频已学完,继续学习下一个视频?", "温馨提示", {
  1168. confirmButtonText: "确定",
  1169. cancelButtonText: "取消",
  1170. type: "warning"
  1171. })
  1172. .then(() => {
  1173. this.openMenu(ary);
  1174. this.unfoldFunc(ary, true, true);
  1175. })
  1176. .catch(() => {});
  1177. }
  1178. } else {
  1179. if (hasNoStudyStatus) {
  1180. this.$alert(
  1181. "恭喜您课程学习全部完成,教务会在1-3个工作日内完成学习初审,请耐心等待。",
  1182. "温馨提示",
  1183. {
  1184. confirmButtonText: "确定",
  1185. showClose: false,
  1186. callback: action => {
  1187. if (action == "confirm") {
  1188. this.jumpPage();
  1189. }
  1190. }
  1191. }
  1192. );
  1193. }
  1194. }
  1195. },
  1196. //回到个人中心
  1197. jumpPage() {
  1198. this.$router.replace({
  1199. path: "/person-center/my-course"
  1200. });
  1201. }
  1202. },
  1203. beforeDestroy() {
  1204. this.$bus.$off("BackVideoFunc");
  1205. }
  1206. };
  1207. </script>
  1208. <style lang="scss" scoped>
  1209. .active {
  1210. background-color: rgb(107, 107, 107) !important;
  1211. }
  1212. .activeImg_style {
  1213. width: 20px;
  1214. height: 30px;
  1215. }
  1216. .courseTree {
  1217. height: 416px;
  1218. overflow: auto;
  1219. user-select: none;
  1220. &::-webkit-scrollbar {
  1221. width: 5px;
  1222. height: 9px;
  1223. }
  1224. /*定义滚动条轨道 内阴影+圆角*/
  1225. &::-webkit-scrollbar-track {
  1226. background-color: inherit;
  1227. border: none;
  1228. border-radius: 10px;
  1229. }
  1230. /*定义滑块 内阴影+圆角*/
  1231. &::-webkit-scrollbar-thumb {
  1232. border-radius: 10px;
  1233. position: relative;
  1234. right: 2px;
  1235. background-color: #c6c6cd;
  1236. width: 6px;
  1237. }
  1238. }
  1239. .level1 {
  1240. padding-left: 0px;
  1241. }
  1242. .level2 {
  1243. padding-left: 10px;
  1244. }
  1245. .level3 {
  1246. padding-left: 20px;
  1247. }
  1248. .menu_box {
  1249. margin-top: 4px;
  1250. padding: 0px 10px;
  1251. display: flex;
  1252. align-items: center;
  1253. min-height: 30px;
  1254. max-height: 60px;
  1255. color: #fff;
  1256. cursor: pointer;
  1257. &:hover {
  1258. background-color: rgb(81, 81, 81);
  1259. }
  1260. & > .left {
  1261. flex-shrink: 0;
  1262. padding-right: 10px;
  1263. }
  1264. & > .center {
  1265. flex: 1;
  1266. word-break: break-all;
  1267. text-overflow: ellipsis;
  1268. overflow: hidden;
  1269. display: -webkit-box;
  1270. -webkit-line-clamp: 2;
  1271. -webkit-box-orient: vertical;
  1272. }
  1273. & > .right {
  1274. & > .during {
  1275. margin-right: 4px;
  1276. }
  1277. display: flex;
  1278. align-items: center;
  1279. // width: 80px;
  1280. flex-shrink: 0;
  1281. }
  1282. }
  1283. .live_style {
  1284. & > span {
  1285. font-size: 12px;
  1286. }
  1287. }
  1288. </style>