video-class-detail.tsx 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  1. import CourseVideoItem from '@/business-components/course-video-item'
  2. import SectionDetail from '@/business-components/section-detail'
  3. import ColHeader from '@/components/col-header'
  4. import ColVideo from '@/components/col-video'
  5. import request from '@/helpers/request'
  6. import {
  7. Cell,
  8. Icon,
  9. Rate,
  10. Tab,
  11. Tabs,
  12. Image,
  13. Button,
  14. Sticky,
  15. Field,
  16. List,
  17. Toast,
  18. Dialog
  19. } from 'vant'
  20. import { defineComponent } from 'vue'
  21. import styles from './video-class-detail.module.less'
  22. import { state } from '@/state'
  23. import iconTeacher from '@common/images/icon_teacher.png'
  24. import ColResult from '@/components/col-result'
  25. import dayjs from 'dayjs'
  26. import { onSubmitZero, orderStatus } from '@/views/order-detail/orderStatus'
  27. export default defineComponent({
  28. name: 'VideoClassDetail',
  29. data() {
  30. const query = this.$route.query
  31. return {
  32. groupId: query.groupId,
  33. classId: query.classId,
  34. tabIndex: 1,
  35. title: '',
  36. lessonPrice: 0,
  37. useRelationType: '',
  38. alreadyBuy: false,
  39. videoDetail: {} as any,
  40. detailList: [],
  41. posterUrl: '',
  42. srcUrl: '',
  43. message: '',
  44. navHeight: 0 as any,
  45. currentClassIndex: 1,
  46. reload: false,
  47. videoContent: '',
  48. list: [],
  49. dataShow: true, // 判断是否有数据
  50. loading: false,
  51. finished: false,
  52. params: {
  53. page: 1,
  54. rows: 20
  55. },
  56. freeRate: 0, // 试看百分比
  57. trySee: false, // 是否试看
  58. videoHeight: '212px'
  59. }
  60. },
  61. computed: {
  62. users() {
  63. return state.user.data
  64. },
  65. offsetTop() {
  66. const navHeight: number = this.navHeight
  67. const top = Number(navHeight) + 44
  68. return top + 'px'
  69. }
  70. },
  71. async mounted() {
  72. // 处理视频显示
  73. const width = document.body.clientWidth || document.body.offsetWidth
  74. this.videoHeight = (width / 16) * 9 + 'px'
  75. this.navHeight = sessionStorage.getItem('navHeight') || 0
  76. try {
  77. //
  78. await this.__init()
  79. const config = await request.get(
  80. '/api-student/sysConfig/queryByParamNameList',
  81. {
  82. params: {
  83. paramNames: 'video_lesson_free_rate'
  84. }
  85. }
  86. )
  87. this.freeRate = config.data[0]?.paramValue || 0
  88. } catch {}
  89. this.getList()
  90. },
  91. methods: {
  92. async __init() {
  93. this.reload = true
  94. try {
  95. const res = await request.get(
  96. '/api-student/videoLesson/selectVideoLesson',
  97. {
  98. params: {
  99. groupId: this.groupId
  100. }
  101. }
  102. )
  103. const result = res.data || {}
  104. this.videoDetail = result.lessonGroup
  105. this.title = result.lessonGroup.lessonName
  106. this.lessonPrice = result.lessonGroup.lessonPrice
  107. this.useRelationType = result.lessonGroup.relationType
  108. this.alreadyBuy = result.alreadyBuy
  109. this.detailList = result.detailList || []
  110. this.trySee = !result.alreadyBuy
  111. this.detailList.forEach((item: any, index: number) => {
  112. if (item.id === Number(this.classId)) {
  113. this.posterUrl = item.coverUrl
  114. this.srcUrl = item.videoUrl
  115. this.title = item.videoTitle
  116. this.currentClassIndex = index + 1
  117. this.videoContent = item.videoContent
  118. }
  119. })
  120. } catch {
  121. //
  122. }
  123. this.reload = false
  124. },
  125. onSearch() {
  126. this.params.page = 1
  127. this.list = []
  128. this.dataShow = true // 判断是否有数据
  129. this.loading = false
  130. this.finished = false
  131. this.getList()
  132. },
  133. async getList() {
  134. try {
  135. const params = this.params
  136. const res = await request.post('/api-student/videoLesson/page', {
  137. data: {
  138. ...params,
  139. videoId: this.classId
  140. }
  141. })
  142. this.loading = false
  143. const result = res.data || {}
  144. // 处理重复请求数据
  145. if (this.list.length > 0 && result.pageNo === 1) {
  146. return
  147. }
  148. this.list = this.list.concat(result.rows || [])
  149. this.finished = result.pageNo >= result.totalPage
  150. this.params.page = result.pageNo + 1
  151. this.dataShow = this.list.length > 0
  152. } catch {
  153. this.dataShow = false
  154. this.finished = true
  155. }
  156. },
  157. onPlay(item: any) {
  158. // 判断是否点击的是当前播放的视频
  159. if (item.id === Number(this.classId)) {
  160. return
  161. }
  162. this.reload = true
  163. this.posterUrl = item.imgUrl
  164. this.srcUrl = item.videoUrl
  165. this.title = item.title
  166. this.currentClassIndex = item.index
  167. this.videoContent = item.content
  168. this.classId = item.id
  169. this.onSearch()
  170. setTimeout(() => {
  171. this.reload = false
  172. }, 0)
  173. },
  174. async onSubmit() {
  175. try {
  176. await request.post('/api-student/videoLesson/evaluate', {
  177. data: {
  178. isTeacher: 0,
  179. videoId: this.classId,
  180. content: this.message,
  181. studentId: state.user.data.userId
  182. }
  183. })
  184. Toast('评论成功')
  185. this.message = ''
  186. setTimeout(() => {
  187. this.onSearch()
  188. }, 500)
  189. } catch {}
  190. }
  191. },
  192. render() {
  193. return (
  194. <div class={styles['video-class-detail']}>
  195. {/* <Sticky offsetTop={0} position="top"> */}
  196. <ColHeader
  197. v-slots={{
  198. default: () =>
  199. !this.reload && (
  200. <ColVideo
  201. freeRate={Number(this.freeRate)}
  202. freeTitleStatus={this.lessonPrice > 0 ? true : false}
  203. trySee={this.trySee}
  204. src={this.srcUrl}
  205. poster={this.posterUrl}
  206. height={this.videoHeight}
  207. isBuy
  208. onBuyEmit={async () => {
  209. if (this.lessonPrice > 0) {
  210. this.$router.back()
  211. return
  212. }
  213. try {
  214. const userInfo = this.videoDetail
  215. orderStatus.orderObject.orderType = 'VIDEO'
  216. orderStatus.orderObject.orderName = '视频课购买'
  217. orderStatus.orderObject.orderDesc = '视频课购买'
  218. orderStatus.orderObject.actualPrice = userInfo.lessonPrice
  219. orderStatus.orderObject.recomUserId = ''
  220. orderStatus.orderObject.orderNo = ''
  221. orderStatus.orderObject.orderList = [
  222. {
  223. orderType: 'VIDEO',
  224. goodsName: '视频课购买',
  225. courseGroupId: userInfo.id,
  226. courseGroupName: userInfo.lessonName,
  227. coursePrice: userInfo.lessonPrice,
  228. teacherName:
  229. userInfo.username ||
  230. `游客${userInfo.teacherId || ''}`,
  231. teacherId: userInfo.teacherId,
  232. avatar: userInfo.headUrl,
  233. relationType: userInfo.relationType,
  234. courseInfo: this.detailList,
  235. recomUserId: ''
  236. }
  237. ]
  238. await onSubmitZero(() => {
  239. Dialog.alert({
  240. message: '领取成功',
  241. confirmButtonText: '确定',
  242. confirmButtonColor: '#2dc7aa'
  243. }).then(() => {
  244. this.__init()
  245. })
  246. })
  247. } catch {
  248. //
  249. }
  250. }}
  251. />
  252. )
  253. }}
  254. />
  255. {/* </Sticky> */}
  256. <Cell
  257. border={false}
  258. class={styles.cell}
  259. title={this.title}
  260. titleClass={[styles.titleInfo, 'van-ellipsis']}
  261. v-slots={{
  262. icon: () => (
  263. <Icon
  264. name="video"
  265. size={18}
  266. color="var(--van-primary)"
  267. style={{ display: 'flex', alignItems: 'center' }}
  268. />
  269. ),
  270. value: () => (
  271. <div class={styles.label}>
  272. <span>{this.currentClassIndex}</span>/{this.detailList.length}
  273. 课时
  274. </div>
  275. )
  276. }}
  277. ></Cell>
  278. <div class={styles.videoDesc}>{this.videoContent}</div>
  279. <Tabs
  280. v-model:active={this.tabIndex}
  281. class={styles.infoField}
  282. color="var(--van-primary)"
  283. sticky
  284. offsetTop={this.offsetTop}
  285. lineWidth={20}
  286. >
  287. <Tab title="目录" name={1}>
  288. <div
  289. style={{
  290. // height: `calc(100vh - 320px - ${this.navHeight}px)`,
  291. overflowY: 'auto'
  292. }}
  293. >
  294. <SectionDetail title="课程列表" icon="courseList" border>
  295. {this.detailList.map((item: any, index: number) => {
  296. const musicAlbumInfos = item.musicAlbumInfos || []
  297. const temp = musicAlbumInfos.map((info: any) => {
  298. return {
  299. relationMusicAlbum: info.relationType,
  300. musicAlbumName: info.name,
  301. musicAlbumId: info.musicAlbumId,
  302. status: info.status,
  303. useRelationType: this.useRelationType
  304. }
  305. })
  306. return (
  307. <CourseVideoItem
  308. musicAlbumInfos={temp}
  309. playId={Number(this.classId)}
  310. detail={{
  311. id: item.id,
  312. title: item.videoTitle,
  313. content: item.videoContent,
  314. imgUrl: item.coverUrl,
  315. videoUrl: item.videoUrl,
  316. index: index + 1
  317. }}
  318. onPlay={this.onPlay}
  319. onMusicAlbumDetail={(item: any) => {
  320. if (!this.alreadyBuy && !item.status) {
  321. Toast('数据正在更新,请稍后再试')
  322. return
  323. }
  324. if (item.relationMusicAlbum === 'MUSIC') {
  325. this.$router.push({
  326. path: '/music-detail',
  327. query: {
  328. id: item.musicAlbumId
  329. }
  330. })
  331. } else if (item.relationMusicAlbum === 'ALBUM') {
  332. this.$router.push({
  333. path: '/music-album-detail/' + item.musicAlbumId
  334. })
  335. }
  336. }}
  337. />
  338. )
  339. })}
  340. </SectionDetail>
  341. </div>
  342. </Tab>
  343. <Tab title="讨论" name={3}>
  344. <div
  345. style={{
  346. // height: `calc(100vh - 380px - ${this.navHeight}px)`,
  347. overflowY: 'auto',
  348. marginBottom:
  349. 'calc(var(--van-cell-vertical-padding) * 2 + var( --van-cell-line-height))'
  350. }}
  351. >
  352. {this.dataShow ? (
  353. <List
  354. v-model:loading={this.loading}
  355. finished={this.finished}
  356. finishedText=" "
  357. immediateCheck={false}
  358. onLoad={this.getList}
  359. >
  360. {this.list.map((item: any) => (
  361. <Cell
  362. class={[
  363. styles['message-list'],
  364. item.isTeacher === 1 && styles['message-active']
  365. ]}
  366. valueClass={styles['message-time']}
  367. v-slots={{
  368. icon: () => (
  369. <Image
  370. class={styles.userLogo}
  371. src={item.avatar || iconTeacher}
  372. fit="cover"
  373. />
  374. ),
  375. title: () => (
  376. <div class={styles.title}>
  377. <div class={styles['message-name']}>
  378. {item.userName}
  379. </div>
  380. <div class={styles['message-time']}>
  381. {dayjs(item.evaluateTime).format(
  382. 'YYYY年MM月DD日'
  383. )}
  384. </div>
  385. </div>
  386. ),
  387. label: () => (
  388. <div class={styles.label}>{item.content}</div>
  389. )
  390. }}
  391. />
  392. ))}
  393. </List>
  394. ) : (
  395. <ColResult btnStatus={false} tips="暂无讨论" />
  396. )}
  397. </div>
  398. {/* <Sticky offsetBottom={0} position="bottom"> */}
  399. <div class={styles.messageContainer}>
  400. <Field
  401. placeholder="快来讨论吧~"
  402. v-model={this.message}
  403. v-slots={{
  404. button: () => (
  405. <Button
  406. type="primary"
  407. disabled={!this.message}
  408. style={{ padding: '0 20px' }}
  409. size="small"
  410. round
  411. onClick={this.onSubmit}
  412. >
  413. 发布
  414. </Button>
  415. )
  416. }}
  417. />
  418. </div>
  419. {/* </Sticky> */}
  420. </Tab>
  421. </Tabs>
  422. </div>
  423. )
  424. }
  425. })