|
@@ -46,7 +46,7 @@
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
<!-- 红包内容 -->
|
|
<!-- 红包内容 -->
|
|
- <view class="hongbao-box" v-if="liveItem.redInfo?.redStatus==1">
|
|
|
|
|
|
+ <view class="hongbao-box" v-if="liveItem.redInfo?.redStatus==1&&isShowRed">
|
|
<view class="u-flex-y-center">
|
|
<view class="u-flex-y-center">
|
|
<view class="tip">领红包</view>
|
|
<view class="tip">领红包</view>
|
|
<view class="item">
|
|
<view class="item">
|
|
@@ -66,15 +66,27 @@
|
|
<view class="videolist" style="margin: auto 0">
|
|
<view class="videolist" style="margin: auto 0">
|
|
<view class="video" style="height:100vh">
|
|
<view class="video" style="height:100vh">
|
|
<!-- 视频组件 -->
|
|
<!-- 视频组件 -->
|
|
- <video v-if="currentSwiperIndex === index && liveItem.livingUrl"
|
|
|
|
|
|
+ <!-- <live-player v-if="currentSwiperIndex === index && liveItem.livingUrl" mode="live"
|
|
|
|
+ orientation="vertical" :id="'myVideo_' + liveItem.liveId" :src="liveItem.livingUrl"
|
|
|
|
+ :autoplay="currentSwiperIndex === index"
|
|
|
|
+ bindstatechange="onStatechange"></live-player> -->
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ <!-- <video v-if="currentSwiperIndex === index && liveItem.livingUrl"
|
|
:id="'myVideo_' + liveItem.liveId"
|
|
:id="'myVideo_' + liveItem.liveId"
|
|
:class="liveItem.showType == 1 ? 'video_row' : 'videotop'" :src="liveItem.livingUrl"
|
|
:class="liveItem.showType == 1 ? 'video_row' : 'videotop'" :src="liveItem.livingUrl"
|
|
- :autoplay="currentSwiperIndex === index" :controls='false' object-fit='contain'
|
|
|
|
- :custom-cache="false" :enable-progress-gesture="false"
|
|
|
|
- vslide-gesture-in-fullscreen='true' :show-center-play-btn="false"
|
|
|
|
- :http-cache="false" @error="videoError">
|
|
|
|
- </video>
|
|
|
|
-
|
|
|
|
|
|
+ :controls='false' object-fit='contain' :custom-cache="false"
|
|
|
|
+ :enable-progress-gesture="false" vslide-gesture-in-fullscreen='true'
|
|
|
|
+ :show-center-play-btn="false" :http-cache="false" @error="videoError">
|
|
|
|
+ </video> -->
|
|
|
|
+ <live-player v-if="currentSwiperIndex === index && liveItem.livingUrl"
|
|
|
|
+ :id="'myLivePlayer_' + liveItem.liveId"
|
|
|
|
+ :class="liveItem.showType == 1 ? 'video_row' : 'videotop'" :src="liveItem.livingUrl"
|
|
|
|
+ mode="live" orientation="vertical" :autoplay="currentSwiperIndex === index"
|
|
|
|
+ :controls="false" object-fit="contain" :enable-progress-gesture="false"
|
|
|
|
+ :show-center-play-btn="false" @error="livePlayerError"
|
|
|
|
+ @statechange="onLivePlayerStateChange"
|
|
|
|
+ @fullscreenchange="onLiveFullScreenChange"></live-player>
|
|
<video v-if="currentSwiperIndex === index && liveItem.videoUrl"
|
|
<video v-if="currentSwiperIndex === index && liveItem.videoUrl"
|
|
:class="liveItem.showType == 1 ? 'video_row' : 'videotop'" :src="liveItem.videoUrl"
|
|
:class="liveItem.showType == 1 ? 'video_row' : 'videotop'" :src="liveItem.videoUrl"
|
|
:autoplay="currentSwiperIndex === index" :controls='false' object-fit='contain'
|
|
:autoplay="currentSwiperIndex === index" :controls='false' object-fit='contain'
|
|
@@ -140,43 +152,14 @@
|
|
<view class="send" @click="sendMsg(liveItem)">发送</view>
|
|
<view class="send" @click="sendMsg(liveItem)">发送</view>
|
|
</view>
|
|
</view>
|
|
<view class="justify-between mr15 align-center">
|
|
<view class="justify-between mr15 align-center">
|
|
- <!-- <view class="icon ml20" @click="onLike(liveItem)">
|
|
|
|
- <image src="/static/images/live/like.png" class="w56 h56"></image>
|
|
|
|
- <view>{{liveItem.liveViewData?.like||0}}</view>
|
|
|
|
- </view>
|
|
|
|
- <view class="icon ml20">
|
|
|
|
- <button open-type="share" class="button">
|
|
|
|
- <image src="/static/images/live/share.png" class="w56 h56"></image>
|
|
|
|
- </button>
|
|
|
|
- <view>分享</view>
|
|
|
|
- </view> -->
|
|
|
|
<view class="icon-bg ml20" @click="openCart(liveItem)">
|
|
<view class="icon-bg ml20" @click="openCart(liveItem)">
|
|
<image src="/static/images/shopping.png" class="w48 h48"></image>
|
|
<image src="/static/images/shopping.png" class="w48 h48"></image>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
-
|
|
|
|
- <!-- 弹出商品 -->
|
|
|
|
- <!-- v-if="liveItem.goodsCard.isShow" -->
|
|
|
|
- <!-- <view class="goods">
|
|
|
|
- <view class="top">
|
|
|
|
- <view class="left">
|
|
|
|
- <image class="w30 h30 mr8" src="/static/images/signal.png"></image>讲解中
|
|
|
|
- </view>
|
|
|
|
- <image class="w30 h30" src="/static/images/close.png"
|
|
|
|
- @click="liveItem.goodsCard.isShow=false"></image>
|
|
|
|
- </view>
|
|
|
|
- <image class="photo" :src="liveItem.goodsCard.imgUrl"></image>
|
|
|
|
- <view class="item">
|
|
|
|
- <view class="price"><text class="red">¥{{liveItem.goodsCard.price}} </text><text
|
|
|
|
- class="del">¥{{liveItem.goodsCard.otPrice}}</text></view>
|
|
|
|
- <view class="title oneline-hide">{{liveItem.goodsCard.productName}}</view>
|
|
|
|
- <view class="button">立即抢购</view>
|
|
|
|
- </view>
|
|
|
|
- </view> -->
|
|
|
|
</view>
|
|
</view>
|
|
- <u-popup :show="liveItem.goodsCard.isShow" @close="!liveItem.goodsCard.isShow" round='20rpx'
|
|
|
|
|
|
+ <u-popup :show="liveItem.goodsCard?.isShow" @close="!liveItem.goodsCard?.isShow" round='20rpx'
|
|
mode="center" bgColor='#ffffff'>
|
|
mode="center" bgColor='#ffffff'>
|
|
<view class="goods">
|
|
<view class="goods">
|
|
<view class="top">
|
|
<view class="top">
|
|
@@ -186,19 +169,20 @@
|
|
<image class="w30 h30" src="/static/images/close.png"
|
|
<image class="w30 h30" src="/static/images/close.png"
|
|
@click="liveItem.goodsCard.isShow=false"></image>
|
|
@click="liveItem.goodsCard.isShow=false"></image>
|
|
</view>
|
|
</view>
|
|
- <image class="photo" :src="liveItem.goodsCard.imgUrl"></image>
|
|
|
|
|
|
+ <image class="photo" :src="liveItem.goodsCard?.imgUrl"></image>
|
|
<view class="item">
|
|
<view class="item">
|
|
- <view class="price"><text class="red">¥{{liveItem.goodsCard.price}} </text><text
|
|
|
|
- class="del">¥{{liveItem.goodsCard.otPrice}}</text></view>
|
|
|
|
- <view class="title oneline-hide">{{liveItem.goodsCard.productName}}</view>
|
|
|
|
|
|
+ <view class="price"><text class="red">¥{{liveItem.goodsCard?.price}} </text><text
|
|
|
|
+ class="del">¥{{liveItem.goodsCard?.otPrice}}</text></view>
|
|
|
|
+ <view class="title oneline-hide">{{liveItem.goodsCard?.productName}}</view>
|
|
<view class="button"
|
|
<view class="button"
|
|
- @click="goShop(liveItem.goodsCard.productId,liveItem.goodsCard.goodsId)">立即抢购</view>
|
|
|
|
|
|
+ @click="goShop(liveItem.goodsCard?.productId,liveItem.goodsCard?.goodsId)">立即抢购
|
|
|
|
+ </view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</u-popup>
|
|
</u-popup>
|
|
-
|
|
|
|
|
|
+
|
|
<!-- 抽奖 -->
|
|
<!-- 抽奖 -->
|
|
- <!-- <u-popup :show="true" @close="" round='40rpx' bgColor='#fff'>
|
|
|
|
|
|
+ <!-- <u-popup :show="true" @close="" round='40rpx' bgColor='#fff'>
|
|
<view class=" p20 bgf" style="border-radius: 40rpx;height: fit-content;">
|
|
<view class=" p20 bgf" style="border-radius: 40rpx;height: fit-content;">
|
|
<view class="u-flex-row-reverse u-flex">
|
|
<view class="u-flex-row-reverse u-flex">
|
|
<u-icon name="close" size="18" @click=""></u-icon>
|
|
<u-icon name="close" size="18" @click=""></u-icon>
|
|
@@ -330,7 +314,7 @@
|
|
collectStore, // 店铺收藏/取消收藏
|
|
collectStore, // 店铺收藏/取消收藏
|
|
collectGoods, // 商品收藏/取消收藏
|
|
collectGoods, // 商品收藏/取消收藏
|
|
store, // 查询店铺
|
|
store, // 查询店铺
|
|
- showGoods, //弹出商品卡片
|
|
|
|
|
|
+ // showGoods, //弹出商品卡片
|
|
// follow, // 关注/取消关注
|
|
// follow, // 关注/取消关注
|
|
watchUserList, //获取直播间用户(展示在线用户)
|
|
watchUserList, //获取直播间用户(展示在线用户)
|
|
liveMsg, //获取最近聊天记录
|
|
liveMsg, //获取最近聊天记录
|
|
@@ -339,7 +323,8 @@
|
|
// liveGoodsDetail, //商品详情,
|
|
// liveGoodsDetail, //商品详情,
|
|
liveOrderUser, //正在购买
|
|
liveOrderUser, //正在购买
|
|
getLiveInfo, //获取直播间信息接口
|
|
getLiveInfo, //获取直播间信息接口
|
|
- getLiveViewData //直播间点赞、关注、在线人数数据
|
|
|
|
|
|
+ getLiveViewData, //直播间点赞、关注、在线人数数据
|
|
|
|
+ currentActivities //红包 卡片 抽奖
|
|
} from '@/api/live'
|
|
} from '@/api/live'
|
|
import {
|
|
import {
|
|
liveOrderList, // 订单列表
|
|
liveOrderList, // 订单列表
|
|
@@ -371,6 +356,17 @@
|
|
export default {
|
|
export default {
|
|
data() {
|
|
data() {
|
|
return {
|
|
return {
|
|
|
|
+ livePlayerInstances: {}, // 存储 live-player 组件实例(key: liveId, value: 实例)
|
|
|
|
+ livePlayerStates: {}, // 存储每个直播间的直播状态(key: liveId, value: 状态码)
|
|
|
|
+ liveRetryCounts: {}, // 直播重连次数(key: liveId, value: 次数)
|
|
|
|
+ maxLiveRetry: 3, // 最大重连次数
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ timer: null,
|
|
|
|
+
|
|
|
|
+ isShowRed: false,
|
|
// 新增:滑动节流相关变量
|
|
// 新增:滑动节流相关变量
|
|
lastSwiperChangeTime: 0, // 上次切换时间戳
|
|
lastSwiperChangeTime: 0, // 上次切换时间戳
|
|
swiperChangeThrottle: 300, // 切换节流阈值(毫秒)
|
|
swiperChangeThrottle: 300, // 切换节流阈值(毫秒)
|
|
@@ -491,6 +487,7 @@
|
|
} else {
|
|
} else {
|
|
// console.log('当前平台无需显示分享菜单');
|
|
// console.log('当前平台无需显示分享菜单');
|
|
}
|
|
}
|
|
|
|
+ this.getCurrentActivities(options.liveId);
|
|
},
|
|
},
|
|
onReady() {},
|
|
onReady() {},
|
|
onShareAppMessage() {
|
|
onShareAppMessage() {
|
|
@@ -520,38 +517,45 @@
|
|
onHide() {
|
|
onHide() {
|
|
const currentLive = this.list[this.currentSwiperIndex];
|
|
const currentLive = this.list[this.currentSwiperIndex];
|
|
if (currentLive) {
|
|
if (currentLive) {
|
|
- this.pauseVideo(currentLive);
|
|
|
|
- this.clearTimeTimer(currentLive); // 隐藏时清除当前直播间定时器
|
|
|
|
- } // 隐藏时关闭所有连接
|
|
|
|
|
|
+ // 暂停当前直播/回放
|
|
|
|
+ if (currentLive.livingUrl) {
|
|
|
|
+ this.pauseLive(currentLive);
|
|
|
|
+ } else if (currentLive.videoUrl) {
|
|
|
|
+ this.pauseVideo(currentLive);
|
|
|
|
+ }
|
|
|
|
+ this.clearTimeTimer(currentLive);
|
|
|
|
+ }
|
|
this.closeAllWebSockets();
|
|
this.closeAllWebSockets();
|
|
},
|
|
},
|
|
|
|
+
|
|
onUnload() {
|
|
onUnload() {
|
|
- // 清除所有直播间的时间定时器
|
|
|
|
|
|
+ // 1. 销毁所有 live-player 实例
|
|
|
|
+ Object.values(this.livePlayerInstances).forEach(player => {
|
|
|
|
+ try {
|
|
|
|
+ player.stop(); // 停止直播
|
|
|
|
+ } catch (e) {
|
|
|
|
+ console.error('销毁 live-player 失败', e);
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ this.livePlayerInstances = {};
|
|
|
|
+ this.livePlayerStates = {};
|
|
|
|
+ this.liveRetryCounts = {};
|
|
|
|
+
|
|
|
|
+ // 2. 保留原有清理逻辑(WebSocket、定时器、HLS等)
|
|
this.list.forEach(liveItem => {
|
|
this.list.forEach(liveItem => {
|
|
this.clearTimeTimer(liveItem);
|
|
this.clearTimeTimer(liveItem);
|
|
|
|
+ if (liveItem.videoUrl) {
|
|
|
|
+ const videoContext = uni.createVideoContext(`myVideo_${liveItem.liveId}`, this);
|
|
|
|
+ videoContext?.pause();
|
|
|
|
+ }
|
|
});
|
|
});
|
|
- // 关闭所有WebSocket连接
|
|
|
|
this.closeAllWebSockets();
|
|
this.closeAllWebSockets();
|
|
- this.socketInstances = {}; // 强制清空实例对象
|
|
|
|
- // 移除所有全局事件监听
|
|
|
|
this.removeAllEventListeners();
|
|
this.removeAllEventListeners();
|
|
-
|
|
|
|
- // 清理定时器
|
|
|
|
this.clearAllTimers();
|
|
this.clearAllTimers();
|
|
-
|
|
|
|
- // 销毁HLS播放器(如果使用了hls.js)
|
|
|
|
if (this.hlsPlayer) {
|
|
if (this.hlsPlayer) {
|
|
this.hlsPlayer.destroy();
|
|
this.hlsPlayer.destroy();
|
|
this.hlsPlayer = null;
|
|
this.hlsPlayer = null;
|
|
}
|
|
}
|
|
-
|
|
|
|
- this.list.forEach(liveItem => {
|
|
|
|
- const videoId = `myVideo_${liveItem.liveId}`;
|
|
|
|
- const videoContext = uni.createVideoContext(videoId, this);
|
|
|
|
- if (videoContext) {
|
|
|
|
- videoContext.pause(); // 仅暂停视频即可
|
|
|
|
- }
|
|
|
|
- });
|
|
|
|
},
|
|
},
|
|
watch: {
|
|
watch: {
|
|
// 监听orderUser.count的变化
|
|
// 监听orderUser.count的变化
|
|
@@ -565,7 +569,173 @@
|
|
immediate: true
|
|
immediate: true
|
|
}
|
|
}
|
|
},
|
|
},
|
|
- methods: { // 计算当前时间与 liveItem.startTime 的差值,并更新 totalTime
|
|
|
|
|
|
+ methods: {
|
|
|
|
+ // 1. 直播播放器状态变化回调(核心)
|
|
|
|
+ onLivePlayerStateChange(e) {
|
|
|
|
+ const liveId = this.getLiveIdFromPlayerId(e.currentTarget.id); // 从ID中解析liveId
|
|
|
|
+ const state = e.detail.code; // 状态码:0-初始,1-连接中,2-播放中,3-暂停,4-播放结束,5-错误,6-缓冲中
|
|
|
|
+ this.livePlayerStates[liveId] = state; // 记录当前状态
|
|
|
|
+
|
|
|
|
+ const liveItem = this.list.find(item => item.liveId === liveId);
|
|
|
|
+ if (!liveItem) return;
|
|
|
|
+
|
|
|
|
+ // 根据状态码处理逻辑
|
|
|
|
+ switch (state) {
|
|
|
|
+ case 1: // 连接中
|
|
|
|
+ console.log(`直播间${liveId}:正在连接直播流`);
|
|
|
|
+ break;
|
|
|
|
+ case 2: // 播放中
|
|
|
|
+ console.log(`直播间${liveId}:直播已开始`);
|
|
|
|
+ this.liveRetryCounts[liveId] = 0; // 播放成功,重置重连次数
|
|
|
|
+ break;
|
|
|
|
+ case 5: // 错误状态(需重连)
|
|
|
|
+ console.error(`直播间${liveId}:直播播放错误,状态码=${state}`);
|
|
|
|
+ this.handleLivePlayerError(liveItem); // 触发错误处理(重连)
|
|
|
|
+ break;
|
|
|
|
+ case 6: // 缓冲中
|
|
|
|
+ uni.showToast({
|
|
|
|
+ title: '直播缓冲中...',
|
|
|
|
+ icon: 'none',
|
|
|
|
+ duration: 1000
|
|
|
|
+ });
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ // 2. 直播错误回调(补充 statechange 未覆盖的错误场景)
|
|
|
|
+ livePlayerError(e) {
|
|
|
|
+ const liveId = this.getLiveIdFromPlayerId(e.currentTarget.id);
|
|
|
|
+ const liveItem = this.list.find(item => item.liveId === liveId);
|
|
|
|
+ if (liveItem) {
|
|
|
|
+ this.handleLivePlayerError(liveItem);
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ // 3. 直播错误统一处理(重连逻辑)
|
|
|
|
+ handleLivePlayerError(liveItem) {
|
|
|
|
+ const liveId = liveItem.liveId;
|
|
|
|
+ // 初始化重连次数
|
|
|
|
+ if (this.liveRetryCounts[liveId] === undefined) {
|
|
|
|
+ this.liveRetryCounts[liveId] = 0;
|
|
|
|
+ }
|
|
|
|
+ // 超过最大重连次数,停止重试
|
|
|
|
+ if (this.liveRetryCounts[liveId] >= this.maxLiveRetry) {
|
|
|
|
+ uni.showToast({
|
|
|
|
+ title: `直播连接失败,请检查网络(已重试${this.maxLiveRetry}次)`,
|
|
|
|
+ icon: 'none',
|
|
|
|
+ duration: 3000
|
|
|
|
+ });
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ // 重连计数+1,延迟重试(指数退避:1s→2s→4s)
|
|
|
|
+ this.liveRetryCounts[liveId]++;
|
|
|
|
+ const delay = 1000 * Math.pow(2, this.liveRetryCounts[liveId] - 1);
|
|
|
|
+ setTimeout(() => {
|
|
|
|
+ console.log(`直播间${liveId}:第${this.liveRetryCounts[liveId]}次重连`);
|
|
|
|
+ this.playLive(liveItem); // 触发重连播放
|
|
|
|
+ }, delay);
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ // 4. 播放直播(获取 live-player 实例并控制播放)
|
|
|
|
+ playLive(liveItem) {
|
|
|
|
+ if (!liveItem || !liveItem.livingUrl) return;
|
|
|
|
+ const playerId = `myLivePlayer_${liveItem.liveId}`;
|
|
|
|
+
|
|
|
|
+ // 获取 live-player 实例(兼容小程序异步获取)
|
|
|
|
+ const getPlayerInstance = () => {
|
|
|
|
+ return new Promise((resolve) => {
|
|
|
|
+ const player = uni.createLivePlayerContext(playerId, this);
|
|
|
|
+ if (player) {
|
|
|
|
+ this.livePlayerInstances[liveItem.liveId] = player; // 缓存实例
|
|
|
|
+ resolve(player);
|
|
|
|
+ } else {
|
|
|
|
+ // 实例获取失败,100ms后重试(最多3次)
|
|
|
|
+ let retry = 0;
|
|
|
|
+ const retryGet = setInterval(() => {
|
|
|
|
+ const retryPlayer = uni.createLivePlayerContext(playerId, this);
|
|
|
|
+ if (retryPlayer || retry >= 3) {
|
|
|
|
+ clearInterval(retryGet);
|
|
|
|
+ resolve(retryPlayer);
|
|
|
|
+ }
|
|
|
|
+ retry++;
|
|
|
|
+ }, 100);
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ // 控制播放
|
|
|
|
+ getPlayerInstance().then(player => {
|
|
|
|
+ if (player) {
|
|
|
|
+ player.play({
|
|
|
|
+ success: () => {
|
|
|
|
+ console.log(`直播间${liveItem.liveId}:直播播放指令已发送`);
|
|
|
|
+ },
|
|
|
|
+ fail: (err) => {
|
|
|
|
+ console.error(`直播间${liveItem.liveId}:播放指令失败`, err);
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ // 5. 暂停直播(切换swiper时调用)
|
|
|
|
+ pauseLive(liveItem) {
|
|
|
|
+ if (!liveItem) return;
|
|
|
|
+ const player = this.livePlayerInstances[liveItem.liveId];
|
|
|
|
+ if (player) {
|
|
|
|
+ player.pause({
|
|
|
|
+ success: () => {
|
|
|
|
+ console.log(`直播间${liveItem.liveId}:直播已暂停`);
|
|
|
|
+ },
|
|
|
|
+ fail: (err) => {
|
|
|
|
+ console.error(`直播间${liveItem.liveId}:暂停失败`, err);
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ // 6. 从 playerId 中解析 liveId(辅助方法)
|
|
|
|
+ getLiveIdFromPlayerId(playerId) {
|
|
|
|
+ // playerId 格式:myLivePlayer_123456 → 提取 123456
|
|
|
|
+ return playerId.replace('myLivePlayer_', '');
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ // 7. 全屏变化处理(可选,补全体验)
|
|
|
|
+ onLiveFullScreenChange(e) {
|
|
|
|
+ const isFullScreen = e.detail.fullScreen;
|
|
|
|
+ const liveId = this.getLiveIdFromPlayerId(e.currentTarget.id);
|
|
|
|
+ console.log(`直播间${liveId}:${isFullScreen ? '进入' : '退出'}全屏`);
|
|
|
|
+ // 可根据全屏状态调整页面样式(如隐藏底部输入框)
|
|
|
|
+ this.isLiveFullScreen = isFullScreen;
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ // 红包 卡片 抽奖
|
|
|
|
+ getCurrentActivities(liveItem) {
|
|
|
|
+ if (!liveItem.liveId) return;
|
|
|
|
+ // const res = currentActivities(liveId);
|
|
|
|
+ currentActivities(liveItem.liveId).then(res => {
|
|
|
|
+ if (res.code == 200) {
|
|
|
|
+ console.log("红包 卡片 抽奖res>>>", res)
|
|
|
|
+ // 商品卡片
|
|
|
|
+ this.$set(liveItem, 'goodsCard', res.goods);
|
|
|
|
+ // 红包
|
|
|
|
+ this.$set(liveItem, 'redInfo', res.red[0]);
|
|
|
|
+ this.isShowRed = true
|
|
|
|
+ // 抽奖
|
|
|
|
+ this.$set(liveItem, 'lottery', res.lottery);
|
|
|
|
+ } else {
|
|
|
|
+ uni.showToast({
|
|
|
|
+ title: res.msg,
|
|
|
|
+ icon: 'none'
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ rej => {}
|
|
|
|
+ );
|
|
|
|
+ },
|
|
|
|
+ // 计算当前时间与 liveItem.startTime 的差值,并更新 totalTime
|
|
calculateTimeDiff(liveItem) {
|
|
calculateTimeDiff(liveItem) {
|
|
// 1. 校验 startTime 格式(防止无效时间)
|
|
// 1. 校验 startTime 格式(防止无效时间)
|
|
if (!liveItem.startTime) {
|
|
if (!liveItem.startTime) {
|
|
@@ -714,8 +884,9 @@
|
|
// 然后并行加载其他数据
|
|
// 然后并行加载其他数据
|
|
await Promise.allSettled([
|
|
await Promise.allSettled([
|
|
this.getLiveMsg(currentLive),
|
|
this.getLiveMsg(currentLive),
|
|
|
|
+ this.getCurrentActivities(currentLive),
|
|
this.getliveViewData(currentLive),
|
|
this.getliveViewData(currentLive),
|
|
- this.getShowGoods(currentLive),
|
|
|
|
|
|
+ // this.getShowGoods(currentLive),
|
|
this.getliving(currentLive)
|
|
this.getliving(currentLive)
|
|
]);
|
|
]);
|
|
|
|
|
|
@@ -787,48 +958,47 @@
|
|
const oldIndex = this.currentSwiperIndex;
|
|
const oldIndex = this.currentSwiperIndex;
|
|
|
|
|
|
// 相同索引或节流期间,不处理
|
|
// 相同索引或节流期间,不处理
|
|
- if (newIndex === oldIndex) return;
|
|
|
|
- if (now - this.lastSwiperChangeTime < this.swiperChangeThrottle) {
|
|
|
|
- console.log('Swiper切换过快,已节流');
|
|
|
|
|
|
+ if (newIndex === oldIndex || now - this.lastSwiperChangeTime < this.swiperChangeThrottle) {
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
-
|
|
|
|
- // 更新最后切换时间
|
|
|
|
this.lastSwiperChangeTime = now;
|
|
this.lastSwiperChangeTime = now;
|
|
|
|
|
|
- // 1. 立即关闭旧直播间的WebSocket(同步操作)
|
|
|
|
|
|
+ // 1. 暂停旧直播间(直播用 pauseLive,回放用 pauseVideo)
|
|
const oldLive = this.list[oldIndex];
|
|
const oldLive = this.list[oldIndex];
|
|
- this.closeShop()
|
|
|
|
if (oldLive) {
|
|
if (oldLive) {
|
|
- this.pauseVideo(oldLive);
|
|
|
|
- this.clearTimeTimer(oldLive); // 清除旧直播间的时间定时器
|
|
|
|
- console.log("oldLive", oldLive)
|
|
|
|
- // 强制关闭并删除实例,确保连接被释放
|
|
|
|
|
|
+ if (oldLive.livingUrl) {
|
|
|
|
+ this.pauseLive(oldLive); // 暂停旧直播
|
|
|
|
+ // 清理旧直播状态
|
|
|
|
+ delete this.livePlayerInstances[oldLive.liveId];
|
|
|
|
+ delete this.livePlayerStates[oldLive.liveId];
|
|
|
|
+ delete this.liveRetryCounts[oldLive.liveId];
|
|
|
|
+ } else if (oldLive.videoUrl) {
|
|
|
|
+ this.pauseVideo(oldLive); // 暂停旧回放
|
|
|
|
+ }
|
|
|
|
+ this.clearTimeTimer(oldLive); // 清除旧直播间定时器
|
|
|
|
+ // 关闭旧直播间WebSocket(原有逻辑保留)
|
|
if (this.socketInstances[oldLive.liveId]) {
|
|
if (this.socketInstances[oldLive.liveId]) {
|
|
- try {
|
|
|
|
- this.socketInstances[oldLive.liveId].instance.close({
|
|
|
|
- code: 1000,
|
|
|
|
- reason: '切换直播间'
|
|
|
|
- });
|
|
|
|
- console.log(`关闭旧直播间 ${oldLive.liveId} WebSocket`);
|
|
|
|
- } catch (e) {
|
|
|
|
- console.error('关闭旧连接失败:', e);
|
|
|
|
- }
|
|
|
|
- delete this.socketInstances[oldLive.liveId]; // 彻底删除实例
|
|
|
|
|
|
+ this.closeWebSocket(oldLive.liveId);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- // 2. 处理新直播间(后续逻辑不变)
|
|
|
|
|
|
+ // 2. 处理新直播间
|
|
this.currentSwiperIndex = newIndex;
|
|
this.currentSwiperIndex = newIndex;
|
|
const newLive = this.list[newIndex];
|
|
const newLive = this.list[newIndex];
|
|
if (!newLive) return;
|
|
if (!newLive) return;
|
|
|
|
|
|
this.liveId = newLive.liveId;
|
|
this.liveId = newLive.liveId;
|
|
this.resetUserListParams();
|
|
this.resetUserListParams();
|
|
- await this.initCurrentLiveData(); // 内部会启动新直播间的定时器
|
|
|
|
|
|
+ await this.initCurrentLiveData(); // 初始化当前直播间数据
|
|
this.getliveUser(false);
|
|
this.getliveUser(false);
|
|
|
|
+
|
|
|
|
+ // 3. 播放新直播间(直播用 playLive,回放用 playVideo)
|
|
setTimeout(() => {
|
|
setTimeout(() => {
|
|
- this.playVideo(newLive);
|
|
|
|
|
|
+ if (newLive.livingUrl) {
|
|
|
|
+ this.playLive(newLive); // 播放新直播
|
|
|
|
+ } else if (newLive.videoUrl) {
|
|
|
|
+ this.playVideo(newLive); // 播放新回放
|
|
|
|
+ }
|
|
}, 100);
|
|
}, 100);
|
|
},
|
|
},
|
|
// 获取直播间列表
|
|
// 获取直播间列表
|
|
@@ -922,8 +1092,9 @@
|
|
await Promise.all([
|
|
await Promise.all([
|
|
this.getliving(liveItem),
|
|
this.getliving(liveItem),
|
|
this.getLiveMsg(liveItem),
|
|
this.getLiveMsg(liveItem),
|
|
|
|
+ this.getCurrentActivities(liveItem),
|
|
this.getliveViewData(liveItem),
|
|
this.getliveViewData(liveItem),
|
|
- this.getShowGoods(liveItem)
|
|
|
|
|
|
+ // this.getShowGoods(liveItem)
|
|
]);
|
|
]);
|
|
liveItem.loaded = true;
|
|
liveItem.loaded = true;
|
|
} catch (error) {
|
|
} catch (error) {
|
|
@@ -1051,18 +1222,18 @@
|
|
onRed(item) {
|
|
onRed(item) {
|
|
// console.log("点了this.liveId", item)
|
|
// console.log("点了this.liveId", item)
|
|
if (!item.liveId) return;
|
|
if (!item.liveId) return;
|
|
- // console.log("点了this.liveId", this.liveId)
|
|
|
|
if (!item.redInfo?.redId) return;
|
|
if (!item.redInfo?.redId) return;
|
|
- // console.log("点了this.redInfo?.redId", this.redInfo.redId)
|
|
|
|
- // console.log("点了this.userinfo.userId", this.userinfo.userId)
|
|
|
|
|
|
+
|
|
let data = {
|
|
let data = {
|
|
liveId: item.liveId,
|
|
liveId: item.liveId,
|
|
userId: this.userinfo.userId,
|
|
userId: this.userinfo.userId,
|
|
redId: item.redInfo.redId,
|
|
redId: item.redInfo.redId,
|
|
}
|
|
}
|
|
- console.log("点了this.liveId", data)
|
|
|
|
liveRed(data).then(res => {
|
|
liveRed(data).then(res => {
|
|
|
|
+ this.isShowRed = false
|
|
if (res.code == 200) {
|
|
if (res.code == 200) {
|
|
|
|
+ console.log(this.isShowRed)
|
|
|
|
+ console.log("点了this.liveId", liveItem.redInfo)
|
|
uni.showToast({
|
|
uni.showToast({
|
|
title: res.msg,
|
|
title: res.msg,
|
|
icon: 'none'
|
|
icon: 'none'
|
|
@@ -1338,56 +1509,7 @@
|
|
console.error('点赞失败:', error);
|
|
console.error('点赞失败:', error);
|
|
}
|
|
}
|
|
},
|
|
},
|
|
- // 点赞
|
|
|
|
- // async onLike(liveItem) {
|
|
|
|
- // // 基础校验
|
|
|
|
- // if (!liveItem || !liveItem.liveId) return;
|
|
|
|
-
|
|
|
|
- // // 确保liveViewData存在(初始化默认值)
|
|
|
|
- // if (!liveItem.liveViewData) {
|
|
|
|
- // await this.getliveViewData(liveItem);
|
|
|
|
- // if (!liveItem.liveViewData) {
|
|
|
|
- // uni.showToast({
|
|
|
|
- // title: "数据加载中,请稍后再试",
|
|
|
|
- // icon: 'none'
|
|
|
|
- // });
|
|
|
|
- // return;
|
|
|
|
- // }
|
|
|
|
- // // 初始化点赞数(防止undefined)
|
|
|
|
- // liveItem.liveViewData.like = liveItem.liveViewData.like || 0;
|
|
|
|
- // }
|
|
|
|
-
|
|
|
|
- // // 限制最多10次点赞
|
|
|
|
- // const maxLikeCount = 10;
|
|
|
|
- // if (liveItem.liveViewData.like >= maxLikeCount) {
|
|
|
|
- // uni.showToast({
|
|
|
|
- // title: "最多可点赞10次哦~",
|
|
|
|
- // icon: 'none'
|
|
|
|
- // });
|
|
|
|
- // return;
|
|
|
|
- // }
|
|
|
|
|
|
|
|
- // try {
|
|
|
|
- // const res = await liveDataLike(liveItem.liveId);
|
|
|
|
- // if (res.code === 200) {
|
|
|
|
- // // 无论后端是否返回res.like,前端按规则累加(最多到10次)
|
|
|
|
- // const newLikeCount = Math.min(liveItem.liveViewData.like + 1, maxLikeCount);
|
|
|
|
- // liveItem.liveViewData.like = newLikeCount;
|
|
|
|
-
|
|
|
|
- // } else {
|
|
|
|
- // uni.showToast({
|
|
|
|
- // title: res.msg || "点赞失败",
|
|
|
|
- // icon: 'none'
|
|
|
|
- // });
|
|
|
|
- // }
|
|
|
|
- // } catch (error) {
|
|
|
|
- // console.error('点赞失败:', error);
|
|
|
|
- // uni.showToast({
|
|
|
|
- // title: "网络异常,点赞失败",
|
|
|
|
- // icon: 'none'
|
|
|
|
- // });
|
|
|
|
- // }
|
|
|
|
- // },
|
|
|
|
//直播间点赞、关注、在线人数数据
|
|
//直播间点赞、关注、在线人数数据
|
|
async getliveViewData(liveItem) {
|
|
async getliveViewData(liveItem) {
|
|
if (!liveItem || !liveItem.liveId) return;
|
|
if (!liveItem || !liveItem.liveId) return;
|
|
@@ -1407,17 +1529,17 @@
|
|
}
|
|
}
|
|
},
|
|
},
|
|
//弹出商品卡片
|
|
//弹出商品卡片
|
|
- async getShowGoods(liveItem) {
|
|
|
|
- if (!liveItem || !liveItem.liveId) return;
|
|
|
|
- try {
|
|
|
|
- const res = await showGoods(liveItem.liveId);
|
|
|
|
- if (res.code == 200) {
|
|
|
|
- this.$set(liveItem, 'goodsCard', res);
|
|
|
|
- }
|
|
|
|
- } catch (error) {
|
|
|
|
- console.error("暂无商品卡片", error);
|
|
|
|
- }
|
|
|
|
- },
|
|
|
|
|
|
+ // async getShowGoods(liveItem) {
|
|
|
|
+ // if (!liveItem || !liveItem.liveId) return;
|
|
|
|
+ // try {
|
|
|
|
+ // const res = await showGoods(liveItem.liveId);
|
|
|
|
+ // if (res.code == 200) {
|
|
|
|
+ // this.$set(liveItem, 'goodsCard', res);
|
|
|
|
+ // }
|
|
|
|
+ // } catch (error) {
|
|
|
|
+ // console.error("暂无商品卡片", error);
|
|
|
|
+ // }
|
|
|
|
+ // },
|
|
// 去购买,跳商品详情
|
|
// 去购买,跳商品详情
|
|
goShop(productId, goodsId) {
|
|
goShop(productId, goodsId) {
|
|
const currentLive = this.list[this.currentSwiperIndex];
|
|
const currentLive = this.list[this.currentSwiperIndex];
|
|
@@ -1743,91 +1865,57 @@
|
|
// 处理Socket消息
|
|
// 处理Socket消息
|
|
handleSocketMessage(message, liveItem) {
|
|
handleSocketMessage(message, liveItem) {
|
|
let data = JSON.parse(message.data)
|
|
let data = JSON.parse(message.data)
|
|
- const socketMessage = data.data
|
|
|
|
- // console.log("处理",data)
|
|
|
|
|
|
+ const socketMessage = data.data // 服务端返回的消息体
|
|
if (data.code == 200) {
|
|
if (data.code == 200) {
|
|
const messageData = {
|
|
const messageData = {
|
|
- ...socketMessage, // 原数据(可能包含部分字段)
|
|
|
|
- cmd: socketMessage.cmd || '' // 强制添加cmd,确保存在(默认空字符串)
|
|
|
|
|
|
+ ...socketMessage,
|
|
|
|
+ cmd: socketMessage.cmd || '', // 确保cmd字段存在
|
|
|
|
+ ts: Date.now() // 新增时间戳,用于排序(可选)
|
|
};
|
|
};
|
|
- // console.log("messageData是",messageData)
|
|
|
|
|
|
+
|
|
|
|
+ // 【关键修改2】处理服务端返回的sendMsg消息,加入本地列表
|
|
if (socketMessage.cmd == 'sendMsg') {
|
|
if (socketMessage.cmd == 'sendMsg') {
|
|
- // this.$set(liveItem, 'talklist', [...liveItem.talklist, messageData]);
|
|
|
|
|
|
+ // 1. 将消息追加到当前直播间的talklist中(响应式更新)
|
|
|
|
+ this.$set(liveItem, 'talklist', [...(liveItem.talklist || []), messageData]);
|
|
|
|
+ // 2. 延迟下一帧滚动到最新消息(确保DOM已更新)
|
|
this.$nextTick(() => {
|
|
this.$nextTick(() => {
|
|
- this.$set(liveItem, 'scrollIntoView', `list_${liveItem.talklist.length-1}`);
|
|
|
|
|
|
+ const lastIndex = liveItem.talklist.length - 1;
|
|
|
|
+ this.$set(liveItem, 'scrollIntoView', `list_${lastIndex}`);
|
|
});
|
|
});
|
|
- } else if (socketMessage.cmd == 'red') {
|
|
|
|
- // 领红包
|
|
|
|
|
|
+ }
|
|
|
|
+ // 以下是原有其他cmd的处理逻辑,保持不变
|
|
|
|
+ else if (socketMessage.cmd == 'red') {
|
|
this.$set(liveItem, 'redInfo', JSON.parse(socketMessage.data));
|
|
this.$set(liveItem, 'redInfo', JSON.parse(socketMessage.data));
|
|
- console.log("红包是liveItem.redInfo", liveItem)
|
|
|
|
- // liveItem.redInfo = JSON.parse(message.data.data);
|
|
|
|
-
|
|
|
|
- } else if (socketMessage.cmd == 'sendRedPacketQuestion') {
|
|
|
|
- const list = JSON.parse(message.data.data);
|
|
|
|
- // 处理红包答题逻辑
|
|
|
|
|
|
+ this.isShowRed = true
|
|
|
|
+ this.timer = setInterval(() => {
|
|
|
|
+ this.isShowRed = true
|
|
|
|
+ }, liveItem.redInfo.duration * 1000)
|
|
} else if (socketMessage.cmd == 'entry' || socketMessage.cmd == 'out') {
|
|
} else if (socketMessage.cmd == 'entry' || socketMessage.cmd == 'out') {
|
|
-
|
|
|
|
this.$set(liveItem, 'talklist', [...liveItem.talklist, messageData]);
|
|
this.$set(liveItem, 'talklist', [...liveItem.talklist, messageData]);
|
|
- // 先重置为 false(避免残留状态)
|
|
|
|
this.$set(liveItem, 'showWelcomeMessage', false);
|
|
this.$set(liveItem, 'showWelcomeMessage', false);
|
|
- // 立即设为 true(触发 DOM 更新)
|
|
|
|
this.$set(liveItem, 'showWelcomeMessage', true);
|
|
this.$set(liveItem, 'showWelcomeMessage', true);
|
|
- this.getliveUser(false); // 调用获取在线用户接口
|
|
|
|
- // console.log("开启欢迎消息:", liveItem.showWelcomeMessage);
|
|
|
|
- if (liveItem.welcomeTimer) {
|
|
|
|
- clearTimeout(liveItem.welcomeTimer);
|
|
|
|
- }
|
|
|
|
- // 存储定时器到 liveItem 中(避免引用丢失)
|
|
|
|
|
|
+ this.getliveUser(false);
|
|
|
|
+ if (liveItem.welcomeTimer) clearTimeout(liveItem.welcomeTimer);
|
|
liveItem.welcomeTimer = setTimeout(() => {
|
|
liveItem.welcomeTimer = setTimeout(() => {
|
|
this.$set(liveItem, 'showWelcomeMessage', false);
|
|
this.$set(liveItem, 'showWelcomeMessage', false);
|
|
- // console.log("关闭欢迎消息:", liveItem.showWelcomeMessage);
|
|
|
|
- }, 1000); // 1秒后隐藏
|
|
|
|
|
|
+ }, 1000);
|
|
} else if (socketMessage.cmd == 'Integral') {
|
|
} else if (socketMessage.cmd == 'Integral') {
|
|
let integral = {
|
|
let integral = {
|
|
msg: socketMessage.msg,
|
|
msg: socketMessage.msg,
|
|
status: true
|
|
status: true
|
|
}
|
|
}
|
|
- // 观看奖励事件 弹出框2s 显示msg
|
|
|
|
this.$set(liveItem, 'integral', integral);
|
|
this.$set(liveItem, 'integral', integral);
|
|
- // uni.showToast({
|
|
|
|
- // title: socketMessage.msg,
|
|
|
|
- // icon: 'none',
|
|
|
|
- // duration: 2000
|
|
|
|
- // });
|
|
|
|
-
|
|
|
|
} else if (socketMessage.cmd == 'blockUser') {
|
|
} else if (socketMessage.cmd == 'blockUser') {
|
|
- //拉黑事件 主动删除用户token,强制跳转到登录页面
|
|
|
|
uni.removeStorage({
|
|
uni.removeStorage({
|
|
- key: 'AppToken', // 存储 Token 时的 key
|
|
|
|
|
|
+ key: 'AppToken',
|
|
success: () => {
|
|
success: () => {
|
|
uni.reLaunch({
|
|
uni.reLaunch({
|
|
- url: '/pages/auth/login' // 登录页路径,需根据项目实际路径调整
|
|
|
|
|
|
+ url: '/pages/auth/login'
|
|
});
|
|
});
|
|
- console.log('Token 删除成功');
|
|
|
|
- },
|
|
|
|
- fail: (err) => {
|
|
|
|
- console.error('Token 删除失败:', err);
|
|
|
|
}
|
|
}
|
|
});
|
|
});
|
|
} else if (socketMessage.cmd == 'goods') {
|
|
} else if (socketMessage.cmd == 'goods') {
|
|
this.$set(liveItem, 'goodsCard', JSON.parse(socketMessage.data));
|
|
this.$set(liveItem, 'goodsCard', JSON.parse(socketMessage.data));
|
|
- } else if (socketMessage.cmd == 'out') {
|
|
|
|
- // console.log("进入:", liveItem);
|
|
|
|
- console.log("有人退出了:");
|
|
|
|
-
|
|
|
|
- } else if (socketMessage.cmd == 'live_start') {
|
|
|
|
- // 直播开始
|
|
|
|
- } else if (socketMessage.cmd == 'live_end') {
|
|
|
|
- // 直播结束
|
|
|
|
- } else if (socketMessage.data.cmd == 'deleteId') {
|
|
|
|
- uni.$emit('deleteId');
|
|
|
|
- } else if (socketMessage.data.cmd == 'sendRedPacketQuestion') {
|
|
|
|
- // const list = JSON.parse(socketMessage)
|
|
|
|
- // liveItem.redanswerAll = [...liveItem.redanswerAll, ...list]
|
|
|
|
- // if (liveItem.redanswerAll[1].randomAmount !== null) {
|
|
|
|
- // liveItem.redanswertips = JSON.parse(liveItem.redanswerAll[0]
|
|
|
|
- // .randomAmount)
|
|
|
|
- // }
|
|
|
|
}
|
|
}
|
|
} else {
|
|
} else {
|
|
uni.showToast({
|
|
uni.showToast({
|
|
@@ -1835,7 +1923,6 @@
|
|
icon: 'none'
|
|
icon: 'none'
|
|
});
|
|
});
|
|
}
|
|
}
|
|
-
|
|
|
|
},
|
|
},
|
|
scheduleReconnect(liveId) {
|
|
scheduleReconnect(liveId) {
|
|
if (this.isManualClose || this.reconnectCount >= this.maxReconnectAttempts) return;
|
|
if (this.isManualClose || this.reconnectCount >= this.maxReconnectAttempts) return;
|
|
@@ -1871,22 +1958,12 @@
|
|
const liveId = liveItem.liveId;
|
|
const liveId = liveItem.liveId;
|
|
const socketItem = this.socketInstances[liveId];
|
|
const socketItem = this.socketInstances[liveId];
|
|
|
|
|
|
- // 乐观更新:立即把消息加入到本地聊天列表并清空输入(更好的体验)
|
|
|
|
- const localMsg = {
|
|
|
|
- cmd: 'sendMsg',
|
|
|
|
- msg: text,
|
|
|
|
- nickName: this.userinfo.nickName,
|
|
|
|
- avatar: this.userinfo.avatar,
|
|
|
|
- userId: this.userinfo.userId,
|
|
|
|
- local: true, // 标记为本地消息(可用于样式或后续差异处理)
|
|
|
|
- ts: Date.now()
|
|
|
|
- };
|
|
|
|
- this.$set(liveItem, 'talklist', [...(liveItem.talklist || []), localMsg]);
|
|
|
|
- this.$set(liveItem, 'value', ''); // 立即清空输入
|
|
|
|
|
|
+ // 【关键修改1】删除“发送时直接加入本地列表”的代码,仅清空输入框
|
|
|
|
+ this.$set(liveItem, 'value', ''); // 立即清空输入框,提升体验
|
|
|
|
|
|
|
|
+ // 检查socket连接状态
|
|
if (!socketItem || !socketItem.instance || !socketItem.isOpen) {
|
|
if (!socketItem || !socketItem.instance || !socketItem.isOpen) {
|
|
- // 如果 socket 未连接,尝试建立连接(但不要阻塞用户)
|
|
|
|
- this.initSocket(liveItem);
|
|
|
|
|
|
+ this.initSocket(liveItem); // 尝试重连
|
|
uni.showToast({
|
|
uni.showToast({
|
|
title: "连接未建立,正在重连",
|
|
title: "连接未建立,正在重连",
|
|
icon: 'none'
|
|
icon: 'none'
|
|
@@ -1894,21 +1971,23 @@
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ // 构造发送给服务端的消息数据
|
|
const data = {
|
|
const data = {
|
|
liveId,
|
|
liveId,
|
|
userId: this.userinfo.userId,
|
|
userId: this.userinfo.userId,
|
|
userType: 0,
|
|
userType: 0,
|
|
- cmd: "sendMsg",
|
|
|
|
|
|
+ cmd: "sendMsg", // 指令标识,服务端会根据此返回sendMsg消息
|
|
msg: text,
|
|
msg: text,
|
|
nickName: this.userinfo.nickName,
|
|
nickName: this.userinfo.nickName,
|
|
avatar: this.userinfo.avatar
|
|
avatar: this.userinfo.avatar
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+ // 发送socket消息
|
|
try {
|
|
try {
|
|
socketItem.instance.send({
|
|
socketItem.instance.send({
|
|
data: JSON.stringify(data),
|
|
data: JSON.stringify(data),
|
|
success: () => {
|
|
success: () => {
|
|
- // 比如找到最后一条 local 消息并标记为 sent
|
|
|
|
|
|
+ // 发送成功无需额外操作,等待服务端返回消息后再更新列表
|
|
},
|
|
},
|
|
fail: (err) => {
|
|
fail: (err) => {
|
|
console.error("发送失败:", err);
|
|
console.error("发送失败:", err);
|
|
@@ -1916,13 +1995,17 @@
|
|
title: "发送失败,请重试",
|
|
title: "发送失败,请重试",
|
|
icon: 'none'
|
|
icon: 'none'
|
|
});
|
|
});
|
|
- // 可选择回滚本地显示(此处简单提示即可)
|
|
|
|
|
|
+ // 发送失败时可恢复输入框内容(可选,提升体验)
|
|
|
|
+ this.$set(liveItem, 'value', text);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
} catch (err) {
|
|
} catch (err) {
|
|
console.error('socket send 异常', err);
|
|
console.error('socket send 异常', err);
|
|
|
|
+ // 异常时恢复输入框内容(可选)
|
|
|
|
+ this.$set(liveItem, 'value', text);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+
|
|
}
|
|
}
|
|
};
|
|
};
|
|
</script>
|
|
</script>
|
|
@@ -2129,6 +2212,8 @@
|
|
z-index: 5;
|
|
z-index: 5;
|
|
border-radius: 16rpx;
|
|
border-radius: 16rpx;
|
|
background-color: rgba(77, 77, 77, 0.5);
|
|
background-color: rgba(77, 77, 77, 0.5);
|
|
|
|
+ width: 90rpx;
|
|
|
|
+ height: 100rpx;
|
|
|
|
|
|
.tip {
|
|
.tip {
|
|
position: absolute;
|
|
position: absolute;
|
|
@@ -2139,7 +2224,6 @@
|
|
background-color: rgba(15, 15, 15, 0.8);
|
|
background-color: rgba(15, 15, 15, 0.8);
|
|
border-radius: 16rpx;
|
|
border-radius: 16rpx;
|
|
text-align: center;
|
|
text-align: center;
|
|
- padding: 4rpx 0;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
.item {
|
|
.item {
|
|
@@ -2147,7 +2231,7 @@
|
|
|
|
|
|
image {
|
|
image {
|
|
width: 80rpx;
|
|
width: 80rpx;
|
|
- margin: 6rpx 0;
|
|
|
|
|
|
+ height: 100%;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -2255,6 +2339,35 @@
|
|
width: 100%;
|
|
width: 100%;
|
|
background-color: rgba(57, 57, 57, 0.4);
|
|
background-color: rgba(57, 57, 57, 0.4);
|
|
|
|
|
|
|
|
+ .videolist {
|
|
|
|
+ position: relative;
|
|
|
|
+
|
|
|
|
+ .video {
|
|
|
|
+ height: 100vh;
|
|
|
|
+ width: 100%;
|
|
|
|
+ background-color: rgba(57, 57, 57, 0.4);
|
|
|
|
+
|
|
|
|
+ .time {
|
|
|
|
+ color: #ffffff;
|
|
|
|
+ font-size: 20rpx;
|
|
|
|
+ margin-left: 10rpx;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 横竖屏样式(直播和回放共用)
|
|
|
|
+ .videotop {
|
|
|
|
+ width: 100%;
|
|
|
|
+ height: 100%;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ .video_row {
|
|
|
|
+ width: 100%;
|
|
|
|
+ max-height: 500rpx;
|
|
|
|
+ overflow: hidden;
|
|
|
|
+ margin-top: 360rpx;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
.time {
|
|
.time {
|
|
color: #ffffff;
|
|
color: #ffffff;
|
|
font-size: 20rpx;
|
|
font-size: 20rpx;
|
|
@@ -2408,24 +2521,6 @@
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
-
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- .icon {
|
|
|
|
- font-weight: 500;
|
|
|
|
- font-size: 24rpx;
|
|
|
|
- color: #FFFFFF;
|
|
|
|
- text-align: center;
|
|
|
|
- margin-bottom: -10rpx;
|
|
|
|
-
|
|
|
|
- .button {
|
|
|
|
- background-color: transparent;
|
|
|
|
- margin: 0;
|
|
|
|
- line-height: 1;
|
|
|
|
- padding: 0;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
.icon-bg {
|
|
.icon-bg {
|
|
background-color: rgba(57, 57, 57, 0.8);
|
|
background-color: rgba(57, 57, 57, 0.8);
|
|
border-radius: 50%;
|
|
border-radius: 50%;
|
|
@@ -2518,7 +2613,8 @@
|
|
background: #fff;
|
|
background: #fff;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-// 抽奖
|
|
|
|
|
|
+
|
|
|
|
+ // 抽奖
|
|
.answerpop {
|
|
.answerpop {
|
|
background: linear-gradient(to right, #fff7f8, #fee1e2);
|
|
background: linear-gradient(to right, #fff7f8, #fee1e2);
|
|
margin-top: 30rpx;
|
|
margin-top: 30rpx;
|
|
@@ -2548,6 +2644,7 @@
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+
|
|
.shoppop {
|
|
.shoppop {
|
|
padding: 22rpx 16rpx;
|
|
padding: 22rpx 16rpx;
|
|
|
|
|