123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902 |
- <template>
- <view>
- <title-header />
- <live-pusher :url="liveApplyInfo?liveApplyInfo.live_apply_push_url:''" mode="HD" id="camera-push" :beauty="beauty"
- enable-camera="true" @statechange="statechange" @error="liveError" device-position="back"
- waiting-image="@/static/image/live/video.png" style="top:0;bottom:0;right:0;left:0;position:fixed">
-
- </live-pusher>
- <cover-view style="top:0;bottom:0;right:0;left:0;position:fixed">
- <cover-view class="live-header">
- <cover-view class="live-header-content">
- <cover-image class="live-avatar live-header-avatar" v-if="liveApplyInfo"
- :src="liveApplyInfo.live_apply_image"></cover-image>
- <cover-view class="live-header-info">
- <cover-view><text class="course-name" v-if="liveApplyInfo">{{liveApplyInfo.live_apply_name}}</text></cover-view>
- <cover-view><text class="course-online-count" v-if="onlineInfo">在线人数:{{onlineInfo.online_count}}</text></cover-view>
- </cover-view>
- </cover-view>
- <cover-view class="live-header-right">
- <cover-image v-if="openLive && !closeLive" class="live-header-icon" src="@/static/image/live/close.png"
- @click='goCloseLive'></cover-image>
- </cover-view>
- </cover-view>
- <cover-view class="live-online-list" v-if="onlineInfo">
- <cover-view class="live-online-avatar" v-for="(item,index) in onlineInfo.online_list" :key="index">
- <cover-image v-if="item.instant_message_from_avatar" class="live-avatar"
- :src="item.instant_message_from_avatar"></cover-image>
- </cover-view>
- </cover-view>
- <cover-view><text v-if="closeLive" class='close-live-text'>您已退出直播间</text></cover-view>
- <cover-view><text v-if="!openLive" class='close-live-text'>未找到直播</text></cover-view>
- <cover-view><text v-if="openLive && !buttonOn" @click="createContext" class='on-live-text'>点击开始直播</text></cover-view>
- <cover-view class="live-bottom">
- <cover-view class="chat-message-list" v-if="chatList && showChat">
- <cover-view class="chat-message-item-wrapper" v-for="(item,index) in chatList" :key="index">
- <cover-view class="chat-message-item">
- <text class="chat-user-name">{{item.instant_message_from_name}}:</text>
- <text class="chat-message-text">{{item.instant_message}}</text>
- </cover-view>
- <cover-view class="line-content"></cover-view>
- </cover-view>
-
- </cover-view>
-
- <cover-view v-if="hasgift" class="gift-wrapper">
- <cover-view class="gift-ani">
- <cover-image :src="giftAvatar" class="avatar" />
- <cover-view class="info-name">
- <cover-view><text class="nick">{{giftNick}}</text></cover-view>
- <cover-view><text class="gift">送出 火箭</text></cover-view>
- </cover-view>
- </cover-view>
- <cover-image src="@/static/image/live/gift-item.png" class="gitf-item" />
- </cover-view>
-
-
- <cover-view class="cover-input-wrapper">
- <cover-view v-if="buttonOn" class="cover-btn-wrapper">
- <cover-image class="cover-btn-icon icon-barrage" src="@/static/image/live/beauty.png" @click="goBeauty">
- </cover-image>
- </cover-view>
- <cover-view class="cover-btn-wrapper">
- <cover-image class="cover-btn-icon icon-barrage" src="@/static/image/live/setting.png" @click="goSetting">
- </cover-image>
- </cover-view>
- <cover-view v-if="openLive" class="cover-btn-wrapper">
- <cover-image class="cover-btn-icon icon-barrage" src="@/static/image/live/refresh.png" @click="goFresh">
- </cover-image>
- </cover-view>
- <cover-view v-if="buttonOn" class="cover-btn-wrapper">
- <cover-image v-if="showChat" class="cover-btn-icon icon-barrage" src="@/static/image/live/barrage-off.png"
- @click="toggleChat"></cover-image>
- <cover-image v-else class="cover-btn-icon icon-barrage" src="@/static/image/live/barrage-on.png"
- @click="toggleChat"></cover-image>
- </cover-view>
- <cover-view v-if="buttonOn" class="cover-btn-wrapper">
- <cover-image class="cover-btn-icon icon-barrage" src="@/static/image/live/camera.png" @click="toggleCamera">
- </cover-image>
- </cover-view>
- </cover-view>
- </cover-view>
- </cover-view>
- <uni-popup background-color="#fff" ref="confirm" type="dialog">
- <uni-popup-dialog :mode="dialog.mode" :title="dialog.title" :content="dialog.content"
- :placeholder="dialog.content" @confirm="confirmDialog" @close="closeDialog"></uni-popup-dialog>
- </uni-popup>
- </view>
- </template>
- <script>
- import {
- mapState,
- mapActions
- } from 'vuex'
- import {
- getLiveApplyInfo,
- leaveLiveApply,
- changeLiveApply,
- joinLiveApply
- } from '../../../../api/sellerLiveApply'
- import {
- addInstantMessage
- } from '../../../../api/sellerInstantMessage'
- import TitleHeader from '../../../TitleHeader'
- export default {
- data() {
- return {
- canReconnect:true,
- dialog:{},
- beauty: 0,
- ifAuthRecord: false,
- ifAuthCamera: false,
- liveApplyInfo: false,
- onlineInfo: {
- online_count: 0,
- online_list: {}
- },
- chatList: {},
- showChat: true,
- ifConnect: false,
- clientId: false,
- openLive: false,
- closeLive: false,
- lockReconnect: false,
- timeOut: false,
- livePusher: {},
- buttonOn: false,
- hasgift: false,
- giftAvatar: '',
- giftNick: '',
- }
- },
- computed: {
- ...mapState({
- config: state => state.config.config,
- }),
- },
- components: {
- TitleHeader,
- },
- props: ['live_apply_id'],
- created() {
- this.fetchConfig({})
- if (!this.live_apply_id) {
- uni.showToast({
- icon: 'none',
- title: '参数错误'
- })
- }
- this.load()
- },
- mounted() {
- var livePusher = uni.createLivePusherContext('camera-push',this)
- this.livePusher = livePusher
- },
- beforeDestroy: function () {
- this.canReconnect=false
- heartCheck.stop()
- },
- methods: {
- ...mapActions({
- fetchConfig: 'fetchConfig'
- }),
- closeDialog() {},
- confirmDialog(value) {
- uni.showLoading({
- title: '请稍等',
- })
- leaveLiveApply({
- live_apply_push_message: value,
- live_apply_id: this.liveApplyInfo.live_apply_id,
- client_id: this.clientId
- }).then(res=>{
- if(this.livePusher){
- this.livePusher.stop()
- }
-
- this.closeLive = true
- uni.hideLoading()
- uni.navigateBack({delta:1})
- }).catch(res=>{
- console.log(res)
- uni.showToast({
- title: res.message,
- icon: 'none'
- });
- uni.hideLoading()
- })
- },
- load(){
- getLiveApplyInfo(this.live_apply_id).then(res => {
- var live_apply_info = res.result.live_apply_info
- if (live_apply_info.live_apply_state != 1) {
- uni.showToast({
- title: '直播未审核',
- icon: 'none'
- });
- return
- }
- if (!live_apply_info.live_apply_push_url) {
- uni.showToast({
- title: '未设置推流URL',
- icon: 'none'
- });
- return
- }
- if (!live_apply_info.live_apply_play_url) {
- uni.showToast({
- title: '未设置播放URL',
- icon: 'none'
- });
- return
- }
- var timestamp = new Date().getTime() / 1000
- if (live_apply_info.live_apply_end_time < timestamp) {
- uni.showToast({
- title: '直播已结束',
- icon: 'none'
- });
- return
- }
- if ((live_apply_info.live_apply_play_time - 3600) > timestamp) {
- uni.showToast({
- title: '只能提前一小时入场',
- icon: 'none'
- });
- return
- }
-
- //用户授权
- /*uni.getSetting({
- success(res) {
- if (!res.authSetting['scope.record']) {
- uni.authorize({
- scope: 'scope.record',
- success() {
- _this.ifAuthRecord = true
- },
- fail() {
- uni.showToast({
- title: '获取录音权限失败',
- icon: 'none'
- });
- }
- })
- } else {
- _this.ifAuthRecord = true
- }
- if (!res.authSetting['scope.camera']) {
- uni.authorize({
- scope: 'scope.camera',
- success() {
- _this.ifAuthCamera = true
- },
- fail() {
- uni.showToast({
- title: '获取摄像权限失败',
- icon: 'none'
- });
- }
- })
- } else {
- _this.ifAuthCamera = true
- }
- },
- fail(res) {
- uni.showToast({
- title: res.errMsg,
- icon: 'none'
- });
- }
- })*/
-
-
- this.liveApplyInfo = live_apply_info,
- this.onlineInfo = res.result.online_info,
- this.openLive = true
-
- if (live_apply_info.instant_message_url) {
- this.createWebSocket()
- }
- }).catch(error => {
- uni.showToast({
- icon: 'none',
- title: error.message
- })
- })
- },
- createContext() {
- /*if (!this.ifAuthCamera || !this.ifAuthRecord) {
- uni.showToast({
- title: '直播需要录音与摄像权限,请点击底部设置按钮开启后刷新',
- icon: 'none'
- });
- return
- }*/
- var _this = this
- this.buttonOn = true
- _this.livePusher.start({
- success: function(res) {
- changeLiveApply({
- live_apply_id: _this.liveApplyInfo.live_apply_id,
- live_apply_push_state: 1
- }).then(res=> {
- console.log(res)
- uni.showToast({
- title: res.message,
- icon: 'none'
- });
- }).catch(res=> {
- console.log(res)
- uni.showToast({
- title: res.message,
- icon: 'none'
- });
- })
- },
- fail: function(res) {
- console.log(res)
- }
- });
- },
- statechange(res) {
- console.log(res)
- },
- liveError(res) {
- console.log(res)
- uni.showToast({
- title: res.errMsg,
- icon: 'none'
- });
- },
- goBeauty() {
- this.beauty = (this.beauty + 1) % 10
- console.log(this.beauty)
- uni.showToast({
- title: '美颜' + (this.beauty + 1) * 10 + '%',
- icon: 'none'
- });
- },
- goFresh() {
- this.load()
- },
- goSetting() {
- uni.openSetting({
- fail(res) {
- uni.showToast({
- title: '操作失败',
- icon: 'none'
- });
- }
- })
- },
- toggleChat(e) {
- this.showChat = !this.showChat
- },
- sendMessage(e) {
- var _this = this
- if (!this.ifConnect) {
- uni.showToast({
- title: '未接入聊天系统',
- icon: 'none'
- });
- return;
- }
- if (!this.inputInfo) {
- uni.showToast({
- title: '请输入聊天内容',
- icon: 'none'
- });
- return;
- }
- uni.showLoading({
- title: '发送中',
- })
- addInstantMessage({
- to_id: this.liveApplyInfo.live_apply_id,
- to_type: 2,
- message: this.inputInfo,
- message_type: 0
- }).then(res=> {
- _this.inputFocus = false,
- _this.inputInfo = '',
- _this.inputModel = ''
- console.log(res)
- uni.showToast({
- title: res.message,
- icon: 'none'
- });
- }).catch(res=> {
- console.log(res)
- uni.showToast({
- title: res.message,
- icon: 'none'
- });
- })
- },
- goCloseLive() {
- this.dialog = {
- mode: 'input',
- title: '请输入关闭直播理由',
- }
- this.$refs.confirm.open()
- },
- createWebSocket() {
- try {
- uni.connectSocket({
- url: this.liveApplyInfo.instant_message_url,
- })
- this.initWebSocket();
- } catch (e) {
- this.reconnectWebSocket();
- }
- },
- initWebSocket() {
- var _this = this
- uni.onSocketClose(function(res) {
- _this.ifConnect = false,
- console.log(res)
- if (res.reason) {
- uni.showToast({
- title: '聊天系统连接断开:' + res.reason,
- icon: 'none'
- });
- }
- if(_this.canReconnect){
- _this.reconnectWebSocket();
- }
- })
- uni.onSocketError(function(res) {
- uni.showToast({
- title: '聊天系统接入失败:' + res.errMsg,
- icon: 'none'
- });
- _this.reconnectWebSocket();
- })
- uni.onSocketOpen(function(res) {
- _this.ifConnect = true,
- uni.showToast({
- title: '聊天系统接入成功',
- icon: 'none'
- });
- //心跳检测重置
- heartCheck.start();
- })
- uni.onSocketMessage(function(res) {
- console.log(res)
- var message = JSON.parse(res.data)
- if (!message) {
- uni.showToast({
- title: '消息转换失败:' + res,
- icon: 'none'
- });
- return
- }
- var type = message.type || '';
- switch (type) {
- // Events.php中返回的init类型的消息,将client_id发给后台进行uid绑定
- case 'init':
- _this.clientId = message.client_id
- //加入聊天
- joinLiveApply({
- live_apply_id: _this.liveApplyInfo.live_apply_id,
- client_id: message.client_id
- }).catch(res=> {
- console.log(res)
- uni.showToast({
- title: res.message,
- icon: 'none'
- });
- })
- break;
- case 'leave':
- //离开聊天
- var onlineInfo = {
- online_count: message.online_count,
- online_list: message.online_list
- }
- _this.onlineInfo = onlineInfo
- break;
- case 'join':
- var onlineInfo = {
- online_count: message.online_count,
- online_list: message.online_list
- }
- _this.onlineInfo = onlineInfo
- break;
- case 'gift':
- _this._this.hasgift = true
- _this.giftAvatar = message.member.member_avatar
- _this.giftNick = message.member.member_name
- console.log(message.member.member_avatar)
- setTimeout(() => {
- _this.hasgift = false
- _this.giftAvatar = ''
- _this.giftNick = ''
- }, 1000)
- break
- // 当mvc框架调用GatewayClient发消息时直接alert出来
- default:
- var chatList = _this.chatList
- if (!chatList) {
- chatList = {}
- }
- chatList[(Date.parse(new Date()) / 1000) + '' + Math.ceil(Math.random() * 10000)] = message
- _this.chatList = chatList
- console.log(chatList)
- }
- heartCheck.start();
- })
- },
- reconnectWebSocket() {
- var _this = this
- if (_this.lockReconnect) {
- return;
- };
- _this.lockReconnect = true
- //没连接上会一直重连,设置延迟避免请求过多
- _this.timeOut && clearTimeout(_this.timeOut);
- _this.timeOut = setTimeout(function() {
- _this.createWebSocket();
- _this.lockReconnect = false
- }, 4000)
- },
- toggleCamera() {
- this.livePusher.switchCamera()
- }
- }
- }
- //心跳检测
- var heartCheck = {
- timeout: 3000,
- timeoutObj: null,
- start: function() {
- var self = this;
- this.timeoutObj && clearInterval(this.timeoutObj);
- this.timeoutObj = setInterval(function() {
- //这里发送一个心跳,后端收到后,返回一个心跳消息,
- uni.sendSocketMessage({
- data: "123456789"
- })
- }, this.timeout)
- },
- stop:function(){
- this.timeoutObj && clearInterval(this.timeoutObj)
- }
- }
- </script>
- <style lang="scss" scoped>
- .message-box-wrapper {
- position: fixed;
- top: 0;
- bottom: 0;
- left: 0;
- right: 0;
- background: rgba(0, 0, 0, 0.5);
- z-index: 10
- }
- .message-box {
- background: #fff;
- border-radius: 20rpx;
- padding: 40rpx;
- width: 80%;
- /* #ifndef APP-PLUS-NVUE */
- margin: 0 auto;
- /* #endif */
- position: relative;
- top: 50%;
- margin-top: -120rpx;
- }
- .message-box-content {
- border: 1px solid #eee;
- padding: 10rpx;
- position: relative
- }
- .message-box-titile {
- margin-bottom: 20rpx;
- color: #999
- }
- .message-box-text {
- height: 100rpx;
- border-radius: 10rpx;
- }
- .message-box-input {
- height: 100rpx;
- position: absolute;
- left: 10rpx;
- top: 10rpx;
- z-index: 2
- }
- .message-box-btn {
- /* #ifndef APP-PLUS-NVUE */
- display: flex;
- /* #endif */
- margin-top: 20rpx;
- }
- .message-box-btn-item {
- flex: 1
- }
- .message-box-btn-item:first-child {
- margin-right: 10rpx;
- }
- .message-box-btn-item:last-child {
- margin-left: 10rpx;
- }
- .live-header {
- /* #ifndef APP-PLUS-NVUE */
- display: flex;
- /* #endif */
- margin-top: 60rpx;
- flex-direction: row;
- }
- .live-header-content {
- flex: 1;
- /* #ifndef APP-PLUS-NVUE */
- display: flex;
- /* #endif */
- flex-direction: row;
- position: relative;
- left: -70rpx;
- align-items: center;
- background: rgba(0, 0, 0, 0.5);
- padding-right: 50rpx;
- border-radius: 70rpx;
- padding-top: 20rpx;
- padding-bottom: 20rpx;
- padding-left: 90rpx;
- }
- .live-header-info {
- flex: 1;
- padding-left: 20rpx;
- }
- .live-header-right {
- padding-right: 20rpx;
- width: 100rpx;
- }
- .live-header-icon {
- width: 50rpx;
- height: 50rpx;
- /* #ifndef APP-PLUS-NVUE */
- float: right;
- /* #endif */
- }
- .live-avatar {
- width: 100rpx;
- height: 100rpx;
- border-radius: 50%;
- border: 1px solid #fff;
- }
- .close-live-text {
- text-align: center;
- margin-top: 20rpx;
- color: #fff;
- }
- .on-live-text {
- text-align: center;
- margin-top: 20rpx;
- color: #26a2ff;
- }
- .course-name {
- /* #ifndef APP-PLUS-NVUE */
- white-space: nowrap;
- /* #endif */
- text-overflow: ellipsis;
- overflow: hidden;
- color: #fff;
- font-size: 30rpx;
- }
- .course-online-count {
- color: #eee;
- margin-top: 10rpx;
- font-size: 26rpx;
- }
- .live-online-list {
- flex-direction: row;
- align-items: center;
- padding: 10rpx;
- height: 120rpx;
- }
- .live-online-avatar {
- margin: 0 10rpx;
- }
- .chat-message-list {
- padding: 20rpx;
- position: absolute;
- bottom: 100rpx;
- left: 0;
- right: 0;
- }
- .chat-message-item-wrapper{
- /* #ifndef APP-PLUS-NVUE */
- display: flex;
- /* #endif */
- flex-direction: row;
- }
- .line-content{flex:1}
- .chat-message-item {
- /* opacity: 0.8; */
- padding: 10rpx 20rpx;
- background: rgba(0, 0, 0, 0.5);
- font-size: 26rpx;
- /* #ifndef APP-PLUS-NVUE */
- display: flex;
- /* #endif */
- flex-direction: row;
- margin-bottom: 20rpx;
- border-radius: 26rpx;
- }
- .chat-user-name {
- padding-right: 10rpx;
- color: #19a1fd;
- }
- .chat-message-text {
- flex: 1;
- color: #fff;
- /* #ifndef APP-PLUS-NVUE */
- display: inline;
- white-space: normal;
- word-wrap: break-word;
- word-break: break-all;
- /* #endif */
- }
- .cover-input-wrapper {
- /* #ifndef APP-PLUS-NVUE */
- display: flex;
- /* #endif */
- flex-direction: row;
- padding: 20rpx;
- /* opacity: 0.8; */
- align-items: center;
- justify-content: flex-end;
- position: absolute;
- bottom: 0;
- left: 0;
- right: 0;
- }
- .cover-input {
- flex: 1;
- height: 80rpx;
- line-height: 80rpx;
- border-radius: 40rpx;
- background-color: rgba(0, 0, 0, 0.5);
- position: relative;
- padding-left: 20rpx;
- padding-right: 20rpx;
- color: #fff;
- }
- .cover-input-text {
- height: 80rpx;
- line-height: 80rpx;
- }
- .cover-input-input {
- height: 80rpx;
- line-height: 80rpx;
- /* margin-top为text的高度,保持视觉上一致 */
- margin-top: -80rpx;
- }
- .cover-btn-wrapper {
- padding-left: 20rpx;
- }
- .cover-btn {
- width: 60rpx;
- height: 60rpx;
- border-radius: 60rpx;
- background-color: rgba(0, 0, 0, 0.5);
- }
- .icon-send {
- width: 40rpx;
- height: 40rpx;
- margin-top: 10rpx;
- margin-left: 10rpx;
- }
- .icon-barrage {
- width: 60rpx;
- height: 60rpx;
- }
- .live-bottom {
- position: absolute;
- bottom: 0;
- left:0;
- right:0;
- height: 600rpx;
- z-index: 2;
- overflow: hidden;
- }
- .gift-wrapper {
- position: relative;
- height: 170rpx;
- }
- .gift-ani {
- /* #ifndef APP-PLUS-NVUE */
- display: flex;
- /* #endif */
- flex-direction: row;
- background-color: rgba(0, 0, 0, 0.18);
- border-radius: 40rpx;
- width: 330rpx;
- height: 80rpx;
- position: relative;
- margin-top: 40rpx;
- }
- .avatar,
- .info-name {
- /* #ifndef APP-PLUS-NVUE */
- display: inline-block;
- vertical-align: middle
- /* #endif */
- }
- .avatar {
- height: 72rpx;
- width: 72rpx;
- border-radius: 36rpx;
- margin: 4rpx;
- }
- .nick {
- font-family: PingFangSC-Medium;
- font-size: 12px;
- color: #FFFFFF;
- /* #ifndef APP-PLUS-NVUE */
- letter-spacing: 0;
- /* #endif */
- text-align: left;
- line-height: 24rpx;
- }
- .gift {
- font-family: PingFangSC-Regular;
- font-size: 12px;
- color: #FFFFFF;
- /* #ifndef APP-PLUS-NVUE */
- letter-spacing: 0;
- /* #endif */
- text-align: left;
- line-height: 44rpx;
- }
- .gitf-item {
- height: 170rpx;
- width: 93rpx;
- position: absolute;
- left: 230rpx;
- top: 0;
- margin-top: 0rpx
- }
- </style>
|