index.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. <!-- 学习课件 -->
  2. <template>
  3. <div class="learningMaterials">
  4. <div class="page-container-top-max-big-box">
  5. <p class="top-1-p">开始学习</p>
  6. <p class="top-2-p" @click="backPage()">返回</p>
  7. </div>
  8. <div class="content-box">
  9. <div class="left-max-big-box">
  10. <coursewareComponent ref="coursewareComponent"></coursewareComponent>
  11. </div>
  12. <div class="right-max-big-box">
  13. <p class="title-p">{{newData.coursewareName}}</p>
  14. <div class="text-p" style="color:#666;">{{secondsToMinutes(newData.minLearningDuration)}}分钟丨{{newData.creditHours}}学时丨{{newData.points}}积分</div>
  15. <!--<div class="text-p">已学 {{newData.learnedCoursewareCount}} / 总 {{newData.totalCoursewareCount}} 课件</div>-->
  16. <div class="progress-box">
  17. <p class="progress-title-box">学习进度</p>
  18. <div class="progress-min-box">
  19. <el-progress :stroke-width="12" :percentage="newData.progress?newData.progress:0"></el-progress>
  20. </div>
  21. </div>
  22. <div class="learning-for-max-big-box scrollbar-box">
  23. <div class="for-max-big-box"
  24. v-for="(item,index) in newData.coursewares" :key="index">
  25. <div class="for-big-box"
  26. :class="checkLearningIndex == index ?'check-for-box':''">
  27. <!--<p>{{item.coursewareName}}</p>-->
  28. <p></p>
  29. <p>已学习:{{formatTime(item.learnedDuration)}} / {{formatTime(item.minLearningDuration)}}</p>
  30. <p :class="item.learnStatus ==2 ?'colorA':(item.learnStatus ==1 ?'colorB':'')">{{item.learnStatus==2?'已学完':(item.learnStatus==1?'学习中':'未学完')}}</p>
  31. </div>
  32. <p class="for-button" @click="finishStudying(index)"
  33. v-if="item.learnStatus != 2 && item.learnedDuration == item.minLearningDuration && checkLearningIndex == index">
  34. 若已完成学习,请点此确认
  35. </p>
  36. </div>
  37. </div>
  38. </div>
  39. </div>
  40. </div>
  41. </template>
  42. <script>
  43. import StudyTimer from '@/utils/timerUtils/StudyTimer';
  44. import { VerifySlider } from 'vue-verify-slider';
  45. import coursewareComponent from "@/views/safetyEducationExaminationNew/components/coursewareComponent.vue";
  46. import {
  47. examUserCoursewareLearningDetail,examUserCoursewareLearningReportProgress,
  48. examUserCoursewareLearningConfirmCompletion,examUserCoursewareLearningRestart
  49. } from "@/api/safetyEducationExaminationNew/index";
  50. export default {
  51. name: 'index',
  52. components: {
  53. VerifySlider,
  54. coursewareComponent,
  55. },
  56. data() {
  57. return {
  58. coursewareId:null,
  59. newData:{
  60. coursewareName:"",
  61. minLearningDuration:0,
  62. creditHours:0,
  63. points:0,
  64. learnedCoursewareCount:0,
  65. totalCoursewareCount:0,
  66. progress:0,
  67. coursewares:[],
  68. },
  69. //当前选中
  70. checkLearningIndex:0,
  71. //学习计时器
  72. studyTimer: null,
  73. //验证器弹窗状态
  74. dialogVisible:false,
  75. //验证器弹窗间隔时间
  76. verificationTime:0,
  77. //心跳回报时间
  78. heartbeatValue:30,
  79. }
  80. },
  81. created() {
  82. },
  83. mounted() {
  84. this.initialization();
  85. },
  86. methods: {
  87. //初始化
  88. initialization(){
  89. this.$set(this,'coursewareId',this.$route.params.coursewareId)
  90. this.$nextTick(()=>{
  91. this.examUserCoursewareLearningDetail();
  92. })
  93. },
  94. //根据课件ID查询课件信息
  95. examUserCoursewareLearningDetail(){
  96. examUserCoursewareLearningDetail({ coursewareId:this.coursewareId }).then(response => {
  97. response.data.coursewares=[
  98. {
  99. coursewareName:response.data.coursewareName,
  100. learnedDuration:response.data.learnedDuration,
  101. minLearningDuration:response.data.minLearningDuration,
  102. learnStatus:response.data.learnStatus,
  103. attachmentPath:response.data.attachmentPath,
  104. attachmentType:response.data.attachmentType,
  105. materialType:response.data.materialType,
  106. articleContent:response.data.articleContent,
  107. },
  108. ]
  109. this.$set(this, 'newData', response.data)
  110. this.initTimer();//学习计时器
  111. this.courseRendering(this.checkLearningIndex);//课件渲染
  112. })
  113. },
  114. //返回
  115. backPage() {
  116. this.$router.back()
  117. },
  118. //完成课件学习
  119. finishStudying(index){
  120. let self = this;
  121. this.$confirm('确认完成课件学习?', "警告", {
  122. confirmButtonText: "确认",
  123. cancelButtonText: "取消",
  124. type: "warning"
  125. }).then(function() {
  126. }).then(() => {
  127. self.examUserCoursewareLearningConfirmCompletion();
  128. }).catch(() => {});
  129. },
  130. //课件渲染
  131. courseRendering(index){
  132. let name = this.newData.coursewares[index].coursewareName;
  133. let url = '';
  134. let type = '';
  135. if(this.newData.coursewares[index].materialType == 1){
  136. //视频
  137. type = 'video'
  138. url = this.newData.coursewares[index].attachmentPath;
  139. }else if(this.newData.coursewares[index].materialType == 3){
  140. //文档
  141. if(this.newData.coursewares[index].attachmentType == '.docx'){
  142. type = 'docx'
  143. }else if(this.newData.coursewares[index].attachmentType == '.xlsx'){
  144. type = 'excel'
  145. }else if(this.newData.coursewares[index].attachmentType == '.pdf'){
  146. type = 'pdf'
  147. }
  148. url = this.newData.coursewares[index].attachmentPath;
  149. }else if(this.newData.coursewares[index].materialType == 4){
  150. //文章
  151. type = 'text'
  152. url = this.newData.coursewares[index].articleContent;
  153. }
  154. this.$refs['coursewareComponent'].initialize(name,url,type);
  155. if(this.newData.coursewares[this.checkLearningIndex].learnStatus == 0){
  156. this.$set(this.newData.coursewares[this.checkLearningIndex],'learnStatus',1);
  157. }
  158. },
  159. //学习完成
  160. examUserCoursewareLearningConfirmCompletion(){
  161. let self = this;
  162. let obj = {
  163. coursewareId:this.coursewareId,
  164. courseId:0,
  165. examId:0,
  166. }
  167. examUserCoursewareLearningConfirmCompletion(obj).then(response => {
  168. this.$set(this.newData.coursewares[this.checkLearningIndex],'learnStatus',2);
  169. this.msgSuccess(response.message)
  170. })
  171. },
  172. //心跳接口
  173. examUserCoursewareLearningReportProgress(){
  174. let obj = {
  175. coursewareId:this.coursewareId,
  176. courseId:0,
  177. examId:0,
  178. duration:this.heartbeatValue,
  179. }
  180. examUserCoursewareLearningReportProgress(obj).then(response => {})
  181. },
  182. //倒计时时间换算
  183. formatTime(totalSeconds) {
  184. if (typeof totalSeconds !== 'number' || totalSeconds <= 0) {
  185. return '0秒';
  186. }
  187. const minutes = Math.floor(totalSeconds / 60);
  188. const seconds = totalSeconds % 60;
  189. if (minutes > 0 && seconds > 0) {
  190. return `${minutes}分${seconds}秒`;
  191. } else if (minutes > 0 && seconds === 0) {
  192. return `${minutes}分`;
  193. } else {
  194. return `${seconds}秒`;
  195. }
  196. },
  197. //展示时间换算
  198. secondsToMinutes(totalSeconds) {
  199. if (typeof totalSeconds !== 'number' || totalSeconds <= 0) {
  200. return 1;
  201. }
  202. return Math.ceil(totalSeconds / 60);
  203. },
  204. /*=================================== 学习定时器 ===================================*/
  205. // 初始化定时器
  206. initTimer() {
  207. let self = this;
  208. if(this.newData.coursewares[this.checkLearningIndex].learnedDuration == this.newData.coursewares[this.checkLearningIndex].minLearningDuration){
  209. return
  210. }
  211. this.studyTimer = new StudyTimer({
  212. initialSeconds: self.newData.coursewares[self.checkLearningIndex].learnedDuration,
  213. maxSeconds: self.newData.coursewares[self.checkLearningIndex].minLearningDuration,
  214. onTick: (elapsedSeconds) => {
  215. //每次计时
  216. this.$set(self.newData.coursewares[self.checkLearningIndex],'learnedDuration',elapsedSeconds);
  217. //心跳判断
  218. if (elapsedSeconds % this.heartbeatValue === 0) {
  219. //调用心跳方法
  220. this.examUserCoursewareLearningReportProgress();
  221. }
  222. },
  223. onMaxTimeReached: (elapsedSeconds) => {
  224. //达到最大值
  225. this.$set(self.newData.coursewares[self.checkLearningIndex],'learnedDuration',elapsedSeconds);
  226. //调用心跳方法
  227. this.examUserCoursewareLearningReportProgress();
  228. }
  229. });
  230. this.studyTimer.resume();
  231. },
  232. // 切换暂停/继续
  233. handleToggle() {
  234. if (!this.studyTimer) return;
  235. if (this.studyTimer.isRunning) {
  236. this.studyTimer.pause();
  237. } else {
  238. this.studyTimer.resume();
  239. }
  240. },
  241. // 清除定时器
  242. clearTimer() {
  243. if (this.studyTimer) {
  244. this.studyTimer.clear();
  245. this.studyTimer = null;
  246. }
  247. },
  248. //乘法
  249. accMul(arg1,arg2){
  250. var m=0,s1=arg1.toString(),s2=arg2.toString();
  251. try{m+=s1.split(".")[1].length}catch(e){}
  252. try{m+=s2.split(".")[1].length}catch(e){}
  253. return Number(s1.replace(".",""))*Number(s2.replace(".",""))/Math.pow(10,m)
  254. },
  255. },
  256. beforeDestroy() {
  257. if (this.studyTimer) {
  258. this.studyTimer.destroy();
  259. this.studyTimer = null;
  260. }
  261. },
  262. }
  263. </script>
  264. <style scoped lang="scss">
  265. .learningMaterials {
  266. flex:1;
  267. display: flex;
  268. flex-direction: column;
  269. background:#FFFFFF;
  270. border-radius:10px;
  271. overflow: hidden;
  272. padding:0;
  273. box-shadow: 0 0 8px 1px rgba(0, 0, 0, 0.1);
  274. margin:10px;
  275. ::v-deep .el-progress .el-progress__text{
  276. margin-left:10px;
  277. margin-top:1px;
  278. }
  279. .content-box{
  280. flex: 1;
  281. display: flex;
  282. overflow: hidden;
  283. .left-max-big-box{
  284. flex:1;
  285. display: flex;
  286. flex-direction: column;
  287. background-color: #dedede;
  288. margin:20px 20px;
  289. border-radius:10px;
  290. overflow: hidden;
  291. box-shadow: 0 0 15px rgba(0, 0, 0, 0.4);
  292. border:1px solid #dedede;
  293. }
  294. .right-max-big-box{
  295. padding:20px 0;
  296. width:400px;
  297. display: flex;
  298. flex-direction: column;
  299. overflow: hidden;
  300. border-left: 1px solid #dedede;
  301. .title-p{
  302. font-size:16px;
  303. font-weight:700;
  304. line-height:30px;
  305. padding:0 20px;
  306. }
  307. .text-p{
  308. font-size:14px;
  309. line-height:20px;
  310. margin-bottom:20px;
  311. padding:0 20px;
  312. }
  313. .progress-box{
  314. width:400px;
  315. height:20px;
  316. display: flex;
  317. padding:0 20px;
  318. .progress-title-box{
  319. font-size:14px;
  320. line-height:20px;
  321. font-weight:700;
  322. }
  323. .progress-min-box{
  324. flex:1;
  325. padding:0 0 0 20px;
  326. .el-progress{
  327. margin-top:1px;
  328. }
  329. }
  330. }
  331. .learning-for-max-big-box{
  332. flex:1;
  333. display: flex;
  334. flex-direction: column;
  335. padding:0 20px;
  336. .for-max-big-box{
  337. margin-top:40px;
  338. .for-big-box{
  339. cursor: pointer;
  340. border-radius:6px;
  341. border:1px solid #666;
  342. padding:15px 10px;
  343. position: relative;
  344. p{
  345. line-height:30px;
  346. }
  347. p:nth-child(1){
  348. font-size:16px;
  349. font-weight:700;
  350. /*单行省略号*/
  351. display:block;
  352. overflow:hidden;
  353. text-overflow:ellipsis;
  354. white-space:nowrap;
  355. }
  356. p:nth-child(2){
  357. font-size:14px;
  358. }
  359. p:nth-child(3){
  360. font-size:14px;
  361. position: absolute;
  362. bottom: 16px;
  363. right: 16px;
  364. color:#fff;
  365. background-color: #999;
  366. border-radius:4px;
  367. width:60px;
  368. line-height:24px;
  369. text-align: center;
  370. }
  371. .colorA{
  372. background-color: #0183fa!important;
  373. color:#fff!important;
  374. }
  375. .colorB{
  376. background-color: #fff!important;
  377. border:1px solid #0183fa!important;
  378. color:#0183fa!important;
  379. }
  380. }
  381. .for-button{
  382. margin-top:20px;
  383. background-color: #13CE66;
  384. color:#fff;
  385. text-align: center;
  386. line-height:30px;
  387. border-radius:4px;
  388. cursor: pointer;
  389. font-size:14px;
  390. }
  391. .check-for-box{
  392. background-color: rgba(1,131,250,0.1);
  393. border:1px solid #0183fa!important;
  394. p{
  395. color:#0183fa!important;
  396. }
  397. }
  398. }
  399. }
  400. }
  401. }
  402. }
  403. </style>