liveDetail.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602
  1. <template>
  2. <view class="container">
  3. <trtc-room ref="trtc-component" :config="rtcConfig"> </trtc-room>
  4. <view class="top_box" :style="{ top: btn_top, left: btn_left}">
  5. <image class="top_box_img" :src="head_img" mode="aspectFill"></image>
  6. <view class="top_box_text">
  7. <text class="text_box_top">{{title}}</text><text class="text_box_bottom" style="display:none">{{user_name}}</text>
  8. <view class="tx_btn" @click="subTap()"><text class="iconfont tixingn">{{tixing_str}}</text></view>
  9. </view>
  10. </view>
  11. <view class="popup-open" @click="showGoods()">
  12. <view class="iconfont iconzhibo-shangpin"></view>
  13. </view>
  14. <uni-popup ref="popup" type="bottom">
  15. <view class="popup-box">
  16. <view class="popup-close" @click="closeGoods()">收起</view>
  17. <scroll-view scroll-y="true" class="good-box">
  18. <view class="goods-row" v-for="(item, index) in goodsList" :key="index" @click="goToGoodDetails(item)">
  19. <image class="goods-img" :src="item.imgPath" mode="aspectFill"></image>
  20. <view class="goods-info">
  21. <view class="goods-name">{{item.productName}}</view>
  22. <view class="goods-sales">{{item.sellCount}}人付款</view>
  23. <view class="goods-number">
  24. <text class="goods-icon">¥</text>
  25. <text class="goods-spec">{{item.bizPrice}}</text>
  26. <text class="price">原价:{{item.originalPrice}}</text>
  27. </view>
  28. </view>
  29. <view class="more-button">
  30. <view class="iconfont icongengduo"></view>
  31. </view>
  32. </view>
  33. </scroll-view>
  34. </view>
  35. </uni-popup>
  36. <u-top-tips ref="uTips"></u-top-tips>
  37. </view>
  38. </template>
  39. <script>
  40. const NET = require('@/utils/request')
  41. const API = require('@/config/api')
  42. import {
  43. genTestUserSig,
  44. setData
  45. } from "@/pagesGood/debug/GenerateTestUserSig";
  46. import trtcRoom from "@/pagesGood/trtc-room/trtc-room";
  47. export default {
  48. components: {
  49. trtcRoom
  50. },
  51. data() {
  52. return {
  53. videoType: '',
  54. roomID: '',
  55. userID: '',
  56. goodsList: [],
  57. rtcConfig: {
  58. sdkAppID: '',
  59. // 必要参数 开通实时音视频服务创建应用后分配的 sdkAppID
  60. userID: '',
  61. // 必要参数 用户 ID 可以由您的帐号系统指定
  62. userSig: '',
  63. // 必要参数 身份签名,相当于登录密码的作用
  64. template: '' // 必要参数 组件模版,支持的值 1v1 grid custom ,注意:不支持动态修改, iOS 不支持 pusher 动态渲染
  65. },
  66. showTipToast: false,
  67. options: {},
  68. btn_left:0,
  69. btn_top:0,
  70. title:"888",
  71. user_name:'1',
  72. head_img:"../static/images/loginLogo.png",
  73. liveId: '',
  74. tixing_str:'直播提醒'
  75. }
  76. },
  77. onLoad(options) {
  78. this.videoType = options.videoType
  79. this.roomID = options.roomId
  80. this.userID = "1323565280579813376"
  81. this.liveId = options.liveId
  82. NET.request(API.isSubscribed + options.liveId, {}, 'GET').then(res => {
  83. if(res.data==false){
  84. this.tixing_str="直播提醒"
  85. }else{
  86. this.tixing_str="取消提醒"
  87. }
  88. //this.goodsList = res.data
  89. }).catch(res => {
  90. })
  91. },
  92. onReady() {
  93. var that=this;
  94. // NET.request(API.getLiveGoodsDetail + options.liveId, {}, 'GET').then(res => {
  95. // this.goodsList = res.data
  96. // }).catch(res => {
  97. // this.$refs.uTips.show({
  98. // title: '获取商品列表失败',
  99. // type: 'warning',
  100. // })
  101. // })
  102. let menuButtonObject = wx.getMenuButtonBoundingClientRect();
  103. wx.getSystemInfo({
  104. success: res => {
  105. this.setData({
  106. btn_left: res.windowWidth - menuButtonObject.right+'rpx',
  107. btn_top:menuButtonObject.top+menuButtonObject.height+14+'rpx',
  108. head_img:uni.getStorageSync("liveImgUrl"),
  109. title:uni.getStorageSync("liveName"),
  110. })
  111. },
  112. })
  113. wx.setKeepScreenOn({
  114. keepScreenOn: true
  115. }); // 获取 rtcroom 实例
  116. this.trtcComponent = this.$refs['trtc-component']; // 监听TRTC Room 关键事件
  117. this.bindTRTCRoomEvent(); // 将String 类型的 true false 转换成 boolean
  118. this.options.localAudio = false; //开启麦克风
  119. this.options.localVideo = false; //开启视频
  120. this.enterRoom({
  121. roomID: Number(this.roomID),
  122. userID: uni.getStorageSync("userData").userId,
  123. template: "grid",
  124. });
  125. },
  126. methods: {
  127. subTap: function() {
  128. let that = this;
  129. if(that.tixing_str=="取消提醒"){
  130. var post_data={liveId:that.liveId,mid:uni.getStorageSync("userData").userId}
  131. post_data.isDelete=0;
  132. NET.request(API.subscribeLive, post_data, 'POST').then(res => {
  133. if(that.tixing_str=="取消提醒"){
  134. that.tixing_str="直播提醒"
  135. }else{
  136. that.tixing_str="取消提醒"
  137. }
  138. //this.goodsList = res.data
  139. }).catch(res => {
  140. })
  141. }else{
  142. wx.requestSubscribeMessage({
  143. tmplIds: ['y0X1cZfbEMg7HdTGN_bW8v7TKeI3M0CHSVTY1zStIXM'],
  144. success(res) {
  145. if(res.y0X1cZfbEMg7HdTGN_bW8v7TKeI3M0CHSVTY1zStIXM=="accept"){
  146. var post_data={liveId:that.liveId,mid:uni.getStorageSync("userData").userId}
  147. if(that.tixing_str=="直播提醒"){
  148. post_data.isDelete=1;
  149. }else{
  150. post_data.isDelete=0;
  151. }
  152. NET.request(API.subscribeLive, post_data, 'POST').then(res => {
  153. if(that.tixing_str=="取消提醒"){
  154. that.tixing_str="直播提醒"
  155. }else{
  156. that.tixing_str="取消提醒"
  157. }
  158. //this.goodsList = res.data
  159. }).catch(res => {
  160. })
  161. }else{
  162. console.log('444', res);
  163. }
  164. //console.log('1111', res);
  165. },
  166. fail(res) {
  167. console.log('2222', res);
  168. }
  169. })
  170. }
  171. },
  172. // 打开弹窗
  173. showGoods() {
  174. this.$refs.popup.open()
  175. NET.request(API.getLiveGoodsDetail + this.liveId, {}, 'GET').then(res => {
  176. this.goodsList = res.data
  177. }).catch(res => {
  178. this.$refs.uTips.show({
  179. title: '获取商品列表失败',
  180. type: 'warning',
  181. })
  182. })
  183. },
  184. // 关闭弹窗
  185. closeGoods() {
  186. this.$refs.popup.close()
  187. },
  188. goToGoodDetails(item) {
  189. uni.navigateTo({
  190. url: '/pagesGood/goodDetails?goodId=' + item.productId
  191. });
  192. },
  193. setData,
  194. enterRoom: function(params) {
  195. params.template = params.template || '1v1';
  196. params.roomID = params.roomID || 2333;
  197. params.userID = params.userID || new Date().getTime().toString(16);
  198. console.log('* room enterRoom', params);
  199. const Signature = genTestUserSig(params.userID);
  200. params.sdkAppID = Signature.sdkAppID;
  201. params.userSig = Signature.userSig;
  202. this.template = params.template;
  203. this.rtcConfig = {
  204. sdkAppID: params.sdkAppID,
  205. // 您的实时音视频服务创建应用后分配的 sdkAppID
  206. userID: params.userID,
  207. userSig: params.userSig,
  208. template: params.template,
  209. // 1v1 grid custom
  210. debugMode: false,
  211. // 非必要参数,打开组件的调试模式,开发调试时建议设置为 true
  212. // cloudenv: params.cloudenv, // 非必要参数
  213. frontCamera: "front",
  214. enableEarMonitor: false,
  215. enableAutoFocus: true,
  216. localMirror: 'auto',
  217. enableAgc: true,
  218. enableAns: true,
  219. enableMic:false,
  220. enableCamera:false,
  221. encsmall: false ? 1 : 0,
  222. videoWidth: 1280,
  223. videoHeight: 720,
  224. scene: "live",
  225. maxBitrate: 2000,
  226. minBitrate: 1500,
  227. beautyLevel: 9 // 默认开启美颜
  228. };
  229. this.setData({
  230. rtcConfig: this.rtcConfig
  231. }, () => {
  232. // roomID 取值范围 1 ~ 4294967295
  233. this.trtcComponent.enterRoom({
  234. roomID: params.roomID
  235. }).then(() => {
  236. if (this.template === 'custom') {
  237. // 设置推流端视窗的坐标和尺寸
  238. this.trtcComponent.setViewRect({
  239. userID: params.userID,
  240. xAxis: '480rpx',
  241. yAxis: '160rpx',
  242. width: '240rpx',
  243. height: '320rpx'
  244. });
  245. }
  246. }).catch(res => {
  247. console.error('* room joinRoom 进房失败:', res);
  248. });
  249. });
  250. },
  251. bindTRTCRoomEvent: function() {
  252. const TRTC_EVENT = this.trtcComponent.EVENT;
  253. this.timestamp = []; // 初始化事件订阅
  254. this.trtcComponent.on(TRTC_EVENT.LOCAL_JOIN, event => {
  255. let user_list=this.trtcComponent.getRemoteUserList();
  256. this.setData({
  257. user_name:user_list.length
  258. })
  259. console.log('* room LOCAL_JOIN', event); // 进房成功,触发该事件后可以对本地视频和音频进行设置
  260. if (this.options.localVideo === true || this.options.template === '1v1') {
  261. //this.trtcComponent.publishLocalVideo();
  262. }
  263. if (this.options.localAudio === true || this.options.template === '1v1') {
  264. //this.trtcComponent.publishLocalAudio();
  265. }
  266. });
  267. this.trtcComponent.on(TRTC_EVENT.LOCAL_LEAVE, event => {
  268. console.log('* room LOCAL_LEAVE', event);
  269. });
  270. this.trtcComponent.on(TRTC_EVENT.ERROR, event => {
  271. console.log('* room ERROR', event);
  272. }); // 远端用户进房
  273. this.trtcComponent.on(TRTC_EVENT.REMOTE_USER_JOIN, event => {
  274. let user_list=this.trtcComponent.getRemoteUserList();
  275. this.setData({
  276. user_name:user_list.length
  277. })
  278. console.log('* room REMOTE_USER_JOIN --- room.vue', event, this.trtcComponent.getRemoteUserList(), this.template);
  279. this.timestamp.push(new Date()); // 1v1视频通话时限制人数为两人的简易逻辑,建议通过后端实现房间人数管理
  280. // 2人以上同时进行通话请选择网格布局
  281. if (this.template === '1v1' && this.timestamp.length > 1) {
  282. const interval = this.timestamp[1] - this.timestamp[0];
  283. if (interval < 1000) {
  284. // 房间里已经有两个人
  285. this.setData({
  286. showTipToast: true
  287. }, () => {
  288. setTimeout(() => {
  289. this.setData({
  290. showTipToast: false
  291. });
  292. wx.navigateBack({
  293. delta: 1
  294. });
  295. }, 4000);
  296. });
  297. }
  298. }
  299. }); // 远端用户退出
  300. this.trtcComponent.on(TRTC_EVENT.REMOTE_USER_LEAVE, event => {
  301. //if (event.data.userID == this.userID) {
  302. console.log("直播用户退出");
  303. this.$refs.uTips.show({
  304. title: '直播用户退出',
  305. type: 'success',
  306. })
  307. setTimeout(() => {
  308. uni.navigateBack()
  309. }, 3000)
  310. //}
  311. let user_list=this.trtcComponent.getRemoteUserList();
  312. this.setData({
  313. user_name:user_list.length
  314. })
  315. }); // 远端用户推送视频
  316. this.trtcComponent.on(TRTC_EVENT.REMOTE_VIDEO_ADD, event => {
  317. console.log('* room REMOTE_VIDEO_ADD', event, this.trtcComponent.getRemoteUserList()); // 订阅视频
  318. const userList = this.trtcComponent.getRemoteUserList();
  319. const data = event.data;
  320. if (this.template === '1v1' && (!this.remoteUser || this.remoteUser === data.userID)) {
  321. // 1v1 只订阅第一个远端流
  322. this.remoteUser = data.userID;
  323. this.trtcComponent.subscribeRemoteVideo({
  324. userID: data.userID,
  325. streamType: data.streamType
  326. });
  327. } else if (this.template === 'grid') {
  328. this.trtcComponent.subscribeRemoteVideo({
  329. userID: data.userID,
  330. streamType: data.streamType
  331. });
  332. }
  333. if (this.template === 'custom' && data.userID && data.streamType) {
  334. let index = userList.findIndex(item => {
  335. return item.userID === data.userID;
  336. });
  337. index++;
  338. const y = 320 * index + 160; // 设置远端视图坐标和尺寸
  339. this.trtcComponent.setViewRect({
  340. userID: data.userID,
  341. streamType: data.streamType,
  342. xAxis: '480rpx',
  343. yAxis: y + 'rpx',
  344. width: '240rpx',
  345. height: '320rpx'
  346. });
  347. }
  348. }); // 远端用户取消推送视频
  349. this.trtcComponent.on(TRTC_EVENT.REMOTE_VIDEO_REMOVE, event => {
  350. console.log('* room REMOTE_VIDEO_REMOVE', event, this.trtcComponent.getRemoteUserList());
  351. }); // 远端用户推送音频
  352. this.trtcComponent.on(TRTC_EVENT.REMOTE_AUDIO_ADD, event => {
  353. console.log('* room REMOTE_AUDIO_ADD', event, this.trtcComponent.getRemoteUserList()); // 订阅音频
  354. const data = event.data;
  355. if (this.template === '1v1' && (!this.remoteUser || this.remoteUser === data.userID)) {
  356. this.remoteUser = data.userID;
  357. this.trtcComponent.subscribeRemoteAudio({
  358. userID: data.userID
  359. });
  360. } else if (this.template === 'grid' || this.template === 'custom') {
  361. this.trtcComponent.subscribeRemoteAudio({
  362. userID: data.userID
  363. });
  364. } // 如果不订阅就不会自动播放音频
  365. // this.trtcComponent.subscribeRemoteAudio({ userID: data.userID })
  366. }); // 远端用户取消推送音频
  367. this.trtcComponent.on(TRTC_EVENT.REMOTE_AUDIO_REMOVE, event => {
  368. console.log('* room REMOTE_AUDIO_REMOVE', event, this.trtcComponent.getRemoteUserList());
  369. });
  370. }
  371. },
  372. }
  373. </script>
  374. <style lang="less" scoped>
  375. page {
  376. width: 100%;
  377. height: 100%;
  378. }
  379. .container {
  380. width: 100%;
  381. height: 100;
  382. float: left;
  383. position: relative;
  384. .popup-open {
  385. width: 50px;
  386. height: 50px;
  387. position: fixed;
  388. bottom: 15px;
  389. right: 15px;
  390. background: #52A63A;
  391. border-radius: 50%;
  392. text-align: center;
  393. line-height: 50px;
  394. .iconzhibo-shangpin {
  395. color: #FFFFFF;
  396. font-size: 34px;
  397. }
  398. }
  399. .popup-box {
  400. width: 100%;
  401. height: 265px;
  402. float: left;
  403. background-color: #FFFFFF;
  404. border-radius: 15px 15px 0px 0px;
  405. box-sizing: border-box;
  406. padding: 16px 0 0 0;
  407. position: relative;
  408. .popup-close {
  409. width: 100%;
  410. height: 26px;
  411. float: left;
  412. box-sizing: border-box;
  413. padding: 10px 15px 0 15px;
  414. font-size: 15px;
  415. font-family: PingFang SC;
  416. color: #52A63A;
  417. line-height: 16px;
  418. }
  419. .good-box {
  420. width: 100%;
  421. height: calc(100% - 26px);
  422. box-sizing: border-box;
  423. padding: 0 0 10px 0;
  424. overflow: auto;
  425. position: relative;
  426. .goods-row:first-child {
  427. margin-top: 12px;
  428. }
  429. .goods-row {
  430. width: calc(100% - 30px);
  431. height: 104px;
  432. float: left;
  433. background: #FFFFFF;
  434. box-shadow: 0px 2px 10px 0px rgba(0, 0, 0, 0.1);
  435. border-radius: 5px;
  436. overflow: hidden;
  437. margin: 0 15px 10px 15px;
  438. position: relative;
  439. .goods-img {
  440. width: 104px;
  441. height: 104px;
  442. object-fit: cover;
  443. float: left;
  444. margin-right: 15px;
  445. }
  446. .goods-info {
  447. width: calc(100% - 120px);
  448. float: left;
  449. padding-top: 10px;
  450. .goods-name {
  451. width: 100%;
  452. height: 30px;
  453. float: left;
  454. font-size: 15px;
  455. font-family: PingFang SC;
  456. color: #333333;
  457. line-height: 15px;
  458. overflow: hidden;
  459. text-overflow: ellipsis;
  460. display: -webkit-box;
  461. -webkit-line-clamp: 2;
  462. -webkit-box-orient: vertical;
  463. word-wrap: break-word;
  464. }
  465. .goods-sales {
  466. width: 100%;
  467. float: left;
  468. font-size: 12px;
  469. font-family: PingFang SC;
  470. color: #666666;
  471. line-height: 15px;
  472. margin: 4px 0 8px 0;
  473. }
  474. .goods-number {
  475. width: 100%;
  476. height: 24px;
  477. float: left;
  478. white-space: nowrap;
  479. font-family: PingFang SC;
  480. color: #52A63A;
  481. line-height: 24px;
  482. .goods-icon {
  483. font-size: 14px;
  484. }
  485. .goods-spec {
  486. font-size: 24px;
  487. }
  488. .price {
  489. font-size: 12px;
  490. text-decoration: line-through;
  491. color: #A67954;
  492. margin-left: 6px;
  493. }
  494. }
  495. }
  496. .more-button {
  497. width: 24px;
  498. height: 24px;
  499. position: absolute;
  500. right: 16px;
  501. bottom: 16px;
  502. .iconfont {
  503. font-size: 36px;
  504. color: #999999;
  505. }
  506. }
  507. }
  508. }
  509. }
  510. .top_box{
  511. width: auto;
  512. height: 86rpx;
  513. position: absolute;
  514. z-index: 100;
  515. background: rgba(0, 0, 0, 0.4);
  516. -webkit-border-radius: 100px;
  517. border-radius: 100px;
  518. top: 182rpx;
  519. left: 30rpx;
  520. padding-right: 10rpx;
  521. }
  522. .top_box_img{
  523. width: 86rpx;
  524. height: 86rpx;
  525. border-radius: 100rpx;
  526. float: left;
  527. }
  528. .top_box_text{
  529. float: left;
  530. color: #fff;
  531. margin-left: 30rpx;
  532. padding-top: 2rpx;
  533. }
  534. .text_box_top{
  535. font-size: 34rpx;
  536. /* width: 100%; */
  537. display: block;
  538. height: 30rpx;
  539. float: left;
  540. margin-top: 19rpx;
  541. display: inline-block;
  542. }
  543. .text_box_bottom{
  544. font-size: 22rpx;
  545. float: left;
  546. margin-top: 10rpx;
  547. }
  548. .tx_btn{
  549. background: #4fa237;
  550. display: inline-block;
  551. height: 70rpx;
  552. margin-top: 5rpx;
  553. border-radius: 60rpx;
  554. line-height: 70rpx;
  555. padding: 0 30rpx;
  556. margin-left: 30rpx;
  557. }
  558. .tx_btn image{
  559. width: 60rpx;
  560. height: 60rpx;
  561. margin-top: 5rpx;
  562. margin-right: 20rpx;
  563. }
  564. .tixingn{
  565. font-size: 34rpx;
  566. display: flex;
  567. }
  568. .tixingn:before {
  569. font-size: 60rpx;
  570. }
  571. }
  572. </style>