CourseTree.vue 40 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313
  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. return {
  363. name: "待开播",
  364. style: "warning"
  365. };
  366. } else if (
  367. item.liveStartTime <= this.nowTime &&
  368. item.liveEndTime > this.nowTime
  369. ) {
  370. return {
  371. name: "直播中",
  372. style: "success"
  373. };
  374. } else if (item.liveEndTime < this.nowTime) {
  375. return {
  376. name: "已结束",
  377. style: "danger"
  378. };
  379. }
  380. }
  381. return {};
  382. };
  383. },
  384. BackExamStatus: function() {
  385. return function(item) {
  386. if (item.rebuild > 0) {
  387. return {
  388. name: "待重测",
  389. style: "danger"
  390. };
  391. } else if (item.learning == 1) {
  392. return {
  393. name: "合格",
  394. style: "success"
  395. };
  396. } else if (item.learning == 0) {
  397. return {
  398. name: "不及格(需重考)",
  399. style: "danger"
  400. };
  401. }
  402. return {};
  403. };
  404. },
  405. isActive: function() {
  406. return function(item) {
  407. return (
  408. item.courseId == this.activeSection.courseId &&
  409. item.moduleId == this.activeSection.moduleId &&
  410. item.chapterId == this.activeSection.chapterId &&
  411. item.sectionId == this.activeSection.sectionId
  412. );
  413. };
  414. },
  415. getStudyStatus: function() {
  416. return function(item) {
  417. if (item.type == 1) {
  418. var STATUSARRAY = this.allSectionList.filter(i => {
  419. return i.moduleId == item.moduleId;
  420. });
  421. if (
  422. STATUSARRAY.findIndex(
  423. i =>
  424. i.moduleId == this.activeSection.moduleId &&
  425. i.chapterId == this.activeSection.chapterId &&
  426. i.sectionId == this.activeSection.sectionId &&
  427. i.studyStatus != 1
  428. ) !== -1
  429. ) {
  430. return {
  431. name: "学习中",
  432. style: "warning"
  433. };
  434. }
  435. }
  436. if (item.type == 2) {
  437. var STATUSARRAY = this.allSectionList.filter(i => {
  438. return i.moduleId == item.moduleId && i.chapterId == item.chapterId;
  439. });
  440. if (
  441. STATUSARRAY.findIndex(
  442. i =>
  443. i.moduleId == this.activeSection.moduleId &&
  444. i.chapterId == this.activeSection.chapterId &&
  445. i.sectionId == this.activeSection.sectionId &&
  446. i.studyStatus != 1
  447. ) !== -1
  448. ) {
  449. return {
  450. name: "学习中",
  451. style: "warning"
  452. };
  453. }
  454. }
  455. if (STATUSARRAY.every(i => i.studyStatus == 1)) {
  456. return {
  457. name: "已学完",
  458. style: "success"
  459. };
  460. } else {
  461. return {
  462. name: "待学习",
  463. style: ""
  464. };
  465. }
  466. };
  467. }
  468. },
  469. created() {
  470. this.init();
  471. this.nowTime = Number(new Date().getTime() / 1000).toFixed(0);
  472. },
  473. mounted() {
  474. this.$bus.$on("BackVideoFunc", () => {
  475. this.BackVideoFunc(); //已学完,重新定位
  476. });
  477. },
  478. methods: {
  479. // 新增用户视频学习日志
  480. studyLog(item, studyItem) {
  481. this.$axios({
  482. url: "/user/study/log",
  483. method: "post",
  484. data: {
  485. goodsId: this.goodsData.goodsId,
  486. orderGoodsId: this.goodsData.orderGoodsId,
  487. courseId: this.courseList[0].courseId,
  488. fromPlat: 2, //来源平台 1小程序 2PC网站
  489. goodsType: this.goodsData.goodsType // 商品类型 1视频2题库 3补考 4前培 5虚拟赠送题库 6直播
  490. }
  491. }).then(res => {
  492. });
  493. },
  494. //是否显示直播时间范围
  495. liveShowTimeData(item) {
  496. if (item.type == 3 && item.sectionType == 2) {
  497. if (
  498. item.liveStartTime &&
  499. item.liveEndTime &&
  500. item.liveStartTime > this.nowTime
  501. ) {
  502. return true;
  503. } else {
  504. return false;
  505. }
  506. } else {
  507. return false;
  508. }
  509. },
  510. async init() {
  511. await this.getGoodsCourseList(); //获取商品课程列表
  512. await this.getCourseData(this.courseList[0].courseId); //获取课程内容
  513. await this.getAllSectionList(); //获取所有节列表
  514. this.studyLog(); // 新增用户视频学习日志
  515. /**播放逻辑 */
  516. if (!(this.rebuild > 0)) {
  517. this.playBackLogic();
  518. }
  519. },
  520. //获取商品课程列表
  521. getGoodsCourseList() {
  522. return new Promise((resolve, reject) => {
  523. this.$request
  524. .courseCourseList({
  525. orderGoodsId: this.goodsData.orderGoodsId,
  526. goodsId: this.goodsData.goodsId,
  527. gradeId: this.goodsData.gradeId
  528. })
  529. .then(res => {
  530. if (res.rows && res.rows.length > 0) {
  531. this.courseList = res.rows;
  532. resolve();
  533. } else {
  534. reject();
  535. }
  536. })
  537. .catch(() => {
  538. reject();
  539. });
  540. });
  541. },
  542. //获取课程内容
  543. getCourseData(id) {
  544. return new Promise(async (resolve, reject) => {
  545. const examRes = await this.$request.reSectionExamList({
  546. chapterId: 0,
  547. courseId: id,
  548. gradeId: this.goodsData.gradeId,
  549. orderGoodsId: this.goodsData.orderGoodsId
  550. }); //获取节关联练习试卷
  551. this.$request
  552. .reMenuList({
  553. courseId: id,
  554. gradeId: this.goodsData.gradeId,
  555. orderGoodsId: this.goodsData.orderGoodsId,
  556. rebuild: this.rebuild
  557. })
  558. .then(res => {
  559. this.courseDataList = res.rows.map(i => {
  560. return {
  561. level: 1,
  562. type: i.type,
  563. name: i.menuName,
  564. courseId: i.courseId,
  565. commonSign: i.commonSign || null,
  566. moduleId: i.type == 1 ? i.menuId : 0,
  567. chapterId: i.type == 2 ? i.menuId : 0,
  568. sectionId: i.type == 3 ? i.menuId : 0,
  569. sectionType: i.type == 3 ? i.sectionType : null,
  570. durationTime: i.type == 3 ? i.durationTime : null,
  571. recordingUrl: i.type == 3 ? i.recordingUrl : null,
  572. liveUrl: i.type == 3 ? i.liveUrl : null,
  573. liveStartTime: i.sectionType == 2 ? i.liveStartTime : null,
  574. liveEndTime: i.sectionType == 2 ? i.liveEndTime : null,
  575. showStatus: false, //展开状态
  576. children: null, //子列表
  577. learning: i.type == 3 ? i.learning : null,
  578. examList:
  579. i.type == 3
  580. ? examRes.data.filter(item => item.sectionId == i.menuId)
  581. : [] //关联试卷
  582. };
  583. });
  584. resolve();
  585. });
  586. });
  587. },
  588. //展开模块
  589. openModule(item, status = false, renewal = false) {
  590. if (item.children && item.children.length > 0) {
  591. item.showStatus = status ? true : !item.showStatus;
  592. }
  593. if (!(item.children && item.children.length > 0) || renewal) {
  594. return new Promise(resolve => {
  595. this.$request
  596. .reChapterList({
  597. moduleId: item.moduleId,
  598. gradeId: this.goodsData.gradeId,
  599. courseId: this.courseList[0].courseId,
  600. orderGoodsId: this.goodsData.orderGoodsId,
  601. rebuild: this.rebuild
  602. })
  603. .then(res => {
  604. this.$set(item, "showStatus", true);
  605. this.$set(
  606. item,
  607. "children",
  608. res.data.map(i => {
  609. if (i.id) {
  610. return {
  611. level: item.level + 1,
  612. type: 2,
  613. name: i.name,
  614. commonSign: i.commonSign || null,
  615. courseId: i.courseId,
  616. moduleId: i.moduleId,
  617. chapterId: i.chapterId,
  618. sectionId: 0,
  619. sectionType: null,
  620. durationTime: null,
  621. recordingUrl: null,
  622. showStatus: false, //展开状态
  623. children: null, //子列表
  624. examList: [] //关联试卷
  625. };
  626. } else {
  627. return Object.assign(i, {
  628. type: -1,
  629. chapterId: 0,
  630. examType: 3
  631. }); //examType:1章卷,2节卷,3模块卷
  632. }
  633. })
  634. );
  635. resolve(item.children);
  636. });
  637. });
  638. }
  639. },
  640. //展开章
  641. /**
  642. * status = true ? '强制展开'
  643. * renewal = true ? '重新获取数据'
  644. */
  645. openChapter(item, status = false, renewal = false) {
  646. if (item.children && item.children.length > 0) {
  647. item.showStatus = item.showStatus = status ? true : !item.showStatus;
  648. }
  649. if (!(item.children && item.children.length > 0) || renewal) {
  650. return new Promise(async resolve => {
  651. const examRes = await this.$request.reSectionExamList({
  652. moduleId: item.moduleId,
  653. chapterId: item.chapterId,
  654. courseId: item.courseId,
  655. gradeId: this.goodsData.gradeId,
  656. orderGoodsId: this.goodsData.orderGoodsId
  657. }); //获取节关联练习试卷
  658. this.$request
  659. .reSectionList({
  660. chapterId: item.chapterId,
  661. gradeId: this.goodsData.gradeId,
  662. courseId: item.courseId,
  663. rebuild: this.rebuild,
  664. moduleId: item.moduleId,
  665. orderGoodsId: this.goodsData.orderGoodsId
  666. })
  667. .then(res => {
  668. this.$set(item, "showStatus", true);
  669. this.$set(
  670. item,
  671. "children",
  672. res.data.map(i => {
  673. if (i.id) {
  674. return {
  675. level: item.level + 1,
  676. type: 3,
  677. name: i.name,
  678. courseId: item.courseId,
  679. moduleId: i.moduleId,
  680. chapterId: i.chapterId,
  681. sectionId: i.sectionId,
  682. sectionType: i.sectionType,
  683. durationTime: i.durationTime,
  684. recordingUrl: i.recordingUrl,
  685. liveUrl: i.liveUrl,
  686. liveStartTime:
  687. i.sectionType == 2 ? i.liveStartTime : null,
  688. liveEndTime: i.sectionType == 2 ? i.liveEndTime : null,
  689. learning: i.learning,
  690. rebuild: i.rebuild,
  691. showStatus: false, //展开状态
  692. children: null, //子列表
  693. examList:
  694. examRes.data.filter(
  695. item => item.sectionId == i.sectionId
  696. ) || [] //关联试卷
  697. };
  698. } else {
  699. return Object.assign(i, { type: -1, examType: 1 }); //examType:1章卷,2节卷,3模块卷
  700. }
  701. })
  702. );
  703. resolve();
  704. });
  705. });
  706. }
  707. },
  708. //获取所有节列表
  709. getAllSectionList() {
  710. return new Promise((resolve, reject) => {
  711. this.$request
  712. .studyrecordgoodsAllListWithExam({
  713. gradeId: this.goodsData.gradeId,
  714. goodsId: this.goodsData.goodsId,
  715. courseId: this.courseList[0].courseId,
  716. orderGoodsId: this.goodsData.orderGoodsId,
  717. rebuild: this.rebuild
  718. })
  719. .then(res => {
  720. let ary = res.data.map(i => {
  721. if (i.type === 3) {
  722. return {
  723. type: 3,
  724. name: i.sectionName,
  725. courseId: i.courseId,
  726. moduleId: i.moduleId,
  727. chapterId: i.chapterId,
  728. sectionId: i.sectionId,
  729. sectionType: i.sectionType,
  730. recordingUrl: i.recordingUrl,
  731. studyStatus: i.studyStatus,
  732. learning: i.studyStatus
  733. };
  734. } else {
  735. return Object.assign(i, {
  736. type: -1,
  737. typeId: i.examId,
  738. chapterId: i.chapterId || 0
  739. });
  740. }
  741. });
  742. this.allSectionList = ary.filter(i => i.sectionType != 2);
  743. resolve();
  744. })
  745. .catch(err => {
  746. if (err.code == 601) {
  747. //资料审核不通过,请前往重新填写
  748. this.$confirm(err.msg, "提示", {
  749. confirmButtonText: "确定",
  750. closeOnClickModal: false,
  751. closeOnPressEscape: false,
  752. distinguishCancelAndClose: false,
  753. showClose: false,
  754. showCancelButton: false
  755. })
  756. .then(_ => {
  757. //停止执行-退出页面
  758. this.$router.back(-1);
  759. })
  760. .catch(_ => {
  761. //停止执行-退出页面
  762. this.$router.back(-1);
  763. });
  764. }
  765. });
  766. });
  767. },
  768. //点击菜单
  769. async openMenu(item) {
  770. if (item.type == 1) {
  771. await this.openModule(item); //展开模块
  772. } else if (item.type == 2) {
  773. await this.openChapter(item); //展开章
  774. } else {
  775. try {
  776. await this.waitCheckStatus(item); //检查是否可以继续执行
  777. if (item.type == 3) {
  778. if (item.sectionType == 1) {
  779. this.watchSection(item);
  780. } else {
  781. this.watchJumpSection(item);
  782. }
  783. }
  784. if (item.type == -1) {
  785. this.doExam(item);
  786. }
  787. } catch (error) {
  788. console.error("菜单展开逻辑报错:",error);
  789. }
  790. }
  791. },
  792. //检查是否可以继续执行
  793. waitCheckStatus(item) {
  794. return new Promise(async (resolve, reject) => {
  795. if (
  796. item.type == 3 &&
  797. ((item.sectionType == 1 && !item.recordingUrl) ||
  798. (item.sectionType == 2 && !item.liveUrl) ||
  799. (item.sectionType == 3 && !item.recordingUrl))
  800. ) {
  801. this.$message({
  802. type: "warning",
  803. message: `暂无播放地址数据`
  804. });
  805. return reject();
  806. }
  807. if (item.type == -1 && item.doType != 1 && item.learning == 1) {
  808. this.$message.warning("考试已通过,请勿重复考试");
  809. return reject();
  810. }
  811. if (!this.orderTopTobottom(item)) {
  812. this.$message({
  813. type: "warning",
  814. message:
  815. item.type == -1
  816. ? "请学完视频课程再进行练习和测试"
  817. : "请按顺序学习视频课程"
  818. });
  819. return reject();
  820. }
  821. // 检查学习次数
  822. if (!(await this.exceedLearnNum(item))) {
  823. return reject();
  824. }
  825. resolve();
  826. });
  827. },
  828. //播放视频节
  829. watchSection(item) {
  830. if (this.isActive(item)) return;
  831. this.$emit("update:activeSection", item);
  832. setTimeout(() => {
  833. this.$bus.$emit("toPlay", item);
  834. }, 100);
  835. let query = {
  836. gradeId: this.goodsData.gradeId,
  837. orderGoodsId: this.goodsData.orderGoodsId,
  838. courseId: item.courseId,
  839. moduleId: item.moduleId || 0,
  840. chapterId: item.chapterId || 0,
  841. sectionId: item.sectionId || 0
  842. };
  843. this.$router.replace({ path: this.$route.path, query });
  844. },
  845. //回放或直播
  846. async watchJumpSection(item) {
  847. if (item.sectionType == 2) {
  848. let data = await this.studyRecordGetChannelBasicInfo(item.liveUrl);
  849. if (data.watchStatus == "end" || data.watchStatus == "playback") {
  850. this.$message({
  851. type: "warning",
  852. message: `直播已结束`
  853. });
  854. return;
  855. }
  856. if (data.watchStatus == "waiting") {
  857. this.$message({
  858. type: "warning",
  859. message: `直播未开始`
  860. });
  861. return;
  862. }
  863. }
  864. this.$confirm(
  865. `确定前往观看${
  866. item.sectionType == 2 ? " [直播] " : " [回放] "
  867. }${item.sectionName || item.name}?`,
  868. "提示",
  869. {
  870. confirmButtonText: "确定",
  871. cancelButtonText: "取消",
  872. type: "warning"
  873. }
  874. )
  875. .then(() => {
  876. var query = {
  877. goodsName: this.goodsData.name,
  878. goodsId: this.goodsData.goodsId,
  879. gradeId: this.goodsData.gradeId,
  880. orderGoodsId: this.goodsData.orderGoodsId,
  881. courseId: item.courseId,
  882. sectionId: item.sectionId,
  883. chapterId: item.chapterId,
  884. moduleId: item.moduleId,
  885. sectionType: item.sectionType,
  886. vid: item.recordingUrl // 回放vid
  887. };
  888. this.$router.push({
  889. path: "/living-room/" + item.liveUrl,
  890. query
  891. });
  892. })
  893. .catch(() => {});
  894. },
  895. //判断跳转试卷
  896. async doExam(item, type) {
  897. if (this.repeatShowTips) {
  898. this.$confirm("是否跳转做题页面?", "提示", {
  899. confirmButtonText: "确定",
  900. cancelButtonText: "取消",
  901. type: "warning"
  902. })
  903. .then(() => {
  904. this.jumpExam(item, type);
  905. })
  906. .catch(() => {});
  907. } else {
  908. this.repeatShowTips = true;
  909. this.jumpExam(item, type);
  910. }
  911. },
  912. //跳转试卷页面
  913. jumpExam(item, type) {
  914. this.$router.push({
  915. path: "/course-exam/" + this.goodsData.goodsId,
  916. query: {
  917. orderGoodsId: this.goodsData.orderGoodsId,
  918. gradeId: this.goodsData.gradeId,
  919. courseId: this.courseList[0].courseId,
  920. moduleId: item.moduleId || 0,
  921. chapterId: item.chapterId || 0,
  922. sectionId: item.sectionId || 0,
  923. examId: item.typeId,
  924. learning: item.learning,
  925. type: type || item.examType,
  926. nextStatus: "next" //是否继续播放课程
  927. }
  928. });
  929. },
  930. //查看直播状态
  931. studyRecordGetChannelBasicInfo(channelId) {
  932. return new Promise(resolve => {
  933. this.$request
  934. .studyRecordGetChannelBasicInfo({
  935. channelId
  936. })
  937. .then(res => {
  938. resolve(res.data);
  939. })
  940. .catch(err => {
  941. this.$message.error(err.msg);
  942. });
  943. });
  944. },
  945. //限制播放顺序
  946. orderTopTobottom(item) {
  947. if (this.businessData.goodsLearningOrder != 2 || item.sectionType == 2) {
  948. return true;
  949. }
  950. const Findex = this.allSectionList.findIndex(i => {
  951. if (item.type == 3) {
  952. return (
  953. i.type == item.type &&
  954. i.moduleId == item.moduleId &&
  955. i.chapterId == item.chapterId &&
  956. i.sectionId == item.sectionId
  957. );
  958. } else {
  959. return (
  960. i.type == item.type &&
  961. i.typeId == item.typeId &&
  962. i.moduleId == item.moduleId &&
  963. i.chapterId == item.chapterId
  964. );
  965. }
  966. });
  967. if (Findex == -1) {
  968. return true;
  969. } else {
  970. const Ary = this.allSectionList.slice(0, Findex);
  971. if (Ary.length == 0) {
  972. return true;
  973. } else {
  974. return Ary.every(i => i.studyStatus == 1 && !(i.rebuild > 0));
  975. }
  976. }
  977. },
  978. // 检查学习次数
  979. async exceedLearnNum(section) {
  980. let learnNum = await this.goodsTodayStudySectionNum();
  981. let hasLearn = await this.gradeCheckGoodsStudy(section);
  982. if (
  983. this.goodsData.sectionMaxNum > 0 &&
  984. learnNum >= this.goodsData.sectionMaxNum &&
  985. !hasLearn
  986. ) {
  987. this.$message({
  988. type: "warning",
  989. message: `每天最多学习${this.goodsData.sectionMaxNum}节`
  990. });
  991. return false;
  992. }
  993. return true;
  994. },
  995. goodsTodayStudySectionNum() {
  996. return new Promise(resolve => {
  997. this.$request
  998. .goodsTodayStudySectionNum({
  999. goodsId: this.goodsData.goodsId,
  1000. orderGoodsId: this.goodsData.orderGoodsId,
  1001. gradeId: this.goodsData.gradeId
  1002. })
  1003. .then(res => {
  1004. resolve(res.data);
  1005. });
  1006. });
  1007. },
  1008. gradeCheckGoodsStudy(option) {
  1009. return new Promise(resolve => {
  1010. let data = {
  1011. type: option.type == -1 ? 2 : 1,
  1012. goodsId: this.goodsData.goodsId,
  1013. gradeId: this.goodsData.gradeId,
  1014. orderGoodsId: this.goodsData.orderGoodsId,
  1015. moduleId: option.moduleId || 0,
  1016. chapterId: option.chapterId || 0
  1017. };
  1018. if (option.type == -1) {
  1019. data.examId = option.typeId;
  1020. } else {
  1021. data.sectionId = option.sectionId;
  1022. }
  1023. this.$request.gradeCheckGoodsStudy(data).then(res => {
  1024. resolve(res.data);
  1025. });
  1026. });
  1027. },
  1028. bankRecordDoNum(section) {
  1029. return new Promise(resolve => {
  1030. this.$request
  1031. .bankRecordDoNum({
  1032. goodsId: this.goodsData.goodsId,
  1033. orderGoodsId: this.goodsData.orderGoodsId,
  1034. gradeId: this.goodsData.gradeId,
  1035. courseId: this.courseList[0].courseId,
  1036. moduleId: 0,
  1037. chapterId: section.chapterId,
  1038. examId: section.typeId
  1039. })
  1040. .then(res => {
  1041. resolve(res.data);
  1042. });
  1043. });
  1044. },
  1045. //播放逻辑
  1046. async playBackLogic() {
  1047. const { courseId, moduleId, chapterId, sectionId } = this.$route.query;
  1048. if (courseId >= 0 && moduleId >= 0 && chapterId >= 0 && sectionId >= 0) {
  1049. let data = this.allSectionList.find(
  1050. e =>
  1051. e.courseId == courseId &&
  1052. e.moduleId == moduleId &&
  1053. e.chapterId == chapterId &&
  1054. e.sectionId == sectionId
  1055. );
  1056. this.openMenu(data);
  1057. this.unfoldFunc(data); //展开定位列表
  1058. } else {
  1059. this.$request
  1060. .studyRecordQueryLiveLast({
  1061. gradeId: this.goodsData.gradeId,
  1062. orderGoodsId: this.goodsData.orderGoodsId,
  1063. courseId: this.courseList[0].courseId
  1064. })
  1065. .then(res => {
  1066. let data = res.data;
  1067. if (data && data.endTime > parseInt(new Date().getTime() / 1000)) {
  1068. this.$confirm(
  1069. "当前无法学习," +
  1070. this.$tools.timestampToTime(data.endTime, false) +
  1071. "过后可继续学习",
  1072. "提示",
  1073. {
  1074. confirmButtonText: "确定",
  1075. closeOnClickModal: false,
  1076. closeOnPressEscape: false,
  1077. showCancelButton: false,
  1078. distinguishCancelAndClose: false,
  1079. showClose: false
  1080. }
  1081. )
  1082. .then(_ => {
  1083. this.$router.back(-1);
  1084. })
  1085. .catch(_ => {});
  1086. return;
  1087. }
  1088. if (!data.sectionId) {
  1089. data = this.allSectionList[0];
  1090. } else {
  1091. data.type = 3;
  1092. }
  1093. if (
  1094. data.learning == 1 &&
  1095. this.businessData.goodsLearningOrder == 2
  1096. ) {
  1097. let next = this.allSectionList.find(e => e.studyStatus != 1);
  1098. next && (data = next);
  1099. }
  1100. this.openMenu(data);
  1101. this.unfoldFunc(data); //展开定位列表
  1102. });
  1103. }
  1104. },
  1105. //展开定位列表
  1106. unfoldFunc(item, status = false, Renewal = false) {
  1107. return new Promise(async resolve => {
  1108. var scrollTopFunc = () => {
  1109. var scrollTop = document.getElementsByClassName("activeImg_style")[0]
  1110. .offsetTop;
  1111. document.getElementsByClassName(
  1112. "courseTree"
  1113. )[0].scrollTop = scrollTop;
  1114. }; //滚动到当前播放位置
  1115. if (item.moduleId) {
  1116. let moduleChildren = await this.openModule(
  1117. this.courseDataList.find(i => i.moduleId == item.moduleId),
  1118. status,
  1119. Renewal
  1120. );
  1121. if (item.chapterId) {
  1122. await this.openChapter(
  1123. moduleChildren.find(i => i.chapterId == item.chapterId),
  1124. status,
  1125. Renewal
  1126. );
  1127. scrollTopFunc();
  1128. }
  1129. }
  1130. if (!item.moduleId && item.chapterId) {
  1131. await this.openChapter(
  1132. this.courseDataList.find(i => i.chapterId == item.chapterId),
  1133. status,
  1134. Renewal
  1135. );
  1136. scrollTopFunc();
  1137. }
  1138. resolve();
  1139. });
  1140. },
  1141. //已学完,重新定位
  1142. async BackVideoFunc() {
  1143. let hasNoStudyStatus = this.allSectionList.find(i => i.studyStatus == -1); //更新前是否存在未学完的课程
  1144. await this.getAllSectionList(); //获取所有节列表
  1145. await this.unfoldFunc(this.activeSection, true, true);
  1146. if (this.businessData.goodsLearningOrder == 2) {
  1147. var ary = this.allSectionList.find(i => i.studyStatus != 1);
  1148. } else {
  1149. var ary = null;
  1150. let index = this.allSectionList.findIndex(
  1151. i =>
  1152. i.courseId == this.activeSection.courseId &&
  1153. i.moduleId == this.activeSection.moduleId &&
  1154. i.chapterId == this.activeSection.chapterId &&
  1155. i.sectionId == this.activeSection.sectionId
  1156. );
  1157. this.$emit(
  1158. "update:activeSection",
  1159. Object.assign({}, this.allSectionList[index])
  1160. ); //重新赋值当前节数据
  1161. ary = this.allSectionList[index + 1];
  1162. if (ary && ary.studyStatus == 1) {
  1163. ary = null;
  1164. hasNoStudyStatus = null;
  1165. }
  1166. }
  1167. if (ary) {
  1168. if (ary.type != 3) {
  1169. this.$confirm(
  1170. "本章视频已学习完成,继续进行下一个考试?",
  1171. "温馨提示",
  1172. {
  1173. confirmButtonText: "确定",
  1174. cancelButtonText: "取消",
  1175. type: "warning"
  1176. }
  1177. )
  1178. .then(() => {
  1179. this.repeatShowTips = false; //是否显示跳转提示
  1180. this.openMenu(ary);
  1181. this.unfoldFunc(ary, true, true);
  1182. })
  1183. .catch(() => {});
  1184. } else {
  1185. this.$confirm("当前视频已学完,继续学习下一个视频?", "温馨提示", {
  1186. confirmButtonText: "确定",
  1187. cancelButtonText: "取消",
  1188. type: "warning"
  1189. })
  1190. .then(() => {
  1191. this.openMenu(ary);
  1192. this.unfoldFunc(ary, true, true);
  1193. })
  1194. .catch(() => {});
  1195. }
  1196. } else {
  1197. if (hasNoStudyStatus) {
  1198. this.$alert(
  1199. "恭喜您课程学习全部完成,教务会在1-3个工作日内完成学习初审,请耐心等待。",
  1200. "温馨提示",
  1201. {
  1202. confirmButtonText: "确定",
  1203. showClose: false,
  1204. callback: action => {
  1205. if (action == "confirm") {
  1206. this.jumpPage();
  1207. }
  1208. }
  1209. }
  1210. );
  1211. }
  1212. }
  1213. },
  1214. //回到个人中心
  1215. jumpPage() {
  1216. this.$router.replace({
  1217. path: "/person-center/my-course"
  1218. });
  1219. }
  1220. },
  1221. beforeDestroy() {
  1222. this.$bus.$off("BackVideoFunc");
  1223. }
  1224. };
  1225. </script>
  1226. <style lang="scss" scoped>
  1227. .active {
  1228. background-color: rgb(107, 107, 107) !important;
  1229. }
  1230. .activeImg_style {
  1231. width: 20px;
  1232. height: 30px;
  1233. }
  1234. .courseTree {
  1235. height: 416px;
  1236. overflow: auto;
  1237. user-select: none;
  1238. &::-webkit-scrollbar {
  1239. width: 5px;
  1240. height: 9px;
  1241. }
  1242. /*定义滚动条轨道 内阴影+圆角*/
  1243. &::-webkit-scrollbar-track {
  1244. background-color: inherit;
  1245. border: none;
  1246. border-radius: 10px;
  1247. }
  1248. /*定义滑块 内阴影+圆角*/
  1249. &::-webkit-scrollbar-thumb {
  1250. border-radius: 10px;
  1251. position: relative;
  1252. right: 2px;
  1253. background-color: #c6c6cd;
  1254. width: 6px;
  1255. }
  1256. }
  1257. .level1 {
  1258. padding-left: 0px;
  1259. }
  1260. .level2 {
  1261. padding-left: 10px;
  1262. }
  1263. .level3 {
  1264. padding-left: 20px;
  1265. }
  1266. .menu_box {
  1267. margin-top: 4px;
  1268. padding: 0px 10px;
  1269. display: flex;
  1270. align-items: center;
  1271. min-height: 30px;
  1272. max-height: 60px;
  1273. color: #fff;
  1274. cursor: pointer;
  1275. &:hover {
  1276. background-color: rgb(81, 81, 81);
  1277. }
  1278. & > .left {
  1279. flex-shrink: 0;
  1280. padding-right: 10px;
  1281. }
  1282. & > .center {
  1283. flex: 1;
  1284. word-break: break-all;
  1285. text-overflow: ellipsis;
  1286. overflow: hidden;
  1287. display: -webkit-box;
  1288. -webkit-line-clamp: 2;
  1289. -webkit-box-orient: vertical;
  1290. }
  1291. & > .right {
  1292. & > .during {
  1293. margin-right: 4px;
  1294. }
  1295. display: flex;
  1296. align-items: center;
  1297. // width: 80px;
  1298. flex-shrink: 0;
  1299. }
  1300. }
  1301. .live_style {
  1302. & > span {
  1303. font-size: 12px;
  1304. }
  1305. }
  1306. </style>