video-class-detail.tsx 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  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. } from 'vant'
  19. import { defineComponent } from 'vue'
  20. import styles from './video-class-detail.module.less'
  21. import { state } from '@/state'
  22. import iconStudent from '@common/images/icon_student.png'
  23. import dayjs from 'dayjs'
  24. import ColResult from '@/components/col-result'
  25. export default defineComponent({
  26. name: 'VideoClassDetail',
  27. data() {
  28. const query = this.$route.query
  29. return {
  30. groupId: query.groupId,
  31. classId: query.classId,
  32. tabIndex: 1,
  33. title: '',
  34. lessonPrice: 0,
  35. currentClassIndex: 1,
  36. detailList: [],
  37. posterUrl: '',
  38. srcUrl: '',
  39. message: '',
  40. navHeight: 0 as any,
  41. reload: false,
  42. videoContent: '',
  43. list: [],
  44. dataShow: true, // 判断是否有数据
  45. loading: false,
  46. finished: false,
  47. params: {
  48. page: 1,
  49. rows: 20
  50. },
  51. freeRate: 0, // 试看百分比
  52. trySee: false, // 是否试看
  53. videoHeight: '212px'
  54. }
  55. },
  56. computed: {
  57. users() {
  58. return state.user.data
  59. },
  60. offsetTop() {
  61. const navHeight: number = this.navHeight
  62. const top = Number(navHeight) + 44
  63. return top + 'px'
  64. }
  65. },
  66. async mounted() {
  67. this.navHeight = sessionStorage.getItem('navHeight') || 0
  68. try {
  69. const res = await request.get(
  70. '/api-teacher/videoLessonGroup/selectVideoLesson',
  71. {
  72. params: {
  73. groupId: this.groupId
  74. }
  75. }
  76. )
  77. const result = res.data || {}
  78. this.title = result.lessonGroup.lessonName
  79. this.lessonPrice = result.lessonGroup.lessonPrice
  80. this.detailList = result.detailList || []
  81. if (state.user.data?.userId !== result.lessonGroup.teacherId) {
  82. this.trySee = !result.alreadyBuy
  83. }
  84. this.detailList.forEach((item: any, index: number) => {
  85. if (item.id === Number(this.classId)) {
  86. this.posterUrl = item.coverUrl
  87. this.srcUrl = item.videoUrl
  88. this.title = item.videoTitle
  89. this.currentClassIndex = index + 1
  90. this.videoContent = item.videoContent
  91. }
  92. })
  93. const config = await request.get(
  94. '/api-student/sysConfig/queryByParamNameList',
  95. {
  96. params: {
  97. paramNames: 'video_lesson_free_rate'
  98. }
  99. }
  100. )
  101. this.freeRate = config.data[0]?.paramValue || 0
  102. this.getList()
  103. } catch {}
  104. },
  105. methods: {
  106. onSearch() {
  107. this.params.page = 1
  108. this.list = []
  109. this.dataShow = true // 判断是否有数据
  110. this.loading = false
  111. this.finished = false
  112. this.getList()
  113. },
  114. async getList() {
  115. try {
  116. const params = this.params
  117. const res = await request.post('/api-student/videoLesson/page', {
  118. data: {
  119. ...params,
  120. videoId: this.classId
  121. }
  122. })
  123. this.loading = false
  124. const result = res.data || {}
  125. console.log(result)
  126. // 处理重复请求数据
  127. if (this.list.length > 0 && result.pageNo === 1) {
  128. return
  129. }
  130. this.list = this.list.concat(result.rows || [])
  131. this.finished = result.pageNo >= result.totalPage
  132. this.params.page = result.pageNo + 1
  133. this.dataShow = this.list.length > 0
  134. } catch {
  135. this.dataShow = false
  136. this.finished = true
  137. }
  138. },
  139. onPlay(item: any) {
  140. // 判断是否点击的是当前播放的视频
  141. if (item.id === Number(this.classId)) {
  142. return
  143. }
  144. this.reload = true
  145. this.posterUrl = item.imgUrl
  146. this.srcUrl = item.videoUrl
  147. this.title = item.title
  148. this.currentClassIndex = item.index
  149. this.videoContent = item.content
  150. this.classId = item.id
  151. this.onSearch()
  152. setTimeout(() => {
  153. this.reload = false
  154. }, 0)
  155. },
  156. async onSubmit() {
  157. try {
  158. await request.post('/api-teacher/videoLesson/evaluate', {
  159. data: {
  160. isTeacher: 1,
  161. videoId: this.classId,
  162. content: this.message,
  163. studentId: state.user.data.userId
  164. }
  165. })
  166. Toast('评论成功')
  167. this.message = ''
  168. setTimeout(() => {
  169. this.onSearch()
  170. }, 1000)
  171. } catch {}
  172. }
  173. },
  174. render() {
  175. return (
  176. <div class={styles['video-class-detail']}>
  177. {/* <ColHeader
  178. v-slots={{
  179. default: () => (
  180. <ColVideo
  181. src={this.srcUrl}
  182. poster={this.posterUrl}
  183. freeRate={Number(this.freeRate)}
  184. trySee={this.trySee}
  185. height={this.videoHeight}
  186. />
  187. )
  188. }}
  189. /> */}
  190. <ColVideo
  191. src={this.srcUrl}
  192. poster={this.posterUrl}
  193. freeTitleStatus={this.lessonPrice > 0 ? true : false}
  194. freeRate={Number(this.freeRate)}
  195. trySee={this.trySee}
  196. height={this.videoHeight}
  197. />
  198. <Cell
  199. border={false}
  200. class={styles.cell}
  201. title={this.title}
  202. titleClass={styles.titleInfo}
  203. v-slots={{
  204. icon: () => (
  205. <Icon
  206. name="video"
  207. size={18}
  208. color="var(--van-primary)"
  209. style={{ display: 'flex', alignItems: 'center' }}
  210. />
  211. ),
  212. value: () => (
  213. <div class={styles.label}>
  214. <span>{this.currentClassIndex}</span>/{this.detailList.length}
  215. 课时
  216. </div>
  217. )
  218. }}
  219. ></Cell>
  220. <div class={styles.videoDesc}>{this.videoContent}</div>
  221. <Tabs
  222. v-model:active={this.tabIndex}
  223. class={styles.infoField}
  224. color="var(--van-primary)"
  225. lineWidth={20}
  226. sticky
  227. lazyRender
  228. offsetTop={this.offsetTop}
  229. >
  230. <Tab title="目录" name={1}>
  231. <div style={{ height: 'calc(100vh - 320px)', overflowY: 'auto' }}>
  232. <SectionDetail title="课程列表" icon="courseList">
  233. {this.detailList.map((item: any, index: number) => (
  234. <CourseVideoItem
  235. class={'mb12'}
  236. playId={Number(this.classId)}
  237. detail={{
  238. id: item.id,
  239. title: item.videoTitle,
  240. content: item.videoContent,
  241. imgUrl: item.coverUrl,
  242. videoUrl: item.videoUrl,
  243. index: index + 1
  244. }}
  245. onPlay={this.onPlay}
  246. />
  247. ))}
  248. </SectionDetail>
  249. </div>
  250. </Tab>
  251. <Tab title="讨论" name={3}>
  252. <div
  253. style={{
  254. overflowY: 'auto',
  255. marginBottom:
  256. 'calc(var(--van-cell-vertical-padding) * 2 + var( --van-cell-line-height))'
  257. }}
  258. >
  259. {this.dataShow ? (
  260. <List
  261. v-model:loading={this.loading}
  262. finished={this.finished}
  263. finishedText=" "
  264. immediateCheck={false}
  265. onLoad={this.getList}
  266. >
  267. {this.list.map((item: any) => (
  268. <Cell
  269. class={[
  270. styles['message-list'],
  271. item.isTeacher === 1 && styles['message-active']
  272. ]}
  273. valueClass={styles['message-time']}
  274. v-slots={{
  275. icon: () => (
  276. <Image
  277. class={styles.userLogo}
  278. src={item.avatar || iconStudent}
  279. fit="cover"
  280. />
  281. ),
  282. title: () => (
  283. <div class={styles.title}>
  284. <div class={styles['message-name']}>
  285. {item.userName}
  286. </div>
  287. <div class={styles['message-time']}>
  288. {dayjs(item.evaluateTime).format(
  289. 'YYYY年MM月DD日'
  290. )}
  291. </div>
  292. </div>
  293. ),
  294. label: () => (
  295. <div class={styles.label}>{item.content}</div>
  296. )
  297. }}
  298. />
  299. ))}
  300. </List>
  301. ) : (
  302. <ColResult btnStatus={false} tips="暂无讨论" />
  303. )}
  304. </div>
  305. <div class={[styles.messageContainer]}>
  306. <Field
  307. placeholder="快来讨论吧~"
  308. v-model={this.message}
  309. v-slots={{
  310. button: () => (
  311. <Button
  312. type="primary"
  313. disabled={!this.message}
  314. style={{ padding: '0 20px' }}
  315. size="small"
  316. round
  317. onClick={this.onSubmit}
  318. >
  319. 发布
  320. </Button>
  321. )
  322. }}
  323. />
  324. </div>
  325. </Tab>
  326. </Tabs>
  327. </div>
  328. )
  329. }
  330. })