index.vue 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. <template>
  2. <view class="container">
  3. <web-view ref="webviewRef" :src="pageUrl" v-if="pageUrl" update-title allow="fullscreen" @message="handleMessage"></web-view>
  4. <image src="@/static/images/course_expiration_img.png" mode="aspectFill"></image>
  5. <view>{{msg}}</view>
  6. </view>
  7. </template>
  8. <script>
  9. import {getRealLink,updateSopAppLink} from "@/api/courseAnswer.js"
  10. //import {getUserLiveInfo} from '@/api/living.js'
  11. export default {
  12. data() {
  13. return {
  14. pageUrl:"",
  15. msg: '加载中...',
  16. userInfo: {},
  17. currentLiveId: null,
  18. currentCompanyId: null,
  19. currentCompanyUserId: null
  20. }
  21. },
  22. onLoad(option) {
  23. this.userInfo = uni.getStorageSync("userInfo") ? JSON.parse(uni.getStorageSync("userInfo")) : {};
  24. //console.log('this.liveUserId',this.liveUserId)
  25. //this.getUserLiveInfo()
  26. // const userId = this.userInfo.userId || ''
  27. const liveId = option.liveId
  28. const companyId = option.companyId
  29. const companyUserId = option.companyUserId
  30. this.currentLiveId = liveId;
  31. this.currentCompanyId = companyId;
  32. this.currentCompanyUserId = companyUserId;
  33. if (liveId) {
  34. const baseUrl ="https://h5vips.cdwjyyh.com/appweb/pages_live/living?liveId="+liveId+"&source=app"
  35. //const baseUrl ="https://yjf.natappvip.cc/appweb/pages_live/living?liveId="+liveId+"&source=app"
  36. //const baseUrl = "http://localhost:80/appweb/pages_live/living?liveId="+liveId+"&source=app"
  37. // 用一个集合传参到 living(web-view 内 H5 与原生 storage 隔离)。断点续播规则由 H5 liveVideo 实现:首轮结束后有存储进度即续播,不要求先看完第一遍;本页只传 videoCurrentTime/firstPassWatched 供 H5 恢复
  38. const rawUserInfo = uni.getStorageSync('userInfo')
  39. const rawUserData = uni.getStorageSync('userData');
  40. const payload = {
  41. companyId:companyId,
  42. companyUserId:companyUserId,
  43. appToken: uni.getStorageSync('AppToken') || '',
  44. liveToken: uni.getStorageSync('liveToken') || '',
  45. videoCurrentTime:uni.getStorageSync('videoProgress_'+liveId),
  46. firstPassWatched:uni.getStorageSync('firstPassWatched_'+liveId),
  47. startTime:uni.getStorageSync('videoProgressSeek_startTime_'+liveId),
  48. duration:uni.getStorageSync('videoProgressSeek_duration_'+liveId),
  49. // 首次进入 H5 时 web-view 与原生 storage 隔离,需通过 payload 传入用户信息,否则无法初始化 WebSocket
  50. // userInfo: rawUserInfo,
  51. // userData: rawUserData
  52. }
  53. console.log("event===videoCurrentTime===",uni.getStorageSync('videoProgress_'+liveId))
  54. console.log("event===videoProgressSeek_startTime_===",uni.getStorageSync('videoProgressSeek_startTime_'+liveId))
  55. // 使用双重 encodeURIComponent 避免 web-view 内部自动解码导致含有空格等特殊字符时被截断
  56. this.pageUrl = baseUrl + '&payload=' + encodeURIComponent(encodeURIComponent(JSON.stringify(payload))) + '&t=' + Date.now();
  57. console.log("qxj pageUrl",this.pageUrl);
  58. //this.getLink(link)
  59. //this.getCourseRead(link)
  60. } else {
  61. this.msg="缺少直播Id"
  62. uni.showToast({
  63. title: "缺少直播Id",
  64. icon: 'error'
  65. })
  66. }
  67. },
  68. onUnload() {
  69. // #ifdef APP-PLUS
  70. // plus.navigator.setFullscreen(false);
  71. // plus.screen.lockOrientation('portrait-primary');
  72. // #endif
  73. },
  74. onBackPress() {
  75. // #ifdef APP-PLUS
  76. // 返回时退出全屏
  77. // plus.navigator.setFullscreen(false);
  78. // plus.screen.lockOrientation('portrait-primary');
  79. // #endif
  80. },
  81. methods: {
  82. getUserLiveInfo(){
  83. getUserLiveInfo().then((res) => {
  84. if (res.code == 200) {
  85. const liveUserId=res.user.userId
  86. uni.setStorageSync('liveUserId', liveUserId)
  87. //console.log('liveUser', res.user)
  88. } else {
  89. uni.showToast({
  90. icon: 'none',
  91. title: '请求失败'
  92. })
  93. }
  94. },
  95. (rej) => {}
  96. )
  97. },
  98. getCourseRead(id) {
  99. updateSopAppLink(id).then(res=>{
  100. if(res.code == 200) {
  101. this.$updateMsgDot()
  102. }
  103. })
  104. },
  105. handleMessage(event) {
  106. //console.log('点击事件',event)
  107. const info = event.detail.data[0]
  108. // 响应 H5 页面发起的 payload 请求
  109. if (info && info.action === 'requestPayload') {
  110. console.log('收到 H5 的 payload 请求');
  111. const liveId = this.currentLiveId;
  112. const payload = {
  113. companyId: this.currentCompanyId,
  114. companyUserId: this.currentCompanyUserId,
  115. appToken: uni.getStorageSync('AppToken') || '',
  116. liveToken: uni.getStorageSync('liveToken') || '',
  117. videoCurrentTime: uni.getStorageSync('videoProgress_'+liveId),
  118. firstPassWatched: uni.getStorageSync('firstPassWatched_'+liveId),
  119. startTime: uni.getStorageSync('videoProgressSeek_startTime_'+liveId),
  120. duration: uni.getStorageSync('videoProgressSeek_duration_'+liveId),
  121. userInfo: uni.getStorageSync('userInfo'),
  122. userData: uni.getStorageSync('userData')
  123. };
  124. // #ifdef APP-PLUS
  125. const currentWebview = this.$mp.page.$getAppWebview();
  126. const wv = currentWebview.children()[0];
  127. if (wv) {
  128. // 使用 evalJS 将数据直接注入到 H5 的 window 对象中
  129. wv.evalJS(`if(window.receivePayloadFromApp) { window.receivePayloadFromApp(${JSON.stringify(payload)}); }`);
  130. }
  131. // #endif
  132. return;
  133. }
  134. // #ifdef APP-PLUS
  135. // if(info.login != 1) {
  136. // // 退出登录
  137. // let IMUserID = uni.getStorageSync('IMUserID');
  138. // //Igexin.unbindAlias(IMUserID);
  139. // uni.removeStorage({key: "IMUserID"});
  140. // uni.setStorageSync("AppToken",null);
  141. // uni.setStorageSync("liveToken",null);
  142. // uni.removeStorage({ key: 'IMToken' });
  143. // uni.removeStorageSync("onLaunch",null)
  144. // uni.removeStorageSync("imUnread",null);
  145. // uni.removeStorageSync("companyUser");
  146. // uni.removeStorageSync("CompanyUserToken");
  147. // uni.$emit("refreshUserInfo");
  148. // let pages = getCurrentPages();
  149. // let url = pages[ pages.length - 1]; //当前页页面实例
  150. // //如果登录界面已打开,自动关闭
  151. // if(url!=undefined&&url.route=="pages/auth/loginIndex"){
  152. // return;
  153. // }
  154. // uni.navigateTo({
  155. // url: '/pages/auth/loginIndex'
  156. // });
  157. // return
  158. // }
  159. // if (info&&info.isFullscreen) {
  160. // plus.screen.lockOrientation('landscape-primary');
  161. // } else {
  162. // plus.screen.lockOrientation('portrait-primary');
  163. // }
  164. //立即购买
  165. if(info&&info.pagesUrl) {
  166. if(info.isBack){
  167. uni.navigateBack()
  168. }else{
  169. console.log('+++++++++++++++++++++++++++++')
  170. uni.navigateTo({
  171. url: info.pagesUrl
  172. })
  173. }
  174. }
  175. if(info&&info.liveId) {
  176. // 断点续播:H5 传来进度就持久化(播放中每 10 秒、退出时 sync、设置节点时 seek)。续播规则在 H5:首轮结束后有进度即续播,不要求先看完第一遍
  177. if (info.videoProgress !== undefined && info.videoProgress !== null && info.videoProgress !== '') {
  178. uni.setStorageSync('videoProgress_'+info.liveId, info.videoProgress)
  179. }
  180. if(info.firstPassWatched){
  181. uni.setStorageSync('firstPassWatched_'+info.liveId, true)
  182. }
  183. // 录播设置播放节点时的附加信息(可选,供原生展示或下次传参)
  184. if (info.videoProgressSeek) {
  185. if (info.isLiveSync === true && info.startTime != null) {
  186. console.log('videoProgressSeek_startTime_'+info.liveId, info.startTime)
  187. uni.setStorageSync('videoProgressSeek_startTime_'+info.liveId, info.startTime)
  188. }
  189. if (info.duration != null) {
  190. uni.setStorageSync('videoProgressSeek_duration_'+info.liveId, info.duration)
  191. }
  192. }
  193. }
  194. // //
  195. // if(info&&info.productId) {
  196. // uni.navigateTo({
  197. // url: info.pagesUrl
  198. // })
  199. // }
  200. // console.log("event===info===",info)
  201. // if(info&&info.pagesUrl) {
  202. // uni.navigateTo({
  203. // url: info.pagesUrl
  204. // })
  205. // }
  206. // #endif
  207. },
  208. getLink(sortLink) {
  209. this.msg = '加载中...'
  210. const userId = this.userInfo.userId || ''
  211. getRealLink({sortLink: sortLink}).then(res => {
  212. if (res.code == 200) {
  213. // 如果响应中包含真实链接,则跳转到真实链接
  214. let pageUrl= res.realLink + "&userId="+userId+"&source=app&time=" + new Date().getTime();
  215. this.pageUrl = pageUrl;
  216. } else {
  217. this.msg = '课程已过期或链接无效'
  218. uni.showModal({
  219. title: '提示',
  220. content: '课程已过期或链接无效',
  221. showCancel: false,
  222. success: function(res) {
  223. if (res.confirm) {
  224. console.log('用户点击确定');
  225. } else if (res.cancel) {
  226. console.log('用户点击取消');
  227. }
  228. }
  229. });
  230. }
  231. }).catch(err => {
  232. this.msg = '发生错误,请稍后再试'
  233. uni.showToast({
  234. title: '发生错误,请稍后再试',
  235. icon: 'none'
  236. });
  237. })
  238. }
  239. }
  240. }
  241. </script>
  242. <style scoped lang="scss">
  243. .container {
  244. display: flex;
  245. flex-direction: column;
  246. align-items: center;
  247. padding-bottom: 88rpx;
  248. justify-content: center;
  249. font-family: PingFang SC, PingFang SC;
  250. font-weight: 400;
  251. font-size: 32rpx;
  252. color: #757575;
  253. line-height: 48rpx;
  254. text-align: center;
  255. image {
  256. width: 428rpx;
  257. height: 360rpx;
  258. margin-bottom: 30rpx;
  259. }
  260. }
  261. </style>