courseDetailed.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. <template>
  2. <view class="details">
  3. <u-navbar back-text="" title="" back-icon-color="#FFFFFF" :background="{ background: '#3D5D4C' }" :border-bottom="false"></u-navbar>
  4. <!-- 视频 -->
  5. <view class="details-video" v-if="isPlay">
  6. <video class="details-video-con" id="myVideo" @timeupdate="timeUpdate" :src="videoInfo.videoUrl" controls
  7. :initial-time="initial_time" object-fit="fill" play-btn-position="center" @tap="videoClick" @loadedmetadata="loadedmetadata">
  8. <cover-view class="video-control">
  9. <cover-view class="multi rate" @tap.stop="showSwitchRate">x {{ currentRate }}</cover-view>
  10. </cover-view>
  11. <cover-view class="multi-list rate" :class="{ active: rateShow }">
  12. <cover-view v-for="(item, index) in ['0.5', '1.0', '1.5', '2.0']" :key="index"
  13. class="multi-item rate" :data-rate="item" @tap="switchRate"
  14. :class="{ active: item == currentRate }">
  15. {{ item }}
  16. </cover-view>
  17. </cover-view>
  18. </video>
  19. </view>
  20. <!-- 介绍 -->
  21. <view class="details-content">
  22. <view class="details-content-title">{{ videoInfo.chapterName }}</view>
  23. <view class="details-content-progress">本课程
  24. 共{{ info.amount }}课,已学完{{ info.finishCount }}课,共进度{{ info.finishPercent || 0 }}%</view>
  25. <view class="details-content-teacher">主讲老师:{{ info.presenter }}</view>
  26. <view class="details-content-info">{{ videoInfo.chapterInfo }}</view>
  27. </view>
  28. <!-- 课程章节 -->
  29. <view class="details-classes">
  30. <view class="details-classes-header">
  31. <view>精选课程</view>
  32. <view>更多
  33. <u-icon name="arrow-right" size="22" color="#A3A3A3" />
  34. </view>
  35. </view>
  36. <view class="details-classes-list">
  37. <view class="details-classes-list-item" v-for="(item, index) in info.chapterList" :key="index"
  38. :class="{'active': index === videoIndex }" @click="classesClick(index)">
  39. <view>{{ index + 1 }}</view>
  40. <view>{{ item.flag === 2 ? '已学' : (item.finishPercent + '%') }}</view>
  41. </view>
  42. </view>
  43. </view>
  44. <view class="details-line">
  45. <view></view>
  46. </view>
  47. <!-- 评论 -->
  48. <view class="details-comment">
  49. <view class="details-comment-header">
  50. <view>课程评论</view>
  51. <view>共{{ total }}条评论</view>
  52. </view>
  53. <view class="details-comment-list">
  54. <view class="details-comment-list-item" v-for="(item, index) in commentList" :key="index">
  55. <view class="left">
  56. <u-avatar :src="item.createByAvatar" size="96" mode="square"></u-avatar>
  57. </view>
  58. <view class="right">
  59. <view>{{ item.createBy }}</view>
  60. <view>
  61. <u-rate :count="5" size="28" disabled="" active-color="#FFBC00" v-model="item.starLevel">
  62. </u-rate>
  63. <text>{{ item.createTime }}</text>
  64. </view>
  65. <view>{{ item.content }}</view>
  66. </view>
  67. </view>
  68. </view>
  69. <view class="details-comment-page" v-if="total">
  70. <wyb-pagination :padding="0" :totalItems="total" :current="query.pageNum" @change="pageChange" />
  71. </view>
  72. <view class="details-comment-mine">
  73. <text>我的评论</text>
  74. </view>
  75. <view class="details-comment-conent">
  76. <view class="details-comment-conent-star">
  77. <u-rate :count="5" size="40" active-color="#FFBC00" v-model="form.starLevel"></u-rate>
  78. </view>
  79. <view class="details-comment-content-textarea">
  80. <u-input v-model="form.content" placeholder="请输入您的评价" type="textarea"
  81. :custom-style="{ backgroundColor: '#F5F5F5', padding: '30rpx', borderRadius: '10rpx', minHeight: '280rpx' }">
  82. </u-input>
  83. </view>
  84. <view class="details-comment-conent-button" @click="submitCommet">提交</view>
  85. </view>
  86. </view>
  87. <u-toast ref="uToast" />
  88. </view>
  89. </template>
  90. <script>
  91. import wybPagination from '@/components/wyb-pagination/wyb-pagination.vue'
  92. export default {
  93. data() {
  94. return {
  95. info: {},
  96. videoContext: uni.createVideoContext('myVideo', this),
  97. videoInfo: {},
  98. videoIndex: 0,
  99. // 视频实时时间
  100. initial_time: 0,
  101. // 视频已经播放时间
  102. playedTime: 0,
  103. rateShow: false, // 倍速浮层
  104. currentRate: 1.0, // 默认倍速
  105. // 视频实际时间
  106. video_real_time: 0,
  107. classesId: '',
  108. isPlay: true,
  109. query: {
  110. pageNum: 1,
  111. pageSize: 5,
  112. tabId: ''
  113. },
  114. total: 0,
  115. commentList: [],
  116. form: {
  117. tabId: '',
  118. starLevel: 0,
  119. content: ''
  120. },
  121. currentDuration: 0,
  122. duration: 0
  123. }
  124. },
  125. onLoad(page) {
  126. if (page.id) {
  127. this.getClassesDetails(page.id);
  128. this.classesId = page.id
  129. }
  130. },
  131. beforeDestroy() {
  132. let playDuration = this.video_real_time;
  133. if (this.videoInfo.playDuration > this.video_real_time) {
  134. this.currentDuration = this.video_real_time;
  135. playDuration = this.videoInfo.playDuration
  136. } else {
  137. this.currentDuration = this.video_real_time
  138. }
  139. this.submitTimeLong({
  140. tabId: this.videoInfo.id,
  141. playDuration: playDuration,
  142. currentDuration: this.currentDuration,
  143. duration: this.duration
  144. }, 0, true)
  145. },
  146. methods: {
  147. /**
  148. * 获取视频总时长
  149. * @param {Object} data
  150. */
  151. loadedmetadata(data) {
  152. console.log(data);
  153. this.duration = data.detail.duration
  154. },
  155. /**
  156. * 显示倍速浮层
  157. * @param {Object} rate
  158. */
  159. showSwitchRate(rate) {
  160. let that = this;
  161. that.rateShow = true;
  162. },
  163. /**
  164. * 切换倍速
  165. * @param {Object} e
  166. */
  167. switchRate(e) {
  168. let that = this;
  169. let rate = Number(e.currentTarget.dataset.rate);
  170. that.currentRate = rate;
  171. that.rateShow = false;
  172. this.videoContext.playbackRate(rate);
  173. },
  174. /**
  175. * 视频点击
  176. * @param {Object} e
  177. */
  178. videoClick(e) {
  179. this.rateShow = false;
  180. },
  181. /**
  182. * 获取课程详情
  183. * @param {Object} id
  184. */
  185. getClassesDetails(id) {
  186. this.$u.api.school.getPackageCourseDetail({
  187. id
  188. }).then(res => {
  189. if (res.code === 200) {
  190. console.log(res)
  191. this.info = res.data;
  192. this.videoInfo = res.data.chapterList[this.videoIndex];
  193. this.initial_time = Number(res.data.chapterList[this.videoIndex].currentDuration)
  194. this.video_real_time = Number(res.data.chapterList[this.videoIndex].playDuration)
  195. this.playedTime = Number(res.data.chapterList[this.videoIndex].playDuration)
  196. this.query.tabId = res.data.chapterList[this.videoIndex].id
  197. this.form.tabId = res.data.chapterList[this.videoIndex].id
  198. this.query.pageNum = 1;
  199. this.isPlay = true
  200. this.getCommentList();
  201. }
  202. })
  203. },
  204. /**
  205. * 课程章节点击
  206. * @param {Object} index
  207. */
  208. classesClick(index) {
  209. this.isPlay = false;
  210. let playDuration = this.video_real_time;
  211. if (this.videoInfo.playDuration > this.video_real_time) {
  212. this.currentDuration = this.video_real_time;
  213. playDuration = this.videoInfo.playDuration
  214. } else {
  215. this.currentDuration = this.video_real_time
  216. }
  217. this.submitTimeLong ({
  218. tabId: this.videoInfo.id,
  219. playDuration: playDuration,
  220. duration: this.duration,
  221. currentDuration: this.currentDuration
  222. }, index);
  223. },
  224. /**
  225. * 控制视频不能快进
  226. * @param {Object} e
  227. */
  228. timeUpdate(e) {
  229. let isReady = 1; // 是否开启可以视频快进 1 禁止开启
  230. if (this.videoInfo.finishPercent === 100) {
  231. isReady = 3;
  232. }
  233. //跳转到指定播放位置 initial-time 时间为秒
  234. let that = this;
  235. //播放的总时长
  236. let duration = e.detail.duration
  237. //实时播放进度 秒数
  238. let currentTime = parseInt(e.detail.currentTime)
  239. //当前视频进度
  240. let jump_time = 0
  241. if (that.video_real_time == 0) {
  242. jump_time = parseInt(that.initial_time) + parseInt(that.video_real_time)
  243. } else {
  244. jump_time = parseInt(that.video_real_time)
  245. }
  246. console.log('jump_time', jump_time)
  247. console.log('that.initial_time', that.initial_time)
  248. console.log('this.playedTime', this.playedTime)
  249. console.log('that.video_real_time', that.video_real_time)
  250. if (jump_time > this.playedTime) {
  251. isReady = 1;
  252. } else {
  253. isReady = 3;
  254. }
  255. if(isReady === 1){
  256. if (currentTime > jump_time && currentTime - jump_time > 3) {
  257. let videoContext = wx.createVideoContext('myVideo')
  258. videoContext.seek(that.video_real_time)
  259. wx.showToast({
  260. title: '未完整看完该视频,不能快进',
  261. icon: 'none',
  262. duration: 2000,
  263. })
  264. }
  265. }
  266. that.video_real_time = currentTime //实时播放进度
  267. },
  268. /**
  269. * 提交课程时长
  270. */
  271. submitTimeLong({
  272. tabId,
  273. playDuration,
  274. currentDuration,
  275. duration
  276. }, index, flag) {
  277. if (tabId) {
  278. this.$u.api.training.videoTimeLongApi({
  279. tabId,
  280. playDuration,
  281. currentDuration,
  282. duration
  283. }).then(res => {
  284. if (res.code === 200) {
  285. if (!flag) {
  286. this.$refs.uToast.show({
  287. title: '已记录章节时长!',
  288. type: 'success'
  289. })
  290. this.videoInfo = this.info.chapterList[index]
  291. this.form.tabId = this.videoInfo.id
  292. this.query.tabId = this.videoInfo.id
  293. this.videoIndex = index
  294. this.getClassesDetails(this.classesId);
  295. }
  296. } else {
  297. this.$refs.uToast.show({
  298. title: res.msg,
  299. type: 'error'
  300. })
  301. }
  302. })
  303. }
  304. },
  305. /**
  306. * 获取评论列表
  307. */
  308. getCommentList() {
  309. if (this.query.tabId) {
  310. this.$u.api.training.getClassesCommentApi(this.query).then(res => {
  311. if (res.code === 200) {
  312. this.total = Number(res.total);
  313. this.commentList = res.rows
  314. }
  315. })
  316. }
  317. },
  318. /**
  319. * @param {Object} e 分页触发
  320. */
  321. pageChange(e) {
  322. this.query.pageNum = e.current
  323. this.getCommentList()
  324. },
  325. /**
  326. * 提交评论
  327. */
  328. submitCommet() {
  329. if (this.form.tabId) {
  330. if (this.form.starLevel && this.form.content) {
  331. this.$u.api.training.addClassesCommentApi(this.form).then(res => {
  332. if (res.code === 200) {
  333. this.$refs.uToast.show({
  334. title: '评论成功!',
  335. type: 'success'
  336. })
  337. this.form.content = ''
  338. this.form.starLevel = 0
  339. this.getCommentList();
  340. } else {
  341. this.$refs.uToast.show({
  342. title: res.msg,
  343. type: 'error'
  344. })
  345. }
  346. })
  347. }
  348. if (!this.form.starLevel) {
  349. this.$refs.uToast.show({
  350. title: '请选择星级',
  351. type: 'warning'
  352. })
  353. }
  354. if (!this.form.content) {
  355. this.$refs.uToast.show({
  356. title: '请输入评论内容',
  357. type: 'warning'
  358. })
  359. }
  360. } else {
  361. this.$refs.uToast.show({
  362. title: '未找到课程章节,无法提交评论!',
  363. type: 'warning'
  364. })
  365. }
  366. }
  367. }
  368. }
  369. </script>
  370. <style lang="scss" scoped>
  371. @import './courseDetailed.scss';
  372. </style>