main-content.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654
  1. <template>
  2. <cover-view class="wrapper" :style="'width:'+getBannerStyle.width+'px;height:'+getBannerStyle.height+'px'">
  3. <cover-view v-if="liveApplyInfo && ownerInfo" class="div room-content wrapper" :style="'width:'+getBannerStyle.width+'px;height:'+getBannerStyle.height+'px'">
  4. <!-- 头部 start -->
  5. <room-header :style="'width:'+getBannerStyle.width+'px'" :showcoupon="showcoupon" @attention="attention" :groupInfo="groupInfo"
  6. :ifLike="ifLike" :ownerInfo="ownerInfo" @coupon="coupon" @quitGroup="quitGroup"></room-header>
  7. <!-- 头部 end -->
  8. <!-- 聊天室 start -->
  9. <chatroom class="chatroom" :style="'width:'+getBannerStyle.width+'px'" :message="messageQueen"></chatroom>
  10. <!-- 聊天室 end -->
  11. <!-- 底部 -->
  12. <room-bottom class="room-bottom" :style="'width:'+getBannerStyle.width+'px'" @send-message="sendMessage" :isTimReady="ifConnect" @like="like"
  13. @showGift="showGift" @showgoods="showgoods"></room-bottom>
  14. <!-- 底部 -->
  15. <!-- 礼物以及动画 start -->
  16. <gift-animation v-if="hasgift" class="gift-animation" :avatar="giftAvatar" :nick="giftNick" @hideani="hideani">
  17. </gift-animation>
  18. <!-- 礼物以及动画 end -->
  19. <!-- 公告start -->
  20. <!-- <notice class="notice" :noticeText="noticeText" v-if="roomStatus!=='0'"></notice> -->
  21. <!-- 公告 end -->
  22. </cover-view>
  23. <no-owner v-else></no-owner>
  24. <uni-popup background-color="#fff" ref="animation" type="bottom">
  25. <!-- 礼物面板 start -->
  26. <gifts class="gifts" @hideGift="hideGift" @sendgift="sendgift"></gifts>
  27. <!-- 礼物面板 end -->
  28. </uni-popup>
  29. <uni-popup background-color="#fff" ref="animation2" type="bottom">
  30. <!-- 商品面板 start -->
  31. <goods-list class="goodslist" @hidegoods="hidegoods" @buy="buy" :goods="goods">
  32. </goods-list>
  33. <!-- 商品面板 end -->
  34. </uni-popup>
  35. <uni-popup ref="hascoupon">
  36. <!-- 优惠券 -->
  37. <coupon class="coupon" :voucher="voucher" @hidecoupon="hidecoupon" @usecoupon="usecoupon">
  38. </coupon>
  39. <!-- 优惠券 -->
  40. </uni-popup>
  41. <uni-popup background-color="#fff" ref="confirm" type="dialog">
  42. <uni-popup-dialog :mode="dialog.mode" :title="dialog.title" :content="dialog.content"
  43. :placeholder="dialog.content" @confirm="confirmDialog" @close="closeDialog"></uni-popup-dialog>
  44. </uni-popup>
  45. </cover-view>
  46. </template>
  47. <script>
  48. import {
  49. mapState
  50. } from 'vuex'
  51. import roomHeader from './header'
  52. import roomBottom from './bottom'
  53. import chatroom from './chatroom'
  54. import gifts from './gifts'
  55. import coupon from './coupon'
  56. import notice from './notice'
  57. import goodsList from './goodslist'
  58. import giftAnimation from './gift-animation'
  59. import noOwner from './noowner'
  60. import {
  61. joinLive,
  62. leaveLive,
  63. addGift,
  64. addLike
  65. } from '../../../../api/memberLive'
  66. import {
  67. addInstantMessage
  68. } from '../../../../api/memberInstantMessage'
  69. import {
  70. receiveVoucher,
  71. getStoreVoucher
  72. } from '../../../../api/homestoredetail'
  73. import {
  74. addFavoriteStore,
  75. delFavoriteStore
  76. } from '../../../../api/memberFavorite'
  77. import {
  78. cartAdd
  79. } from '../../../../api/homecart'
  80. export default {
  81. data() {
  82. return {
  83. canReconnect:true,
  84. dialog: {},
  85. message: [],
  86. groupInfo: {},
  87. ownerInfo: false,
  88. ifLike: false,
  89. hasgift: false,
  90. giftAvatar: '',
  91. giftNick: '',
  92. showcoupon: false,
  93. voucherList: false,
  94. voucher: {},
  95. voucherIndex: 0,
  96. goods: [],
  97. noticeText: '',
  98. showChat: true,
  99. ifConnect: false,
  100. clientId: false,
  101. openLive: false,
  102. closeLive: false,
  103. lockReconnect: false,
  104. timeOut: false,
  105. ifWait: false
  106. }
  107. },
  108. props: ['liveApplyInfo','onlineInfo'],
  109. components: {
  110. roomHeader,
  111. roomBottom,
  112. chatroom,
  113. gifts,
  114. coupon,
  115. notice,
  116. goodsList,
  117. giftAnimation,
  118. noOwner
  119. },
  120. computed: {
  121. ...mapState({
  122. config: state => state.config.config,
  123. user: state => state.member.info
  124. }),
  125. getBannerStyle: function () {
  126. const res = uni.getSystemInfoSync()
  127. var width = res.windowWidth
  128. var height = res.windowHeight
  129. let itemWidth = width
  130. let itemHeight = height
  131. return {
  132. width: itemWidth,
  133. height: itemHeight
  134. }
  135. },
  136. messageQueen: function() {
  137. const queenLen = 100 // 内存里面放100条消息,以免观看直播太久撑爆内存
  138. if (this.message.length > queenLen) {
  139. const vl = this.message.length - queenLen
  140. for (let i = 0; i < vl; i++) {
  141. this.message.shift()
  142. }
  143. }
  144. return this.message
  145. }
  146. },
  147. created() {
  148. getStoreVoucher(this.liveApplyInfo.live_apply_user_id).then(res => {
  149. this.voucherList = res.result.voucher_list
  150. if (this.voucherList.length) {
  151. this.showcoupon = true
  152. }
  153. }).catch(error => {
  154. uni.showToast({
  155. icon: 'none',
  156. title: error.message
  157. })
  158. })
  159. if (this.liveApplyInfo.goods_list) {
  160. this.goods = this.liveApplyInfo.goods_list
  161. }
  162. this.groupInfo = {
  163. groupID: this.liveApplyInfo.live_apply_id,
  164. memberNum: this.onlineInfo.online_count
  165. }
  166. this.ifLike = this.liveApplyInfo.is_favorate
  167. this.ownerInfo = {
  168. avatar: this.liveApplyInfo.live_apply_user_avatar,
  169. nick: this.liveApplyInfo.live_apply_user_name,
  170. fans: this.liveApplyInfo.live_apply_fans
  171. }
  172. this.openLive = true
  173. if (this.liveApplyInfo.instant_message_url) {
  174. this.createWebSocket()
  175. }
  176. },
  177. beforeDestroy: function () {
  178. this.canReconnect=false
  179. heartCheck.stop()
  180. },
  181. methods: {
  182. showPopup(id){
  183. this.$refs[id].open()
  184. },
  185. hidePopup(id){
  186. this.$refs[id].close()
  187. },
  188. closeDialog() {},
  189. confirmDialog(value) {
  190. switch (this.dialog.condition) {
  191. case 3:
  192. addGift(this.liveApplyInfo.live_apply_id).then(res => {
  193. this.hideGift()
  194. this.ifWait = false
  195. }).catch(error => {
  196. uni.showToast({
  197. icon: 'none',
  198. title: error.message
  199. })
  200. this.ifWait = false
  201. })
  202. break
  203. case 4:
  204. if (this.openLive) {
  205. this.closeLive = true
  206. }
  207. if (this.clientId) {
  208. leaveLive(this.liveApplyInfo.live_apply_id, this.clientId).then(res => {
  209. uni.navigateBack({
  210. delta: 1
  211. })
  212. }).catch(error => {
  213. uni.showToast({
  214. icon: 'none',
  215. title: error.message
  216. })
  217. })
  218. } else {
  219. uni.navigateBack({
  220. delta: 1
  221. })
  222. }
  223. break
  224. case 5:
  225. if (this.openLive) {
  226. this.closeLive = true
  227. }
  228. if (this.clientId) {
  229. leaveLive(this.liveApplyInfo.live_apply_id, this.clientId).then(res => {
  230. uni.navigateBack({
  231. delta: 1
  232. })
  233. }).catch(error => {
  234. uni.showToast({
  235. icon: 'none',
  236. title: error.message
  237. })
  238. })
  239. } else {
  240. uni.navigateBack({
  241. delta: 1
  242. })
  243. }
  244. break
  245. }
  246. },
  247. hidecoupon() {
  248. this.hidePopup('hascoupon')
  249. },
  250. usecoupon() {
  251. if (this.ifWait) {
  252. return
  253. }
  254. this.ifWait = true
  255. receiveVoucher(
  256. this.voucher.vouchertemplate_id
  257. ).then((res) => {
  258. this.voucherList.splice(this.voucherIndex, 1)
  259. if (!this.voucherList.length) {
  260. this.showcoupon = false
  261. }
  262. uni.showToast({
  263. icon: 'none',
  264. title: res.message
  265. })
  266. this.ifWait = false
  267. }).catch(function(error) {
  268. uni.showToast({
  269. icon: 'none',
  270. title: error.message
  271. })
  272. this.ifWait = false
  273. })
  274. this.hidecoupon()
  275. },
  276. coupon() {
  277. this.voucherIndex = Math.floor(Math.random() * this.voucherList.length) % this.voucherList.length
  278. this.voucher = this.voucherList[this.voucherIndex]
  279. this.showPopup('hascoupon')
  280. },
  281. buy(data) {
  282. if (this.ifWait) {
  283. return
  284. }
  285. this.ifWait = true
  286. cartAdd(data.goods_id, 1).then(
  287. res => {
  288. uni.showToast({
  289. icon: 'none',
  290. title: res.message
  291. })
  292. this.ifWait = false
  293. },
  294. error => {
  295. uni.showToast({
  296. icon: 'none',
  297. title: error.message
  298. })
  299. this.ifWait = false
  300. }
  301. )
  302. },
  303. hideani() {
  304. this.hasgift = false
  305. this.giftAvatar = ''
  306. this.giftNick = ''
  307. },
  308. showgoods() {
  309. this.showPopup('animation2')
  310. },
  311. hidegoods() {
  312. this.hidePopup('animation2')
  313. },
  314. showGift() {
  315. this.showPopup('animation')
  316. },
  317. sendgift() {
  318. if (this.ifWait) {
  319. return
  320. }
  321. this.ifWait = true
  322. this.dialog = {
  323. condition: 3,
  324. content: '确定要给主播送礼物?'
  325. }
  326. this.$refs.confirm.open()
  327. },
  328. hideGift() {
  329. this.hidePopup('animation')
  330. },
  331. like() {
  332. if (this.ifWait) {
  333. return
  334. }
  335. this.ifWait = true
  336. addLike(this.liveApplyInfo.live_apply_id).then(res => {
  337. this.ifWait = false
  338. }).catch(error => {
  339. this.ifWait = false
  340. })
  341. },
  342. attention() {
  343. if (this.ifWait) {
  344. return
  345. }
  346. this.ifWait = true
  347. if (!this.ifLike) {
  348. addFavoriteStore(this.liveApplyInfo.live_apply_user_id).then(
  349. response => {
  350. uni.showToast({
  351. icon: 'none',
  352. title: response.message
  353. })
  354. this.ifLike = !this.ifLike
  355. this.ifWait = false
  356. },
  357. error => {
  358. uni.showToast({
  359. icon: 'none',
  360. title: error.message
  361. })
  362. this.ifWait = false
  363. }
  364. )
  365. } else {
  366. delFavoriteStore(this.liveApplyInfo.live_apply_user_id).then(
  367. response => {
  368. uni.showToast({
  369. icon: 'none',
  370. title: response.message
  371. })
  372. this.ifLike = !this.ifLike
  373. this.ifWait = false
  374. },
  375. error => {
  376. uni.showToast({
  377. icon: 'none',
  378. title: error.message
  379. })
  380. this.ifWait = false
  381. }
  382. )
  383. }
  384. },
  385. quitGroup() {
  386. this.dialog = {
  387. condition: 4,
  388. content: '您确认要退出直播吗?'
  389. }
  390. this.$refs.confirm.open()
  391. },
  392. _getVarsByKey(arr, key) {
  393. var res
  394. for (var i = 0; i < arr.length; i++) {
  395. if (arr[i].key === key) {
  396. res = arr[i].value
  397. break
  398. }
  399. }
  400. return res
  401. },
  402. toggleChat() {
  403. this.showChat = !this.showChat
  404. },
  405. sendMessage(data) {
  406. if (!this.ifConnect) {
  407. uni.showToast({
  408. icon: 'none',
  409. title: '未接入聊天系统'
  410. })
  411. return
  412. }
  413. if (!data) {
  414. uni.showToast({
  415. icon: 'none',
  416. title: '请输入聊天内容'
  417. })
  418. return
  419. }
  420. addInstantMessage({
  421. to_id: this.liveApplyInfo.live_apply_id,
  422. to_type: 2,
  423. message: data,
  424. message_type: 0
  425. }).then(res => {
  426. uni.showToast({
  427. icon: 'none',
  428. title: res.message
  429. })
  430. }).catch(error => {
  431. uni.showToast({
  432. icon: 'none',
  433. title: error.message
  434. })
  435. })
  436. },
  437. goCloseLive() {
  438. this.dialog = {
  439. condition: 5,
  440. title: '退出提醒',
  441. content: '您确认要退出直播吗?'
  442. }
  443. this.$refs.confirm.open()
  444. },
  445. createWebSocket() {
  446. uni.connectSocket({
  447. url: this.liveApplyInfo.instant_message_url,
  448. }).then(res => {
  449. this.init()
  450. }).catch(error => {
  451. this.reconnect()
  452. })
  453. },
  454. init() {
  455. uni.onSocketOpen(res => {
  456. this.wsOpen()
  457. })
  458. uni.onSocketMessage(res => {
  459. this.wsMessage(res)
  460. })
  461. uni.onSocketClose(res => {
  462. this.wsClose(res)
  463. })
  464. uni.onSocketError(res => {
  465. this.wsError(res)
  466. })
  467. },
  468. reconnect() {
  469. if (this.lockReconnect) {
  470. return
  471. };
  472. this.lockReconnect = true
  473. // 没连接上会一直重连,设置延迟避免请求过多
  474. this.timeOut && clearTimeout(this.timeOut)
  475. var _this = this
  476. this.timeOut = setTimeout(function() {
  477. _this.createWebSocket()
  478. _this.lockReconnect = false
  479. }, 4000)
  480. },
  481. wsOpen() {
  482. this.ifConnect = true
  483. // 心跳检测重置
  484. heartCheck.start()
  485. },
  486. wsMessage(res) {
  487. var message = JSON.parse(res.data)
  488. if (!message) {
  489. uni.showToast({
  490. icon: 'none',
  491. title: '消息转换失败:' + res.data
  492. })
  493. return
  494. }
  495. var type = message.type || ''
  496. switch (type) {
  497. // Events.php中返回的init类型的消息,将client_id发给后台进行uid绑定
  498. case 'init':
  499. this.clientId = message.client_id
  500. joinLive(this.liveApplyInfo.live_apply_id, this.clientId).then(res => {
  501. }).catch(error => {
  502. uni.showToast({
  503. icon: 'none',
  504. title: error.message
  505. })
  506. })
  507. break
  508. case 'leave':
  509. this.groupInfo.memberNum = message.online_count
  510. break
  511. case 'join':
  512. this.groupInfo.memberNum = message.online_count
  513. break
  514. case 'gift':
  515. this.hasgift = true
  516. this.giftAvatar = message.member.member_avatar
  517. this.giftNick = message.member.member_name
  518. setTimeout(() => {
  519. this.hasgift = false
  520. this.giftAvatar = ''
  521. this.giftNick = ''
  522. }, 1000)
  523. break
  524. default:
  525. let msg = []
  526. msg.push({
  527. name: message.instant_message_from_name,
  528. message: message.instant_message,
  529. id: message.instant_message_id
  530. })
  531. this.message = this.message.concat(msg)
  532. this.$forceUpdate()
  533. }
  534. heartCheck.start()
  535. },
  536. wsClose(res) {
  537. this.ifConnect = false
  538. if (res.reason) {
  539. uni.showToast({
  540. icon: 'none',
  541. title: '聊天系统连接断开:' + res.reason
  542. })
  543. }
  544. if(this.canReconnect){
  545. this.reconnect();
  546. }
  547. },
  548. wsError(res) {
  549. uni.showToast({
  550. icon: 'none',
  551. title: res.errMsg
  552. })
  553. this.reconnect()
  554. }
  555. }
  556. }
  557. // 心跳检测
  558. var heartCheck = {
  559. timeout: 3000,
  560. timeoutObj: null,
  561. start: function() {
  562. var self = this
  563. this.timeoutObj && clearInterval(this.timeoutObj)
  564. this.timeoutObj = setInterval(function() {
  565. // 这里发送一个心跳,后端收到后,返回一个心跳消息,
  566. uni.sendSocketMessage({
  567. data: '123456789'
  568. })
  569. }, this.timeout)
  570. },
  571. stop:function(){
  572. this.timeoutObj && clearInterval(this.timeoutObj)
  573. }
  574. }
  575. </script>
  576. <style lang="scss" scoped>
  577. .wrapper{
  578. position: fixed;
  579. top: 0;
  580. left: 0;
  581. z-index: 100;
  582. }
  583. .room-content {
  584. position: absolute;
  585. top:0;
  586. left:0;
  587. z-index: 2;
  588. }
  589. .room-bottom {
  590. position: absolute;
  591. z-index: 2;
  592. padding-bottom: 20rpx;
  593. bottom:0;
  594. left:0;
  595. /* #ifndef APP-PLUS-NVUE */
  596. margin-bottom: constant(safe-area-inset-bottom);
  597. /* 兼容 iOS < 11.2 */
  598. margin-bottom: env(safe-area-inset-bottom);
  599. /* 兼容 iOS >= 11.2 */
  600. /* #endif */
  601. }
  602. .chatroom {
  603. left:0;
  604. height: 800rpx;
  605. position: absolute;
  606. bottom: 100rpx;
  607. z-index: 100;
  608. padding: 22rpx;
  609. overflow: hidden;
  610. }
  611. .gifts{
  612. height: 800rpx;
  613. }
  614. .goodslist {
  615. height: 800rpx;
  616. }
  617. .gift-animation {
  618. position: absolute;
  619. z-index: 100;
  620. top: 500rpx;
  621. left: 20rpx;
  622. height: 170rpx;
  623. /* #ifndef APP-PLUS-NVUE */
  624. display: flex;
  625. /* #endif */
  626. flex-direction: column;
  627. justify-content: center;
  628. }
  629. .coupon {
  630. }
  631. .notice {
  632. position: absolute;
  633. left: 20rpx;
  634. bottom: 50vh;
  635. /* #ifndef APP-PLUS-NVUE */
  636. max-width: 60vw;
  637. /* #endif */
  638. }
  639. </style>