live-detail.tsx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. import CoursePlanStep from '@/business-components/course-plan-step'
  2. import SectionDetail from '@/business-components/section-detail'
  3. import UserDetail from '@/business-components/user-detail'
  4. import request from '@/helpers/request'
  5. import dayjs from 'dayjs'
  6. import { Icon, Sticky, Button, Dialog, Toast, Popup } from 'vant'
  7. import { defineComponent } from 'vue'
  8. import styles from './live-detail.module.less'
  9. import iconTips from '@common/images/icon_tips.png'
  10. import { onSubmitZero, orderStatus } from '@/views/order-detail/orderStatus'
  11. import ColHeader from '@/components/col-header'
  12. import { postMessage } from '@/helpers/native-message'
  13. import ColSticky from '@/components/col-sticky'
  14. import ColShare from '@/components/col-share'
  15. import LiveItem from '@/views/live-class/live-item'
  16. import iconShare from '@/views/shop-mall/images/icon-share.svg'
  17. import { state } from '@/state'
  18. interface IProps {
  19. courseTime: string
  20. coursePlan: string
  21. videoPosterUrl?: string
  22. roomUid?: string
  23. liveState?: number
  24. id?: number | string
  25. }
  26. export default defineComponent({
  27. name: 'LiveDetail',
  28. data() {
  29. const query = this.$route.query
  30. return {
  31. joinRoom: query.joinRoom, // 原生传递过来的参数,判断是否进入直播间
  32. recomUserId: query.recomUserId, // 推荐人id
  33. groupId: query.groupId,
  34. courseId: query.classId,
  35. live: {} as any,
  36. shareStatus: false,
  37. shareUrl: '',
  38. }
  39. },
  40. computed: {
  41. userInfo() {
  42. const live = this.live as any
  43. console.log('live', live)
  44. const planList = live.planList || []
  45. const startTime = planList[0]?.startTime || new Date()
  46. const endTime = planList[0]?.endTime || new Date()
  47. return {
  48. avatar: live.avatar,
  49. headUrl: live.avatar,
  50. username: live.userName || `游客${live.teacherId || ''}`,
  51. id: live.teacherId,
  52. startTime:
  53. `${dayjs(startTime).format('YYYY-MM-DD')} ${dayjs(startTime).format(
  54. 'HH:mm'
  55. )}~${dayjs(endTime).format('HH:mm')}` || '',
  56. buyNum: live.studentCount,
  57. lessonId: live.courseGroupId,
  58. lessonPrice: live.coursePrice,
  59. lessonNum: live.courseNum,
  60. lessonDesc: live.courseIntroduce,
  61. lessonCoverUrl: live.backgroundPic || live.backgroundPicTemplate,
  62. lessonName: live.courseGroupName,
  63. subjectName: live.subjectName,
  64. courseStartTime: live.courseStartTime,
  65. auditVersion: live.auditVersion || 0,
  66. isDegree: live.degreeFlag ? true : false,
  67. isTeacher: live.teacherFlag ? true : false
  68. }
  69. },
  70. courseInfo() {
  71. const tempArr = [] as IProps[]
  72. const coursePlanList = this.live.planList || []
  73. coursePlanList.forEach((item: any) => {
  74. const startTime = item.startTime || new Date()
  75. const endTime = item.endTime || new Date()
  76. tempArr.push({
  77. courseTime: `${dayjs(startTime).format('YYYY-MM-DD')} ${dayjs(
  78. startTime
  79. ).format('HH:mm')}~${dayjs(endTime).format('HH:mm')}`,
  80. coursePlan: item.plan,
  81. roomUid: item.roomUid,
  82. liveState: item.liveState,
  83. id: item.courseId
  84. })
  85. })
  86. return tempArr || []
  87. },
  88. salesEndDate() {
  89. const live = this.live as any
  90. return dayjs(live.salesEndDate || new Date()).format('YYYY-MM-DD')
  91. },
  92. liveStatus() {
  93. const coursePlanList = this.live.planList || []
  94. const tempObj = {
  95. status: false,
  96. liveStatus: 0,
  97. roomUid: ''
  98. }
  99. coursePlanList.forEach((item: any) => {
  100. if (item.courseId === Number(this.courseId)) {
  101. tempObj.status = true
  102. tempObj.liveStatus = item.liveStatus
  103. tempObj.roomUid = item.roomUid
  104. }
  105. })
  106. return tempObj
  107. }
  108. },
  109. async mounted() {
  110. await this._init()
  111. if (/(localhost|192)/g.test(location.origin)) {
  112. this.shareUrl = `https://dev.colexiu.com/teacher/#/shareLive?recomUserId=${state.user.data?.userId}&groupId=${this.groupId}`
  113. } else {
  114. this.shareUrl = `${location.origin}/teacher/#/shareLive?recomUserId=${state.user.data?.userId}&groupId=${this.groupId}`
  115. }
  116. },
  117. methods: {
  118. async _init() {
  119. try {
  120. const res = await request.get(
  121. '/api-student/courseGroup/queryLiveCourseInfo',
  122. {
  123. params: {
  124. groupId: this.groupId
  125. }
  126. }
  127. )
  128. this.live = res.data || {}
  129. } catch {}
  130. },
  131. async onJoinRoom() {
  132. try {
  133. const res = await request.get(
  134. '/api-student/courseGroup/queryLiveCourseInfo',
  135. {
  136. params: {
  137. groupId: this.groupId
  138. }
  139. }
  140. )
  141. const result = res.data || {}
  142. const coursePlanList = result.planList || []
  143. let tempObj: any = {}
  144. coursePlanList.forEach((item: any) => {
  145. if (item.courseId === Number(this.courseId)) {
  146. tempObj = item
  147. }
  148. })
  149. console.log(tempObj, this.live, 'tempObj')
  150. if (tempObj && tempObj.liveState === 1) {
  151. postMessage({
  152. api: 'joinLiveRoom',
  153. content: {
  154. roomId: tempObj.roomUid,
  155. teacherId: this.live.teacherId
  156. }
  157. })
  158. } else if (tempObj && tempObj.liveState === 2) {
  159. setTimeout(() => {
  160. Toast('课程已结束')
  161. }, 100)
  162. } else {
  163. setTimeout(() => {
  164. Toast('课程尚未开始,请耐心等候')
  165. }, 100)
  166. }
  167. } catch {}
  168. },
  169. async onBuy() {
  170. try {
  171. const live = this.live
  172. orderStatus.orderObject.orderType = 'LIVE'
  173. orderStatus.orderObject.orderName = '直播课购买'
  174. orderStatus.orderObject.orderDesc = '直播课购买'
  175. orderStatus.orderObject.actualPrice = live.coursePrice
  176. orderStatus.orderObject.recomUserId = this.recomUserId
  177. orderStatus.orderObject.orderNo = ''
  178. orderStatus.orderObject.orderList = [
  179. {
  180. orderType: 'LIVE',
  181. goodsName: '直播课购买',
  182. courseGroupId: live.courseGroupId,
  183. courseGroupName: live.courseGroupName,
  184. coursePrice: live.coursePrice,
  185. teacherName: live.userName || `游客${live.teacherId || ''}`,
  186. teacherId: live.teacherId,
  187. avatar: live.avatar,
  188. courseInfo: this.courseInfo,
  189. recomUserId: this.recomUserId
  190. }
  191. ]
  192. // 判断是否是0无订单
  193. if (live.coursePrice <= 0) {
  194. await onSubmitZero(() => {
  195. Dialog.alert({
  196. message: '领取成功',
  197. confirmButtonText: '确定',
  198. confirmButtonColor: '#2dc7aa'
  199. }).then(() => {
  200. this._init()
  201. })
  202. })
  203. return
  204. }
  205. const res = await request.post(
  206. '/api-student/userOrder/getPendingOrder',
  207. {
  208. data: {
  209. goodType: 'LIVE',
  210. bizId: this.groupId
  211. }
  212. }
  213. )
  214. const result = res.data
  215. if (result) {
  216. Dialog.confirm({
  217. title: '提示',
  218. message: '您有一个未支付的订单,是否继续支付?',
  219. confirmButtonColor: '#269a93',
  220. cancelButtonText: '取消订单',
  221. confirmButtonText: '继续支付'
  222. })
  223. .then(async () => {
  224. orderStatus.orderObject.orderNo = result.orderNo
  225. orderStatus.orderObject.actualPrice = result.actualPrice
  226. orderStatus.orderObject.discountPrice = result.discountPrice
  227. this.routerTo()
  228. })
  229. .catch(() => {
  230. Dialog.close()
  231. // 只用取消订单,不用做其它处理
  232. this.cancelPayment(result.orderNo)
  233. })
  234. } else {
  235. this.routerTo()
  236. }
  237. } catch {
  238. //
  239. }
  240. },
  241. routerTo() {
  242. const live = this.live
  243. this.$router.push({
  244. path: '/orderDetail',
  245. query: {
  246. orderType: 'LIVE',
  247. courseGroupId: live.courseGroupId
  248. }
  249. })
  250. },
  251. async cancelPayment(orderNo: string) {
  252. try {
  253. await request.post('/api-student/userOrder/orderCancel', {
  254. data: {
  255. orderNo
  256. }
  257. })
  258. // this.routerTo()
  259. } catch {}
  260. }
  261. },
  262. render() {
  263. return (
  264. <div class={[styles['live-detail'], 'mb12']}>
  265. <ColHeader v-slots={{
  266. right: () => (
  267. <img src={iconShare} onClick={() => this.shareStatus = true} />
  268. )
  269. }} />
  270. <UserDetail userInfo={this.userInfo} showBuy={false} />
  271. <SectionDetail border>
  272. <p class={styles.introduction}>{this.userInfo.lessonDesc}</p>
  273. </SectionDetail>
  274. <SectionDetail
  275. title="课程列表"
  276. icon="courseList"
  277. border
  278. contentStyle={{ paddingTop: '0' }}
  279. >
  280. {this.courseInfo.length > 0 && (
  281. <CoursePlanStep
  282. courseInfo={this.courseInfo}
  283. courseId={Number(this.courseId) || 0}
  284. />
  285. )}
  286. </SectionDetail>
  287. <div class={styles.tips}>
  288. <h3>
  289. <Icon name={iconTips} size={15} />
  290. 温馨提示
  291. </h3>
  292. <p>
  293. 1、该直播课程销售截止后,报名人数若少于
  294. {this.live.mixStudentNum || 0}
  295. 人将取消开课,已购买学员付费金额将自动返还,请您放心购买;
  296. <br />
  297. 2、直播课教学计划中的上课时间为老师预计时间,实际上课时间以老师开启直播时间为准;
  298. <br />
  299. 3、若您错过老师直播,可通过视频回放观看完整课程。
  300. </p>
  301. </div>
  302. {this.courseInfo.length > 0 && this.live.existBuy !== 1 && (
  303. // <Sticky offsetBottom={0} position="bottom">
  304. // <div class={['btnGroup', styles.btnMore]}>
  305. // <Button block round type="primary" onClick={this.onBuy}>
  306. // {this.live.coursePrice <= 0 ? '免费领取' : `立即购买`}
  307. // </Button>
  308. // </div>
  309. // </Sticky>
  310. <ColSticky position="bottom" background="white">
  311. <div class={['btnGroup', styles.btnMore]}>
  312. <Button block round type="primary" onClick={this.onBuy}>
  313. {this.live.coursePrice <= 0 ? '免费领取' : `立即购买`}
  314. </Button>
  315. </div>
  316. </ColSticky>
  317. )}
  318. {this.joinRoom == '1' && this.liveStatus.liveStatus !== 2 && (
  319. // <Sticky offsetBottom={0} position="bottom">
  320. // <div class={['btnGroup']} style={{ paddingTop: '12px' }}>
  321. // <Button block round type="primary" onClick={this.onJoinRoom}>
  322. // 进入直播间
  323. // </Button>
  324. // </div>
  325. // </Sticky>
  326. <ColSticky position="bottom" background="white">
  327. <div class={['btnGroup']} style={{ paddingTop: '12px' }}>
  328. <Button block round type="primary" onClick={this.onJoinRoom}>
  329. 进入直播间
  330. </Button>
  331. </div>
  332. </ColSticky>
  333. )}
  334. <Popup
  335. v-model:show={this.shareStatus}
  336. style={{ background: 'transparent' }}
  337. >
  338. <ColShare
  339. teacherId={this.userInfo.id}
  340. shareUrl={this.shareUrl}
  341. shareType="live"
  342. >
  343. <LiveItem
  344. class={styles.shareCourse}
  345. liveInfo={{
  346. backgroundPic: this.userInfo.lessonCoverUrl,
  347. courseGroupId: this.userInfo.lessonId,
  348. courseGroupName: this.userInfo.lessonName,
  349. courseNum: this.userInfo.lessonNum,
  350. coursePrice: this.userInfo.lessonPrice,
  351. teacherName: this.userInfo.username,
  352. teacherId: this.userInfo.id,
  353. avatar: this.userInfo.avatar,
  354. studentCount: this.userInfo.buyNum,
  355. courseStartTime: this.userInfo.courseStartTime,
  356. existBuy: 0,
  357. subjectName: this.userInfo.subjectName
  358. }}
  359. />
  360. </ColShare>
  361. </Popup>
  362. </div>
  363. )
  364. }
  365. })