|
@@ -7,11 +7,11 @@
|
|
|
<view class="blackbg"></view>
|
|
|
<view class="content">
|
|
|
<!-- 页面内容 -->
|
|
|
- <view style=" position: fixed;top: 0;z-index: 5;" class="content-top">
|
|
|
+ <view style="position: fixed;top: 0;z-index: 5;" class="content-top">
|
|
|
<view class="u-flex-y-center">
|
|
|
<image @click="goBack" style="width: 60rpx;height: 64rpx;margin-right: 26rpx;"
|
|
|
- src="@/static/images/live/return.png"></image>
|
|
|
- <view class=" align-center"
|
|
|
+ src="/static/images/live/return.png"></image>
|
|
|
+ <view class="align-center"
|
|
|
style="padding: 6rpx 4rpx;height: 64rpx;background: rgba(0,0,0,0.5);border-radius: 32rpx;">
|
|
|
<u-avatar :src="liveItem.liveImgUrl||$img.logo" size="32"></u-avatar>
|
|
|
<view class="colorf ml10 mr6">
|
|
@@ -19,7 +19,7 @@
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
- <view class="u-flex-end align-center" @click="showadd=!showadd" style="margin-top: 100rpx;">
|
|
|
+ <view class="u-flex-end align-center" @click="toggleViewerList" style="margin-top: 100rpx;">
|
|
|
<image class="w52 h52 mr4" v-for="(item,viewerIndex) in filteredViewers"
|
|
|
:key="viewerIndex" style="border-radius: 26rpx;" :src="item.avatar||$img.logo">
|
|
|
</image>
|
|
@@ -27,32 +27,14 @@
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
- <!-- 页面内容 -->
|
|
|
- <view style=" position: fixed;top:120rpx;left: 0rpx; z-index: 5;" class="content-top">
|
|
|
+ <!-- 红包内容 -->
|
|
|
+ <view style="position: fixed;top:120rpx;left: 0rpx; z-index: 5;" class="content-top">
|
|
|
<view class="u-flex-y-center">
|
|
|
<image @click="onRed(liveItem)" v-if="liveItem.redInfo?.redStatus==1"
|
|
|
- style="width: 70rpx;height: 70rpx;" src="@/static/images/hongbao.png"></image>
|
|
|
+ style="width: 70rpx;height: 70rpx;" src="/static/images/hongbao.png"></image>
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
- <!-- 右边的 -->
|
|
|
- <!-- <view :class=" liveItem.showType==1 ? 'siderow-group' : 'side-group'">
|
|
|
- <view class="side-item">
|
|
|
- <image class="image" @click="onLike(liveItem)" src="/static/images/live/like.png"></image>
|
|
|
- <view>{{liveItem.liveViewData?.like||0}}</view>
|
|
|
- </view>
|
|
|
- <view class="side-item">
|
|
|
- <button open-type="share" class="button">
|
|
|
- <image class="image" src="/static/images/live/share.png" mode="widthFix"></image>
|
|
|
- </button>
|
|
|
- <view>分享</view>
|
|
|
- </view>
|
|
|
- <view class="side-item">
|
|
|
- <image @click="goStore(liveItem)" src="/static/images/live/shop.png"></image>
|
|
|
- <view>店铺</view>
|
|
|
- </view>
|
|
|
- </view> -->
|
|
|
-
|
|
|
<view class="shop-prompt u-flex-y-center" v-if="showPurchasePrompt&&orderUser?.count">
|
|
|
<image class="w32 h32 mr8" src="/static/images/live/shopping.png"></image>
|
|
|
<text> {{orderUser.userName ? maskString(orderUser.userName) : ''}} 等
|
|
@@ -60,10 +42,9 @@
|
|
|
人正在去购买</text>
|
|
|
</view>
|
|
|
|
|
|
- <view class="videolist " style="margin: auto 0">
|
|
|
+ <view class="videolist" style="margin: auto 0">
|
|
|
<view class="video" style="height:100vh">
|
|
|
- <!-- 修改video组件以支持HLS -->
|
|
|
- <!-- showType 1 横屏 2 竖屏 -->
|
|
|
+ <!-- 视频组件 -->
|
|
|
<video v-if="liveItem.livingUrl" :id="'myVideo_' + liveItem.liveId"
|
|
|
:class="liveItem.showType == 1 ? 'video_row' : 'videotop'" :src="liveItem.livingUrl"
|
|
|
:autoplay="currentSwiperIndex === index" :controls='false' object-fit='contain'
|
|
@@ -79,7 +60,7 @@
|
|
|
vslide-gesture-in-fullscreen='true' :show-center-play-btn="false"
|
|
|
:http-cache="false" loop @error="videoError">
|
|
|
</video>
|
|
|
-
|
|
|
+ <view v-if="liveItem.videoUrl" class="time">{{liveItem.totalTime}}</view>
|
|
|
<!-- 加载提示 -->
|
|
|
<view v-if="liveItem.loading" class="loading-tip">
|
|
|
<u-loading mode="circle" color="#ffffff" size="36"></u-loading>
|
|
@@ -87,7 +68,9 @@
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
- <view class="pb40 mt90" style="position: fixed;bottom: 0;width: 100%;">
|
|
|
+
|
|
|
+ <!-- 底部聊天区域 -->
|
|
|
+ <view class="pb40 mt90" style="position: fixed;bottom: 0;width: 100%;">
|
|
|
<view class="w100 h300 mt20">
|
|
|
<scroll-view enable-flex scroll-y="true" class="talk p20 scrolly flex-1 column"
|
|
|
style="width: calc(100% - 40rpx);height: calc(100% - 40rpx);"
|
|
@@ -120,8 +103,6 @@
|
|
|
<view class="talk-list ml16 justify-start">
|
|
|
<view class="fs24">
|
|
|
<text style="color: #ff89d6;">{{item.nickName}} </text>
|
|
|
- <!-- <text class='fs24 colorf'>
|
|
|
- {{item.msg}}直播间{{liveItem.messageContent}}</text> -->
|
|
|
<text class='fs24 colorf'>
|
|
|
{{item.msg}}直播间{{liveItem.messageContent}}</text>
|
|
|
</view>
|
|
@@ -131,6 +112,7 @@
|
|
|
</scroll-view>
|
|
|
</view>
|
|
|
|
|
|
+ <!-- 底部输入框和操作按钮 -->
|
|
|
<view class="justify-between p24">
|
|
|
<view class="u-flex-y-center"
|
|
|
style="background:rgba(57, 57, 57, 0.8);padding:10rpx 14rpx 10rpx 32rpx;width: 500rpx;box-sizing:border-box;border-radius:36rpx;">
|
|
@@ -140,18 +122,7 @@
|
|
|
</u-input>
|
|
|
<view class="send" @click="sendMsg(liveItem)">发送</view>
|
|
|
</view>
|
|
|
- <view class="justify-between mr15 align-center">
|
|
|
- <!-- <view class="side-item">
|
|
|
- <image class="image" @click="onLike(liveItem)" src="/static/images/live/like.png"></image>
|
|
|
- <view>{{liveItem.liveViewData?.like||0}}</view>
|
|
|
- </view>
|
|
|
- <view class="side-item">
|
|
|
- <button open-type="share" class="button">
|
|
|
- <image class="image" src="/static/images/live/share.png" mode="widthFix"></image>
|
|
|
- </button>
|
|
|
- <view>分享</view>
|
|
|
- </view>
|
|
|
- -->
|
|
|
+ <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>
|
|
@@ -162,36 +133,29 @@
|
|
|
</button>
|
|
|
<view>分享</view>
|
|
|
</view>
|
|
|
-
|
|
|
- <!-- <view class="icon-bg ml20" @click="openCart(liveItem)">
|
|
|
- <image src="/static/images/shopping.png" class="w48 h48"></image>
|
|
|
- </view> -->
|
|
|
- <!-- <view class="icon-bg ml20" @click="liveItem.showziliao=!liveItem.showziliao">
|
|
|
- <image src="/static/images/more-icon.png" class="w48 h48"></image>
|
|
|
- </view> -->
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
<!-- 弹出商品 -->
|
|
|
- <view @click="" class="goods">
|
|
|
+ <!-- v-if="liveItem.goodsCard.isShow" -->
|
|
|
+ <view class="goods">
|
|
|
<view class="top">
|
|
|
<view class="left">
|
|
|
- <image class="w30 h30" src="/static/images/signal.png"></image>讲解中
|
|
|
+ <image class="w30 h30 mr8" src="/static/images/signal.png"></image>讲解中
|
|
|
</view>
|
|
|
- <image class="w30 h30" src="/static/images/close.png"></image>
|
|
|
+ <image class="w30 h30" src="/static/images/close.png" @click="liveItem.goodsCard.isShow=false"></image>
|
|
|
</view>
|
|
|
- <image class="photo" src="@/static/images/a.jpg"></image>
|
|
|
+ <image class="photo" :src="liveItem.goodsCard.imgUrl"></image>
|
|
|
<view class="item">
|
|
|
- <view class="price"><text class="red">23.55</text><text class="del">26.66</text></view>
|
|
|
- <view class="title">新鲜土鸡蛋</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>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
-
|
|
|
-
|
|
|
+ <!-- 观众列表弹窗 -->
|
|
|
<u-popup :show="showadd" @close="close" @open="openViews" round='20rpx' bgColor='#ffffff'>
|
|
|
<view class="view-box">
|
|
|
<view class="t-c fs30">在线观众</view>
|
|
@@ -209,21 +173,17 @@
|
|
|
<text>没有更多了</text>
|
|
|
</view>
|
|
|
</scroll-view>
|
|
|
- <!-- <view class="fs24 u-flex-y-center bottom">
|
|
|
- <view class="mr10" style="width:50rpx">我</view>
|
|
|
- <u-avatar :src="livedata.liveImgUrl||$img.logo" size="32"></u-avatar>
|
|
|
- <text class="ml16">{{livedata.liveName||"未命名"}}</text>
|
|
|
- </view> -->
|
|
|
</view>
|
|
|
</u-popup>
|
|
|
|
|
|
+ <!-- 商品弹窗 -->
|
|
|
<u-popup :show="liveItem.shopping" @close="closeShop" @open="openShop" round='20rpx'
|
|
|
bgColor='f3f5f9'>
|
|
|
<view class="shoppop">
|
|
|
<view class="shoppop-top">
|
|
|
<u-avatar :src="store.logoUrl" size="36" class="ml16"></u-avatar>
|
|
|
<view class="search-input u-flex-y-center">
|
|
|
- <image style="width: 24rpx;margin-right: 16rpx;" src="@/static/images/search.png"
|
|
|
+ <image style="width: 24rpx;margin-right: 16rpx;" src="/static/images/search.png"
|
|
|
mode="widthFix">
|
|
|
</image>
|
|
|
<input placeholder="请搜索商品" v-model="liveItem.inputInfo"
|
|
@@ -231,20 +191,20 @@
|
|
|
</view>
|
|
|
<view class="t-c search-top" style="margin-right: 48rpx;">
|
|
|
<image @click="onStoreCollect(liveItem)" v-if="store.isFavorite"
|
|
|
- style="width:40rpx;height: 40rpx;" src="@/static/images/collect_select.png">
|
|
|
+ style="width:40rpx;height: 40rpx;" src="/static/images/collect_select.png">
|
|
|
</image>
|
|
|
<image v-else @click="onStoreCollect(liveItem)" style="width: 32rpx;height: 32rpx;"
|
|
|
- src="@/static/images/collect.png"></image>
|
|
|
+ src="/static/images/collect.png"></image>
|
|
|
<view>收藏</view>
|
|
|
</view>
|
|
|
<view class="t-c search-top" @click="goOrderList(liveItem)">
|
|
|
- <image style="width: 40rpx;height: 40rpx;" src="@/static/images/order.png"></image>
|
|
|
+ <image style="width: 40rpx;height: 40rpx;" src="/static/images/order.png"></image>
|
|
|
<view>订单</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
<scroll-view enable-flex scroll-y class="shop-list" :style="{ height: boxHeight + 'px' }">
|
|
|
- <view class="list-item " v-for="(item,index) in products" :key="index">
|
|
|
+ <view class="list-item" v-for="(item,index) in products" :key="index">
|
|
|
<view class="goods-img">
|
|
|
<image :src="item.imgUrl" mode="widthFix"></image>
|
|
|
<view class="goods-label">{{index+1}}</view>
|
|
@@ -256,7 +216,7 @@
|
|
|
<text class="nummber"><text
|
|
|
style="font-size: 20rpx;font-weight: 600;">¥</text><text
|
|
|
style="font-size: 36rpx;font-weight: bold;">{{Math.trunc(item.price)}}</text>.{{getPureDecimal(item.price)?getPureDecimal(item.price):'00'}}/日</text>
|
|
|
- <view class="btn-group u-flex-y-center">
|
|
|
+ <view class="btn-group u-flex-y-center">
|
|
|
<view class="collect-btn">
|
|
|
<image v-if="item.isFavorite" @click="onGoodsCollect(item)"
|
|
|
style="width: 32rpx;height: 32rpx;"
|
|
@@ -285,7 +245,6 @@
|
|
|
</swiper>
|
|
|
</view>
|
|
|
</template>
|
|
|
-
|
|
|
<script>
|
|
|
import Hls from 'hls.js';
|
|
|
import CryptoJS from 'crypto-js'
|
|
@@ -322,10 +281,9 @@
|
|
|
getAnswerlist,
|
|
|
submitAnswer
|
|
|
} from '@/api/home'
|
|
|
-
|
|
|
+ // var wsUrl = "wss://live.test.ylrztop.com/ws/live-api/app/webSocket"; //余红奇
|
|
|
var wsUrl = "ws://192.168.10.166:7114/app/webSocket"; //余红奇
|
|
|
// var wsUrl = "ws://192.168.10.125:7114/app/webSocket"; //涂小龙
|
|
|
- // var wsUrl = "wss://live.test.ylrztop.com/ws/live-api/app/webSocket"; //余红奇
|
|
|
// var wsUrl = "ws://live.test.ylrztop.com/prod-api/app/webSocket"; //余红奇
|
|
|
// var wsUrl = "ws://nd383294.natappfree.cc/app/webSocket"; //余红奇
|
|
|
// var wsUrl = "ws://v56c9b8e.natappfree.cc/app/webSocket"; //余红奇
|
|
@@ -339,6 +297,11 @@
|
|
|
export default {
|
|
|
data() {
|
|
|
return {
|
|
|
+ // 新增:滑动节流相关变量
|
|
|
+ lastSwiperChangeTime: 0, // 上次切换时间戳
|
|
|
+ swiperChangeThrottle: 300, // 切换节流阈值(毫秒)
|
|
|
+
|
|
|
+
|
|
|
lastClickTime: 0,
|
|
|
clickDelay: 300, // 300ms内只响应一次点击
|
|
|
|
|
@@ -484,10 +447,15 @@
|
|
|
const currentLive = this.list[this.currentSwiperIndex];
|
|
|
if (currentLive) {
|
|
|
this.pauseVideo(currentLive);
|
|
|
+ this.clearTimeTimer(currentLive); // 隐藏时清除当前直播间定时器
|
|
|
} // 隐藏时关闭所有连接
|
|
|
this.closeAllWebSockets();
|
|
|
},
|
|
|
onUnload() {
|
|
|
+ // 清除所有直播间的时间定时器
|
|
|
+ this.list.forEach(liveItem => {
|
|
|
+ this.clearTimeTimer(liveItem);
|
|
|
+ });
|
|
|
// 关闭所有WebSocket连接
|
|
|
this.closeAllWebSockets();
|
|
|
this.socketInstances = {}; // 强制清空实例对象
|
|
@@ -523,7 +491,68 @@
|
|
|
immediate: true
|
|
|
}
|
|
|
},
|
|
|
- methods: {
|
|
|
+ methods: { // 计算当前时间与 liveItem.startTime 的差值,并更新 totalTime
|
|
|
+ calculateTimeDiff(liveItem) {
|
|
|
+ // 1. 校验 startTime 格式(防止无效时间)
|
|
|
+ if (!liveItem.startTime) {
|
|
|
+ liveItem.totalTime = '00小时00分钟00秒';
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const iosCompatibleTime = liveItem.startTime
|
|
|
+ .replace(/-/g, '/') // 把所有 "-" 替换为 "/"
|
|
|
+ .replace(' ', ' '); // 保留空格(也可替换为 "T",即 "yyyy-MM-ddTHH:mm:ss")
|
|
|
+ // 2. 转换时间格式(字符串转 Date 对象)
|
|
|
+ const startTime = new Date(liveItem.startTime);
|
|
|
+ const now = new Date();
|
|
|
+ // 4. 容错处理:防止时间解析失败(极端情况)
|
|
|
+ if (isNaN(startTime.getTime())) {
|
|
|
+ console.warn(`iOS 时间解析失败,原始时间:${liveItem.startTime},转换后:${iosCompatibleTime}`);
|
|
|
+ liveItem.totalTime = '00小时00分钟00秒';
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // 3. 计算毫秒级差值(确保差值非负,避免未来时间出现负数)
|
|
|
+ const diffMs = Math.max(0, now - startTime);
|
|
|
+
|
|
|
+ // 4. 转换为 小时:分钟:秒(补零确保两位数)
|
|
|
+ const totalSeconds = Math.floor(diffMs / 1000);
|
|
|
+ const hours = this.padZero(Math.floor(totalSeconds / 3600));
|
|
|
+ const minutes = this.padZero(Math.floor((totalSeconds % 3600) / 60));
|
|
|
+ const seconds = this.padZero(totalSeconds % 60);
|
|
|
+
|
|
|
+ // 5. 更新当前直播间的 totalTime(响应式更新)
|
|
|
+ this.$set(liveItem, 'totalTime', `${hours}:${minutes}:${seconds}`);
|
|
|
+ },
|
|
|
+
|
|
|
+ // 辅助方法:数字补零(如 9 → 09)
|
|
|
+ padZero(num) {
|
|
|
+ return num < 10 ? `0${num}` : num;
|
|
|
+ },
|
|
|
+
|
|
|
+ // 启动当前直播间的时间差值定时器
|
|
|
+ startTimeTimer(liveItem) {
|
|
|
+ // 先清除当前直播间的旧定时器(避免重复)
|
|
|
+ if (liveItem.timeTimer) {
|
|
|
+ clearInterval(liveItem.timeTimer);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 1. 立即计算一次(避免等待1秒才显示)
|
|
|
+ this.calculateTimeDiff(liveItem);
|
|
|
+
|
|
|
+ // 2. 每秒更新一次(实时刷新)
|
|
|
+ liveItem.timeTimer = setInterval(() => {
|
|
|
+ this.calculateTimeDiff(liveItem);
|
|
|
+ }, 1000);
|
|
|
+ },
|
|
|
+ // 清除指定直播间的时间定时器
|
|
|
+ clearTimeTimer(liveItem) {
|
|
|
+ if (liveItem.timeTimer) {
|
|
|
+ clearInterval(liveItem.timeTimer);
|
|
|
+ liveItem.timeTimer = null; // 清空定时器标识
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
toggleViewerList() {
|
|
|
const now = Date.now()
|
|
|
if (now - this.lastClickTime > this.clickDelay) {
|
|
@@ -616,6 +645,8 @@
|
|
|
]);
|
|
|
|
|
|
currentLive.loaded = true;
|
|
|
+ // 初始化完成后,启动当前直播间的时间差值定时器
|
|
|
+ this.startTimeTimer(currentLive);
|
|
|
} catch (error) {
|
|
|
console.error("初始化直播间数据失败:", error);
|
|
|
} finally {
|
|
@@ -676,14 +707,25 @@
|
|
|
|
|
|
// Swiper切换事件
|
|
|
async onSwiperChange(e) {
|
|
|
+ const now = Date.now();
|
|
|
const newIndex = e.detail.current;
|
|
|
const oldIndex = this.currentSwiperIndex;
|
|
|
+
|
|
|
+ // 相同索引或节流期间,不处理
|
|
|
if (newIndex === oldIndex) return;
|
|
|
+ if (now - this.lastSwiperChangeTime < this.swiperChangeThrottle) {
|
|
|
+ console.log('Swiper切换过快,已节流');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新最后切换时间
|
|
|
+ this.lastSwiperChangeTime = now;
|
|
|
|
|
|
// 1. 立即关闭旧直播间的WebSocket(同步操作)
|
|
|
const oldLive = this.list[oldIndex];
|
|
|
if (oldLive) {
|
|
|
this.pauseVideo(oldLive);
|
|
|
+ this.clearTimeTimer(oldLive); // 清除旧直播间的时间定时器
|
|
|
// 强制关闭并删除实例,确保连接被释放
|
|
|
if (this.socketInstances[oldLive.liveId]) {
|
|
|
try {
|
|
@@ -706,7 +748,7 @@
|
|
|
|
|
|
this.liveId = newLive.liveId;
|
|
|
this.resetUserListParams();
|
|
|
- await this.initCurrentLiveData();
|
|
|
+ await this.initCurrentLiveData(); // 内部会启动新直播间的定时器
|
|
|
this.getliveUser(false);
|
|
|
setTimeout(() => {
|
|
|
this.playVideo(newLive);
|
|
@@ -729,7 +771,12 @@
|
|
|
shopping: false,
|
|
|
inputInfo: '',
|
|
|
showWelcomeMessage: false,
|
|
|
- redInfo: {}
|
|
|
+ redInfo: {},
|
|
|
+ goodsCard:{},
|
|
|
+ totalTime: '00小时00分钟00秒', // 初始化差值为0
|
|
|
+ timeTimer: null, // 存储当前直播间的定时器,用于后续清理
|
|
|
+ // 预处理:统一将 startTime 转换为 iOS 兼容格式(避免重复转换)
|
|
|
+ startTime: item.startTime ? item.startTime.replace(/-/g, '/') : ''
|
|
|
// 其他初始化字段...
|
|
|
}));
|
|
|
console.log("直播间列表加载成功,数量:", this.list.length); // 确认数量是否 > 0
|
|
@@ -870,7 +917,8 @@
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- this.liveViewers = isLoadMore ? [...this.liveViewers, ...res.rows] :res.rows; // 首次加载/切换直播间时重置数据
|
|
|
+ this.liveViewers = isLoadMore ? [...this.liveViewers, ...res.rows] : res
|
|
|
+ .rows; // 首次加载/切换直播间时重置数据
|
|
|
this.viewPageNum++;
|
|
|
// console.log("在线观众", this.liveViewers)
|
|
|
}
|
|
@@ -1010,20 +1058,31 @@
|
|
|
|
|
|
// 视频错误处理
|
|
|
videoError(e, liveItem) {
|
|
|
- // 检查是否是当前直播间,避免处理非活跃直播间的错误
|
|
|
if (!liveItem || liveItem.liveId !== this.liveId) return;
|
|
|
|
|
|
+ // 初始化重试计数
|
|
|
+ if (this.videoRetryCounts[liveItem.liveId] === undefined) {
|
|
|
+ this.videoRetryCounts[liveItem.liveId] = 0;
|
|
|
+ }
|
|
|
+
|
|
|
// 限制重试次数
|
|
|
if (this.videoRetryCounts[liveItem.liveId] >= 3) {
|
|
|
- console.error(`直播间 ${liveItem.liveId} 视频加载失败`);
|
|
|
+ console.error(`直播间 ${liveItem.liveId} 视频加载失败,停止重试`);
|
|
|
+ // 显示错误提示
|
|
|
+ uni.showToast({
|
|
|
+ title: "视频加载失败,请检查网络",
|
|
|
+ icon: 'none',
|
|
|
+ duration: 2000
|
|
|
+ });
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
this.videoRetryCounts[liveItem.liveId]++;
|
|
|
|
|
|
- // 延迟重试,避免与切换操作冲突
|
|
|
+ // 延迟重试
|
|
|
setTimeout(() => {
|
|
|
if (liveItem.liveId === this.liveId) {
|
|
|
+ console.log(`第${this.videoRetryCounts[liveItem.liveId]}次重试播放视频`);
|
|
|
this.playVideo(liveItem);
|
|
|
}
|
|
|
}, 2000);
|
|
@@ -1182,54 +1241,71 @@
|
|
|
|
|
|
// 点赞
|
|
|
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;
|
|
|
-
|
|
|
+ if (res?.like) {
|
|
|
+ liveItem.liveViewData.like++; // 只更新当前直播间的点赞数
|
|
|
} else {
|
|
|
uni.showToast({
|
|
|
- title: res.msg || "点赞失败",
|
|
|
+ title: res.msg,
|
|
|
icon: 'none'
|
|
|
});
|
|
|
}
|
|
|
} catch (error) {
|
|
|
console.error('点赞失败:', error);
|
|
|
- uni.showToast({
|
|
|
- title: "网络异常,点赞失败",
|
|
|
- icon: 'none'
|
|
|
- });
|
|
|
}
|
|
|
},
|
|
|
+ // 点赞
|
|
|
+ // 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) {
|
|
|
if (!liveItem || !liveItem.liveId) return;
|
|
@@ -1412,38 +1488,34 @@
|
|
|
// 修改关闭WebSocket方法
|
|
|
closeWebSocket(liveId = null) {
|
|
|
this.isManualClose = true;
|
|
|
-
|
|
|
- // 清除所有定时器
|
|
|
clearInterval(this.pingpangTimes);
|
|
|
clearInterval(this.reconnectTimer);
|
|
|
|
|
|
- // 关闭指定或所有WebSocket连接
|
|
|
if (liveId) {
|
|
|
- // 关闭指定直播间的连接
|
|
|
+ // 添加空值检查
|
|
|
if (this.socketInstances[liveId] && this.socketInstances[liveId].instance) {
|
|
|
try {
|
|
|
- this.socketInstances[liveId].instance.close(); // socketTask.close()
|
|
|
+ this.socketInstances[liveId].instance.close();
|
|
|
console.log(`直播间 ${liveId} WebSocket 已安全关闭`);
|
|
|
} catch (e) {
|
|
|
- console.error('关闭失败', e);
|
|
|
+ console.error(`关闭直播间 ${liveId} WebSocket 时出错:`, e);
|
|
|
}
|
|
|
- delete this.socketInstances[liveId];
|
|
|
}
|
|
|
+ delete this.socketInstances[liveId];
|
|
|
} else {
|
|
|
- // 关闭所有连接
|
|
|
- Object.keys(this.socketInstances).forEach(liveId => {
|
|
|
- try {
|
|
|
- uni.closeSocket({
|
|
|
- instance: this.socketInstances[liveId]
|
|
|
- });
|
|
|
- console.log(`直播间 ${liveId} WebSocket 已安全关闭`);
|
|
|
- } catch (e) {
|
|
|
- console.error('关闭 WebSocket 时出错:', e);
|
|
|
+ Object.keys(this.socketInstances).forEach(id => {
|
|
|
+ // 添加空值检查
|
|
|
+ if (this.socketInstances[id] && this.socketInstances[id].instance) {
|
|
|
+ try {
|
|
|
+ this.socketInstances[id].instance.close();
|
|
|
+ console.log(`直播间 ${id} WebSocket 已安全关闭`);
|
|
|
+ } catch (e) {
|
|
|
+ console.error(`关闭直播间 ${id} WebSocket 时出错:`, e);
|
|
|
+ }
|
|
|
}
|
|
|
});
|
|
|
this.socketInstances = {};
|
|
|
}
|
|
|
-
|
|
|
isSocketOpen = false;
|
|
|
},
|
|
|
reConnect() {
|
|
@@ -1515,7 +1587,12 @@
|
|
|
}
|
|
|
|
|
|
this.isManualClose = false;
|
|
|
-
|
|
|
+ const connectTimeout = setTimeout(() => {
|
|
|
+ if (!this.socketInstances[liveId]?.isOpen) {
|
|
|
+ console.error(`WebSocket连接超时: ${liveId}`);
|
|
|
+ this.scheduleReconnect(liveId);
|
|
|
+ }
|
|
|
+ }, 10000); // 10秒超时
|
|
|
// 生成签名
|
|
|
const signature = CryptoJS.HmacSHA256(
|
|
|
`${liveId}${this.userinfo.userId}${this.userType}${this.timestamp}`,
|
|
@@ -1545,7 +1622,7 @@
|
|
|
console.log('WebSocket 连接已成功建立', res);
|
|
|
this.socketInstances[liveId].instance = socketTask;
|
|
|
this.socketInstances[liveId].isOpen = true;
|
|
|
-
|
|
|
+ clearTimeout(connectTimeout);
|
|
|
isSocketOpen = true;
|
|
|
this.connectingLiveId = null;
|
|
|
this.reconnectCount = 0;
|
|
@@ -1562,6 +1639,7 @@
|
|
|
});
|
|
|
|
|
|
socketTask.onError((err) => {
|
|
|
+ clearTimeout(connectTimeout);
|
|
|
console.error('WebSocket 连接错误:', err);
|
|
|
this.connectingLiveId = null;
|
|
|
this.scheduleReconnect(liveId);
|
|
@@ -1620,12 +1698,13 @@
|
|
|
// console.log("关闭欢迎消息:", liveItem.showWelcomeMessage);
|
|
|
}, 1000); // 1秒后隐藏
|
|
|
} else if (socketMessage.cmd == 'Integral') {
|
|
|
+ // 观看奖励事件 弹出框2s 显示msg
|
|
|
uni.showToast({
|
|
|
title: socketMessage.msg,
|
|
|
icon: 'none',
|
|
|
- duration: 20000
|
|
|
+ duration: 2000
|
|
|
});
|
|
|
- // 观看奖励事件 弹出框2s 显示msg
|
|
|
+
|
|
|
} else if (socketMessage.cmd == 'blockUser') {
|
|
|
//拉黑事件 主动删除用户token,强制跳转到登录页面
|
|
|
uni.removeStorage({
|
|
@@ -1640,10 +1719,12 @@
|
|
|
console.error('Token 删除失败:', err);
|
|
|
}
|
|
|
});
|
|
|
+ } else if (socketMessage.cmd == 'goods') {
|
|
|
+ this.$set(liveItem, 'goodsCard', JSON.parse(socketMessage.data));
|
|
|
}
|
|
|
- // else if (socketMessage.data.cmd == 'deleteId') {
|
|
|
+ // else if (socketMessage.cmd == 'deleteId') {
|
|
|
|
|
|
- // } else if (socketMessage.data.cmd =='sendRedPacketQuestion') {
|
|
|
+ // } else if (socketMessage.cmd =='sendRedPacketQuestion') {
|
|
|
|
|
|
// }
|
|
|
} else {
|
|
@@ -1654,15 +1735,17 @@
|
|
|
}
|
|
|
|
|
|
},
|
|
|
- scheduleReconnect() {
|
|
|
+ scheduleReconnect(liveId) {
|
|
|
if (this.isManualClose || this.reconnectCount >= this.maxReconnectAttempts) return;
|
|
|
this.reconnectCount++;
|
|
|
const delay = Math.min(3000 * this.reconnectCount, 30000);
|
|
|
clearInterval(this.reconnectTimer);
|
|
|
this.reconnectTimer = setTimeout(() => {
|
|
|
- this.initSocket(this.list.find(item => item.liveId === liveId) || {
|
|
|
+ // 使用传入的liveId
|
|
|
+ const liveItem = this.list.find(item => item.liveId === liveId) || {
|
|
|
liveId
|
|
|
- });
|
|
|
+ };
|
|
|
+ this.initSocket(liveItem);
|
|
|
}, delay);
|
|
|
},
|
|
|
sendMsg(liveItem) {
|
|
@@ -1979,6 +2062,15 @@
|
|
|
width: 100%;
|
|
|
background-color: rgba(57, 57, 57, 0.4);
|
|
|
|
|
|
+ .time {
|
|
|
+ position: relative;
|
|
|
+ z-index: 999;
|
|
|
+ margin-top: -40rpx;
|
|
|
+ color: #ffffff;
|
|
|
+ font-size: 20rpx;
|
|
|
+ margin-left: 10rpx;
|
|
|
+ }
|
|
|
+
|
|
|
.videotop {
|
|
|
width: 100%;
|
|
|
height: 100%;
|
|
@@ -1996,8 +2088,11 @@
|
|
|
|
|
|
.goods {
|
|
|
position: fixed;
|
|
|
- bottom: 160rpx;
|
|
|
- right: 10rpx;
|
|
|
+ // bottom: 160rpx;
|
|
|
+ // right: 10rpx;
|
|
|
+ top: 50%;
|
|
|
+ left: 50%;
|
|
|
+ transform: translate(-50%,-50%);
|
|
|
z-index: 5;
|
|
|
background-color: #fff;
|
|
|
border-radius: 20rpx;
|