|
|
@@ -0,0 +1,2141 @@
|
|
|
+<template>
|
|
|
+ <view class="container">
|
|
|
+ <!-- <view class="popup-video " v-if="!autoplay">
|
|
|
+ <view class="fs36 bold" v-if="livedata.status==1">——直播还未开始——</view>
|
|
|
+ <view class="fs36 bold" v-if="livedata.status==3">——直播已经结束——</view>
|
|
|
+ <view class="fs28 mtb20">了解更多,点击下方联系老师</view>
|
|
|
+ <view class="more" @click="showadd=!showadd">联系老师</view>
|
|
|
+ </view> -->
|
|
|
+
|
|
|
+ <!-- <image :src="livedata.liveImgUrl"
|
|
|
+ :class="livedata.status!=2?'background-images':'background-image'"></image> -->
|
|
|
+ <view class="blackbg"></view>
|
|
|
+ <view class="content">
|
|
|
+ <!-- 页面内容 -->
|
|
|
+ <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"
|
|
|
+ style="padding: 6rpx 4rpx;height: 64rpx;background: rgba(0,0,0,0.5);border-radius: 32rpx;">
|
|
|
+ <u-avatar :src="livedata.liveImgUrl||$img.logo" size="32"></u-avatar>
|
|
|
+ <view class="colorf" style="margin-left: 8rpx;">
|
|
|
+ <view>{{livedata.liveName?truncateString(livedata.liveName,5):"未命名"}}</view>
|
|
|
+ <view style="font-size: 16rpx;">
|
|
|
+ {{ liveViewData.follow || liveViewData.follow === 0 ? liveViewData.follow : 0 }}位粉丝
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <view v-if="isFollow" @click="onFollow" class="follow-btn ml20">关注</view>
|
|
|
+ <view v-else @click="onFollow" class="follow-btn ml20">已关注</view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <view class="u-flex-end align-center" @click="showadd=!showadd">
|
|
|
+ <image class="w52 h52 mr4" v-for="(item,index) in filteredViewers" :key="index"
|
|
|
+ style="border-radius: 26rpx;" :src="item.avatar||$img.logo"></image>
|
|
|
+ <view class="sum">{{liveUserTotal||0}}</view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 页面内容 -->
|
|
|
+ <view style=" position: fixed;top:100rpx;left: 0rpx; z-index: 5;" class="content-top">
|
|
|
+ <view class="u-flex-y-center">
|
|
|
+ <image @click="onRed" v-if="redInfo?.redStatus==1" style="width: 70rpx;height: 70rpx;"
|
|
|
+ src="@/static/images/hongbao.png"></image>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 右边的 -->
|
|
|
+ <view :class=" showType==1 ? 'siderow-group' : 'side-group'">
|
|
|
+ <view class="side-item">
|
|
|
+ <image @click="onLike" src="/static/images/live/like.png"></image>
|
|
|
+ <view>{{liveViewData.like||0}}</view>
|
|
|
+ </view>
|
|
|
+ <view class="side-item">
|
|
|
+ <image @click="goStore()" src="/static/images/live/shop.png"></image>
|
|
|
+ <view>店铺</view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="side-item">
|
|
|
+ <button open-type="share">
|
|
|
+ <image src="/static/images/live/share.png" mode="widthFix"></image>
|
|
|
+ </button>
|
|
|
+ <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) : ''}} 等 {{orderUser.count || 0}}
|
|
|
+ 人正在去购买</text>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- object-fit:fill; -->
|
|
|
+ <view class="videolist " style="margin: auto 0">
|
|
|
+ <!-- <view class="video" :style="{height:isScreen?'100vh':''}"> -->
|
|
|
+ <view class="video" style="height:100vh">
|
|
|
+ <!-- 修改video组件以支持HLS -->
|
|
|
+ <!-- showType 1 横屏 2 竖屏 -->
|
|
|
+ <video v-if="livingUrl" id="myVideo" :class="showType == 1 ? 'video_row' : 'videotop'"
|
|
|
+ :src="livingUrl" :autoplay="autoplay" :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>
|
|
|
+
|
|
|
+ <video v-if="videoUrl" :class="showType == 1 ? 'video_row' : 'videotop'" :src="videoUrl"
|
|
|
+ :autoplay="autoplay" :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" loop @error="videoError">
|
|
|
+ </video>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <view class="pb40 mt90" style="position: fixed;bottom: 0;">
|
|
|
+ <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);"
|
|
|
+ :scroll-into-view="scrollIntoView">
|
|
|
+ <view>
|
|
|
+ <view class="list justify-start" v-for="(item,index) in talklist" :key="index"
|
|
|
+ v-show="item.cmd=='announcement'">
|
|
|
+ <view class="talk-list ml16 justify-start">
|
|
|
+ <view class="fs24">
|
|
|
+ <text class='fs24 colorf'> {{item.msg}}直播间{{messageContent}}</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+
|
|
|
+ <view class="list justify-start" v-for="(item,index) in talklist" :key="index"
|
|
|
+ :id="`list_${index}`" v-show="item.cmd!='red'&&item.cmd!='out'&&item.cmd!='entry'">
|
|
|
+ <view class="talk-list ml16 justify-start">
|
|
|
+ <view class="fs24">
|
|
|
+ <!-- <text style="color: #3fc69b;transform: scale(0.8);display: inline-block; "v-if="item.userId==userinfo.userId&&item.cmd=='sendMsg'">我</text> -->
|
|
|
+ <!-- <text style="color: #c84e1e;transform: scale(0.8);display: inline-block;" v-if="item.userType==1">[ 管理员 ]</text> -->
|
|
|
+ <text style="color: #FFDA73;">{{item.nickName}}:</text>
|
|
|
+ <text class='fs24 colorf'>{{item.msg}}</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <view v-if="showWelcomeMessage" class="welcome-message">
|
|
|
+ <view class="list justify-start" v-for="(item,index) in talklist" :key="index"
|
|
|
+ v-show="item.cmd=='entry'||item.cmd=='out'">
|
|
|
+ <view class="talk-list ml16 justify-start">
|
|
|
+ <view class="fs24">
|
|
|
+ <text style="color: #ff89d6;">{{item.nickName}} </text>
|
|
|
+ <text class='fs24 colorf'> {{item.msg}}直播间{{messageContent}}</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </scroll-view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="justify-between p24">
|
|
|
+ <view class="u-flex-y-center"
|
|
|
+ style="background:rgba(157, 157, 157, 0.8);padding:18rpx 14rpx 18rpx 32rpx;width: 526rpx;box-sizing:border-box;border-radius:36rpx;">
|
|
|
+ <u-input :placeholder="placeholderText" border="none" customStyle='font-size:24rpx;'
|
|
|
+ v-model="value" shape='circle' color='#fff' placeholderStyle='color:#e7e7e7' class="ml20"
|
|
|
+ @keydown.enter="sendMsg" @confirm="sendMsg">
|
|
|
+ </u-input>
|
|
|
+ <!-- <image @click="sendMsg" class="w44 h44" src="@/static/images/live/like.png"></image> -->
|
|
|
+ </view>
|
|
|
+ <!-- :disabled='talkdisabled' ></u-input> -->
|
|
|
+ <!-- <view @click="sendMsg" class="colorf center ml10 fs24">发送</view> -->
|
|
|
+ <view class="justify-between mr30 align-center">
|
|
|
+ <!-- <view class="icon-bg ml20" @click="answerbtn=!answerbtn">
|
|
|
+ <image src="/static/images/redbag.png" class="w40 h40"></image>
|
|
|
+ </view> -->
|
|
|
+ <view class="icon-bg ml20" @click="shopping=!shopping">
|
|
|
+ <image src="/static/images/shopping.png" class="w48 h48"></image>
|
|
|
+ </view>
|
|
|
+ <view class="icon-bg ml20" @click="showziliao=!showziliao">
|
|
|
+ <image src="/static/images/more-icon.png" class="w48 h48"></image>
|
|
|
+ </view>
|
|
|
+ <!-- <view class="icon-bg ml20" :class="{ 'zoom-button-active': isZoom }"
|
|
|
+ @touchstart="handleTouchStart" @touchend="handleTouchEnd">
|
|
|
+ <image src="/static/images/dianzan.png" class="w40 h40"></image>
|
|
|
+ </view> -->
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- <u-popup :show="showadd" @close="close" @open="open" round='20rpx' bgColor='#fffee1'>
|
|
|
+ <view class="addchat p20">
|
|
|
+ <view class="u-flex-row-reverse u-flex">
|
|
|
+ <u-icon name="close" size="18" @click="showadd=!showadd"></u-icon>
|
|
|
+ </view>
|
|
|
+ <view class="column align-center">
|
|
|
+ <view class="fs36" style="color: #ff5c03;">扫码添加助教老师</view>
|
|
|
+ <view class="fs28 color6">扫码添加助教老师</view>
|
|
|
+ <view class="p10 mt40" style="border: #ff5c03 solid 2rpx;">
|
|
|
+ <image :src="codeimg" class="wh180" @touchstart="longPress" @touchend="cancelLongPress">
|
|
|
+ </image>
|
|
|
+ </view>
|
|
|
+ <view class="color6 mt20">长按识别二维码</view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </u-popup> -->
|
|
|
+ <u-popup :show="showadd" @close="close" @open="openViews" round='20rpx' bgColor='#ffffff'>
|
|
|
+ <view class="view-box">
|
|
|
+ <view class="t-c fs30">在线观众</view>
|
|
|
+ <scroll-view scroll-y class="scroll-content" :style="{height: scrollHeight + 'px'}"
|
|
|
+ @scrolltolower="handleScrollToLower">
|
|
|
+ <view class="fs24 u-flex-y-center mb20" v-for="(item,index) in liveViewers" :key="index">
|
|
|
+ <view
|
|
|
+ :style="{color: index === 0 ? '#FF3B30' : index === 1 ? '#FF9500' : index === 2 ? '#FFCC00' : '#8E8E93',fontWeight: index < 3 ? 'bold' : 'normal',width: '50rpx'}"
|
|
|
+ class="mr10">{{index+1}}</view>
|
|
|
+ <u-avatar :src="item.avatar||$img.logo" size="32"></u-avatar>
|
|
|
+ <text class="ml16">{{item.nickName||"未命名"}}</text>
|
|
|
+ </view>
|
|
|
+ <!-- <view class="loading-more" v-if="viewLoading">
|
|
|
+ <u-loading mode="circle"></u-loading>
|
|
|
+ <text>加载中...</text>
|
|
|
+ </view> -->
|
|
|
+ <view class="no-more" v-if="viewNoMoreData && liveViewers.length > 0">
|
|
|
+ <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="showziliao" @close="closes" round='20' bgColor='#fffee1'>
|
|
|
+ <view class="addchat p20 bgf" style="border-radius: 20rpx;">
|
|
|
+ <view class="u-flex-row-reverse u-flex">
|
|
|
+ <u-icon name="close" size="18" @click="showziliao=!showziliao"></u-icon>
|
|
|
+ </view>
|
|
|
+ <view class="column align-center h400">
|
|
|
+ <view class="fs36 " style="color: #ff5c03;">资料</view>
|
|
|
+ <view v-html="livedata.liveDesc"></view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </u-popup> -->
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ <!-- 抽奖 -->
|
|
|
+ <!-- <u-popup :show="answerbtn" @close="closeanswer" round='40rpx' bgColor='#fff'>
|
|
|
+ <view class=" p20 bgf" style="border-radius: 40rpx;height: fit-content;">
|
|
|
+ <view class="u-flex-row-reverse u-flex">
|
|
|
+ <u-icon name="close" size="18" @click="answerbtn=!answerbtn"></u-icon>
|
|
|
+ </view>
|
|
|
+ <view class="column align-center h400">
|
|
|
+ <view class="fs36 " style="color: #000000;font-weight: bold;">答题获取红包/积分奖励</view>
|
|
|
+ <view class="answerpop" @click="noredanswer">
|
|
|
+ <view class="color6 w350 fs24">
|
|
|
+ 边玩边学,解锁你的知识巅峰!
|
|
|
+ </view>
|
|
|
+ <view class="answera">答题挑战</view>
|
|
|
+ </view>
|
|
|
+ <view class="answerpop">
|
|
|
+
|
|
|
+ <view class="color6 fs24" v-if='redanswertips&&redanswertips.length>0'>
|
|
|
+ 🔥 <text style="color: #ff5c03;" class="fs30 bold">{{redanswertips[1].probability}}</text>
|
|
|
+ %用户首抽得
|
|
|
+ <text style="color: #ff5c03;" class="fs40 bold">{{redanswertips[1].maxAmount}}</text>元
|
|
|
+ </view>
|
|
|
+ <view v-else class="color6 w350 fs24">
|
|
|
+ 随机奖励
|
|
|
+ </view>
|
|
|
+ <view class="answera" @click="redbagAnswer">
|
|
|
+ <image src="/static/images/baganswer.png"></image>
|
|
|
+ <view> 获得红包</view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </u-popup> -->
|
|
|
+
|
|
|
+
|
|
|
+ <!-- <u-popup :show="showAnswer" @close="closest" round='40' bgColor='#fffee1' mode="center">
|
|
|
+ <view class="answerbox p20 bgf">
|
|
|
+ <view class="u-flex-row-reverse u-flex">
|
|
|
+ <u-icon name="close" size="18" @click="closest"></u-icon>
|
|
|
+ </view>
|
|
|
+ <view class="column align-center ">
|
|
|
+ <view v-if="answerlist.type==1" style="width: 100%;">
|
|
|
+ <view class="mb40" style="text-align: center;">
|
|
|
+ <text class="color9 fs24">(单选)</text>{{answerlist.title}}
|
|
|
+ </view>
|
|
|
+ <view v-for="(item,index) in answerlist.content" :key="index"
|
|
|
+ :class=" checkboxValue[0]== item.label?'answeract itemanswer':'itemanswer' "
|
|
|
+ @click="handleCheckboxSelect(item.label)">{{item.label}}. {{item.content}}</view>
|
|
|
+ </view>
|
|
|
+ <view v-if="answerlist.type==2" style="width: 100%;">
|
|
|
+ <view class="mb40 " style="text-align: center;">
|
|
|
+ <text class="color9 fs24">(多选)</text>{{answerlist.title}}
|
|
|
+ </view>
|
|
|
+ <view v-for="(item,index) in answerlist.content" :key="index"
|
|
|
+ :class=" checkboxValue.includes(item.label)?'answeract itemanswer':'itemanswer' "
|
|
|
+ @click="handleCheckboxSelect(item.label)">{{item.label}}. {{item.content}}</view>
|
|
|
+ <view class=" submitbtn" @click="submitAnswers">确认</view>
|
|
|
+ </view>
|
|
|
+ <view class="fs24 mtb20 " style="color: #717171;">
|
|
|
+ {{answerfrist+1}}/{{Answerlistall}}
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </u-popup> -->
|
|
|
+
|
|
|
+
|
|
|
+ <!-- <u-popup :show="showAnswerred" @close="closestred" round='40' bgColor='#fffee1' mode="center">
|
|
|
+ <view class="answerbox p20 bgf">
|
|
|
+ <view class="u-flex-row-reverse u-flex">
|
|
|
+ <u-icon name="close" size="18" @click="closestred"></u-icon>
|
|
|
+ </view>
|
|
|
+ <view class="column align-center ">
|
|
|
+ <view v-if="answerlist.type==1" style="width: 100%;">
|
|
|
+ <view class="mb40" style="text-align: center;">
|
|
|
+ <text class="color9 fs24">(单选)</text>{{answerlist.title}}
|
|
|
+ </view>
|
|
|
+ <view v-for="(item,index) in answerlist.content" :key="index"
|
|
|
+ :class=" checkboxValue[0]== item.label?'answeract itemanswer':'itemanswer' "
|
|
|
+ @click="handleCheckboxSelect(item.label)">{{item.label}}. {{item.content}}</view>
|
|
|
+ </view>
|
|
|
+ <view v-if="answerlist.type==2" style="width: 100%;">
|
|
|
+ <view class="mb40 " style="text-align: center;">
|
|
|
+ <text class="color9 fs24">(多选)</text>{{answerlist.title}}
|
|
|
+ </view>
|
|
|
+ <view v-for="(item,index) in answerlist.content" :key="index"
|
|
|
+ :class=" checkboxValue.includes(item.label)?'answeract itemanswer':'itemanswer' "
|
|
|
+ @click="handleCheckboxSelect(item.label)">{{item.label}}. {{item.content}}</view>
|
|
|
+ <view class=" submitbtn" @click="submitAnswers">确认</view>
|
|
|
+ </view>
|
|
|
+ <view class="fs24 mtb20 " style="color: #717171;">
|
|
|
+ {{answerfrist+1}}/{{Answerlistall}}
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </u-popup> -->
|
|
|
+
|
|
|
+ <!-- 小黄车弹窗 -->
|
|
|
+ <u-popup :show="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;height: 24rpx;margin-right: 16rpx;" src="@/static/images/search.png">
|
|
|
+ </image>
|
|
|
+ <input placeholder="请搜索商品" v-model="inputInfo" @input="handleSearchInput" />
|
|
|
+ </view>
|
|
|
+ <view class="t-c search-top" style="margin-right: 48rpx;">
|
|
|
+ <image @click="onStoreCollect" v-if="store.isFavorite" style="width: 32rpx;height: 32rpx;"
|
|
|
+ src="@/static/images/collect_select.png"></image>
|
|
|
+ <image v-else @click="onStoreCollect" style="width: 32rpx;height: 32rpx;"
|
|
|
+ src="@/static/images/collect.png"></image>
|
|
|
+ <view>收藏</view>
|
|
|
+ </view>
|
|
|
+ <view class="t-c search-top" @click="goOrderList">
|
|
|
+ <image style="width: 32rpx;height: 32rpx;" 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="goods-img">
|
|
|
+ <image :src="item.imgUrl" mode="widthFix"></image>
|
|
|
+ <view class="goods-label">{{index+1}}</view>
|
|
|
+ </view>
|
|
|
+ <view class="goods-right">
|
|
|
+ <view class="goods-title">{{item.productName}}</view>
|
|
|
+ <!-- <view class="goods-details">{{item.productIntroduce}}</view> -->
|
|
|
+ <view class="goods-people">{{item.sales}} 人已购</view>
|
|
|
+ <view class="goods-shop">
|
|
|
+ <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="collect-btn">
|
|
|
+ <image v-if="item.isFavorite" @click="onGoodsCollect(item)"
|
|
|
+ style="width: 32rpx;height: 32rpx;" src="/static/images/collect_select.png">
|
|
|
+ </image>
|
|
|
+ <image v-else @click="onGoodsCollect(item)" style="width: 32rpx;height: 32rpx;"
|
|
|
+ src="/static/images/collect.png">
|
|
|
+ </image>
|
|
|
+ </view>
|
|
|
+ <view v-if="item.status==1" class="shop-btn"
|
|
|
+ @click="goShop(item.productId,item.goodsId)">去购买
|
|
|
+ </view>
|
|
|
+ <view v-else-if="item.status==0" @click="goShop(item.productId)" class="shop-btn">
|
|
|
+ 已下架</view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </scroll-view>
|
|
|
+ </view>
|
|
|
+ </u-popup>
|
|
|
+ <!-- 红包 -->
|
|
|
+ <!-- <u-popup :show="showRed" mode="center" @close="closeRed" round='20rpx' @open="openRed" bgColor='#ff3833'>
|
|
|
+ <view class="red-box" style="width: 500rpx;height: 500rpx; ">
|
|
|
+ <view class="button">立即领取</view>
|
|
|
+ </view>
|
|
|
+ </u-popup> -->
|
|
|
+ </view>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+ import Hls from 'hls.js';
|
|
|
+ import CryptoJS from 'crypto-js'
|
|
|
+ import {
|
|
|
+ liveList
|
|
|
+ } from '@/api/list'
|
|
|
+ import {
|
|
|
+ liveRed, // 点击领红包
|
|
|
+ liveDataLike, // 点赞
|
|
|
+ collectStore, // 店铺收藏/取消收藏
|
|
|
+ collectGoods, // 商品收藏/取消收藏
|
|
|
+ store, // 查询店铺
|
|
|
+ follow, // 关注/取消关注
|
|
|
+ watchUserList, //获取直播间用户(展示在线用户)
|
|
|
+ liveMsg, //获取最近聊天记录
|
|
|
+ // 小黄车
|
|
|
+ liveStore, //店铺展示,
|
|
|
+ // liveGoodsDetail, //商品详情,
|
|
|
+ liveOrderUser, //正在购买
|
|
|
+ getLiveInfo, //获取直播间信息接口
|
|
|
+ getLiveViewData //直播间点赞、关注、在线人数数据
|
|
|
+ } from '@/api/live'
|
|
|
+ import {
|
|
|
+ liveOrderList, // 订单列表
|
|
|
+ } from '@/api/order'
|
|
|
+
|
|
|
+ import parse from '../../uni_modules/uview-plus/libs/config/props/parse';
|
|
|
+ import {
|
|
|
+ LiveWS
|
|
|
+ } from '@/utils/liveWS.js'
|
|
|
+ import {
|
|
|
+ getlive,
|
|
|
+ // gettextlist,
|
|
|
+ getAnswerlist,
|
|
|
+ submitAnswer
|
|
|
+ } from '@/api/home'
|
|
|
+ // var wsUrl = "ws://192.168.10.166:7114/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://nd383294.natappfree.cc/app/webSocket"; //余红奇
|
|
|
+ // var wsUrl = "ws://v56c9b8e.natappfree.cc/app/webSocket"; //余红奇
|
|
|
+ // var wsUrl = "ws://192.168.10.170:7114/app/webSocket"; //陈果
|
|
|
+ // var wsUrl = "ws://live.ylrzcloud.com/socket/app/webSocket";
|
|
|
+ var pingpangTimes = null;
|
|
|
+ var initTimes = null;
|
|
|
+ var isSocketOpen = false;
|
|
|
+ var socket = null;
|
|
|
+
|
|
|
+ export default {
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ liveUserTotal: null,
|
|
|
+ viewPageSize: 10, // 每页数量
|
|
|
+ viewPageNum: 1, // 当前页码
|
|
|
+ viewLoading: false, // 是否正在加载
|
|
|
+ viewNoMoreData: false, // 是否没有更多数据
|
|
|
+ scrollHeight: 0,
|
|
|
+ scrollTimer: null, // 滚动防抖定时器
|
|
|
+
|
|
|
+
|
|
|
+ socketInstance: null, // 统一管理 socket 实例
|
|
|
+ reconnectCount: 0,
|
|
|
+ maxReconnectAttempts: 3,
|
|
|
+ isManualClose: false, // 标记是否手动关闭
|
|
|
+
|
|
|
+ showRed: true,
|
|
|
+ redInfo: {},
|
|
|
+ inputInfo: '',
|
|
|
+ searchTimer: null,
|
|
|
+ storeId: null,
|
|
|
+ reconnectCount: 0,
|
|
|
+ maxReconnectAttempts: 3,
|
|
|
+ reconnectTimer: null,
|
|
|
+ isCollect: false,
|
|
|
+ showPurchasePrompt: false,
|
|
|
+ purchasePromptTimer: null,
|
|
|
+ prevOrderCount: 0, // 用于记录上一次的购买人数
|
|
|
+ videoUrl: null,
|
|
|
+ showType: 1, //横屏1 竖屏2
|
|
|
+ boxHeight: 300, //小黄车高度
|
|
|
+ isFollow: true,
|
|
|
+ liveViewData: {},
|
|
|
+ liveViewers: [], //观众
|
|
|
+ likeName: 0,
|
|
|
+ hlsPlayer: null, // HLS播放器实例,
|
|
|
+ livingUrl: "",
|
|
|
+ products: {},
|
|
|
+ store: {},
|
|
|
+ orderUser: {}, //正在购买
|
|
|
+ userType: 0,
|
|
|
+ timestamp: '',
|
|
|
+ liveId: null,
|
|
|
+ // userId: uni.getStorageSync("userInfo.userId"),
|
|
|
+ codeimg: '',
|
|
|
+ placeholderText: '说点什么...',
|
|
|
+ isZoom: false, //点赞按钮控制是否放大
|
|
|
+ userinfo: '', //用户信息
|
|
|
+ // path: 'http://192.168.10.166/dev-api', //余红奇
|
|
|
+ // path: 'http://v56c9b8e.natappfree.cc', //余红奇
|
|
|
+ // path: 'live.test.ylrztop.com/prod-api', //余红奇
|
|
|
+ // path: 'http://192.168.10.170/dev-api', //陈果
|
|
|
+ value: '',
|
|
|
+ talkdisabled: false, //输入框是否禁用
|
|
|
+ autoplay: false, //视频自动播放
|
|
|
+ showadd: false,
|
|
|
+ talklist: [],
|
|
|
+ scrollIntoView: '',
|
|
|
+ bufferRate: 0, //视频缓冲时间
|
|
|
+ playDuration: 0, //视频播放时间
|
|
|
+ videoContext: '',
|
|
|
+ thistime: uni.$u.timeFormat(new Date(), 'yyyy-mm-dd hh:MM:ss'),
|
|
|
+ upDown: true, //是否视频显示隐藏
|
|
|
+ isLongPress: false, // 是否长按
|
|
|
+ timeout: null, // 计时器
|
|
|
+ showWelcomeMessage: false,
|
|
|
+ isSubmit: false,
|
|
|
+ messageContent: "",
|
|
|
+ showziliao: false,
|
|
|
+ isScreen: true,
|
|
|
+ showAnswer: false, //展示答题
|
|
|
+ Answerlistall: {}, //所有题目
|
|
|
+ answerlist: {}, //当前题目
|
|
|
+ answerfrist: 0, //当前选择
|
|
|
+ checkboxValue: [], //多选数据
|
|
|
+ checkboxFormValue: "", //多选数据
|
|
|
+ allAnswerLists: [], // 新增:存储所有题目列表
|
|
|
+ showAnswerred: false, //展示红包答题
|
|
|
+ answerbtn: false, //答题按钮弹窗
|
|
|
+ redallAnswerLists: [], //储存所有红包答题列表
|
|
|
+ redanswerAll: [], //红包当前题目
|
|
|
+ redanswerList: [], //红包答题列表
|
|
|
+ redanswertips: [], //红包答题提示
|
|
|
+ shopping: false, //小黄车弹窗
|
|
|
+ scrollTop: 0, //弹幕
|
|
|
+ old: {
|
|
|
+ scrollTop: 0
|
|
|
+ },
|
|
|
+ livedata: {}, //直播间点赞、关注、在线人数数据
|
|
|
+
|
|
|
+ };
|
|
|
+ },
|
|
|
+
|
|
|
+
|
|
|
+ onLoad(options) {
|
|
|
+ this.userinfo = JSON.parse(uni.getStorageSync("userInfo"))
|
|
|
+ if (options.liveId) {
|
|
|
+ this.liveId = options.liveId; // 仅当 liveId 变化时更新
|
|
|
+ }
|
|
|
+ uni.showShareMenu({
|
|
|
+ withShareTicket: true
|
|
|
+ });
|
|
|
+ },
|
|
|
+ onShareAppMessage() {
|
|
|
+ return {
|
|
|
+ // title: this.product.title,
|
|
|
+ title: this.livedata.liveName,
|
|
|
+ path: '/pages/home/index',
|
|
|
+ imageUrl: this.products.image,
|
|
|
+ success(res) {
|
|
|
+ console.log("分享成功", res);
|
|
|
+ },
|
|
|
+ fail(err) {
|
|
|
+ console.error("分享失败", err);
|
|
|
+ }
|
|
|
+ };
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ filteredViewers() {
|
|
|
+ if (!this.liveViewers) {
|
|
|
+ return
|
|
|
+ } else {
|
|
|
+ return this.liveViewers.slice(0, 3);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ },
|
|
|
+ mounted() {
|
|
|
+ this.getLiveMsg() // 获取最近聊天记录
|
|
|
+ // this.onLike()
|
|
|
+ this.getliveViewData() ////直播间点赞、关注、在线人数数据
|
|
|
+
|
|
|
+ // this.intervalId = setInterval(() => {
|
|
|
+ // this.getliveViewData();
|
|
|
+ // }, 60 * 1000);
|
|
|
+ // this.getLiveinformation() // 获取直播间信息接口
|
|
|
+ this.getliveOrder() //正在购买
|
|
|
+ // this.getliveStore() // 获取小黄车 店铺展示
|
|
|
+ // this.getliveGoods() // 获取小黄车 商品详情
|
|
|
+ this.getliveUser() // 获取直播间用户
|
|
|
+ this.initTime()
|
|
|
+ // this.initWebSocket()
|
|
|
+ this.initSocket()
|
|
|
+ var that = this;
|
|
|
+ uni.$on('initSocket', () => {
|
|
|
+ that.initSocket()
|
|
|
+ })
|
|
|
+ uni.$on('sendMsg', (item) => {
|
|
|
+ that.sendMsg(item)
|
|
|
+ })
|
|
|
+ uni.$on('closeWebSocket', () => {
|
|
|
+ that.closeWebSocket()
|
|
|
+ })
|
|
|
+ this.getEWechatSdk();
|
|
|
+
|
|
|
+ this.getliving() //hls
|
|
|
+ // this.gettalklist()
|
|
|
+ // this.getAnswerlists()
|
|
|
+
|
|
|
+ },
|
|
|
+ onReady: function(res) {
|
|
|
+ this.videoContext = uni.createVideoContext('myVideo')
|
|
|
+ // console.log(this.videoContext)
|
|
|
+ },
|
|
|
+ onHide() {
|
|
|
+ clearInterval(this.intervalId); // 页面隐藏时清理
|
|
|
+ },
|
|
|
+ onUnload() {
|
|
|
+ this.closeWebSocket();
|
|
|
+ uni.$off('refreshOrder');
|
|
|
+ clearInterval(this.intervalId);
|
|
|
+
|
|
|
+ if (this.hlsPlayer) {
|
|
|
+ this.hlsPlayer.destroy(); // 清理 HLS 播放器
|
|
|
+ }
|
|
|
+ if (this.pingpangTimes) {
|
|
|
+ clearInterval(this.pingpangTimes); // 清除心跳定时器
|
|
|
+ }
|
|
|
+
|
|
|
+ },
|
|
|
+ watch: {
|
|
|
+ // 监听orderUser.count的变化
|
|
|
+ 'orderUser.count': {
|
|
|
+ handler(newVal, oldVal) {
|
|
|
+ if (newVal !== this.prevOrderCount) {
|
|
|
+ this.prevOrderCount = newVal;
|
|
|
+ this.showPurchaseMessage();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ immediate: true
|
|
|
+ }
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ // 获取直播间列表
|
|
|
+ getList() {
|
|
|
+ const data = {
|
|
|
+ page: 1,
|
|
|
+ page_size: 10,
|
|
|
+ };
|
|
|
+ uni.showLoading({
|
|
|
+ title: "处理中..."
|
|
|
+ });
|
|
|
+ liveList(data)
|
|
|
+ .then(res => {
|
|
|
+ if (res.code == 200) {
|
|
|
+ this.list = res.rows; // 直接赋值给 this.list
|
|
|
+ console.log("list>>", this.list); // ✅ 打印已定义的 this.list
|
|
|
+ } else {
|
|
|
+ uni.showToast({
|
|
|
+ title: res.msg,
|
|
|
+ icon: 'none'
|
|
|
+ });
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .catch(rej => {
|
|
|
+ console.log("请求失败:", JSON.stringify(rej));
|
|
|
+ })
|
|
|
+ .finally(() => {
|
|
|
+ uni.hideLoading();
|
|
|
+ });
|
|
|
+ },
|
|
|
+ openViews() {
|
|
|
+ // 计算scroll-view高度
|
|
|
+ this.$nextTick(() => {
|
|
|
+ const query = uni.createSelectorQuery().in(this);
|
|
|
+ query.select('.view-box').boundingClientRect(data => {
|
|
|
+ if (data) {
|
|
|
+ // 减去标题和底部固定区域的高度
|
|
|
+ this.scrollHeight = data.height - 80 - 120; // 80是标题高度,120是底部高度
|
|
|
+ }
|
|
|
+ }).exec();
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+
|
|
|
+ // 获取直播间用户
|
|
|
+ async getliveUser(isLoadMore = false) {
|
|
|
+ if (this.viewLoading || this.viewNoMoreData) return;
|
|
|
+
|
|
|
+ this.viewLoading = true;
|
|
|
+
|
|
|
+ try {
|
|
|
+ const res = await watchUserList(this.liveId, this.viewPageSize, this.viewPageNum, false);
|
|
|
+
|
|
|
+ if (res.code == 200) {
|
|
|
+ this.liveUserTotal = res.total
|
|
|
+ if (res.rows.length === 0) {
|
|
|
+ this.viewNoMoreData = true;
|
|
|
+ if (this.viewPageNum > 1) {
|
|
|
+ uni.showToast({
|
|
|
+ title: '没有更多数据了',
|
|
|
+ icon: 'none'
|
|
|
+ });
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isLoadMore) {
|
|
|
+ // 加载更多时追加数据
|
|
|
+ this.liveViewers = [...this.liveViewers, ...res.rows];
|
|
|
+ } else {
|
|
|
+ // 首次加载或刷新
|
|
|
+ this.liveViewers = res.rows;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 只有成功获取数据后才增加页码
|
|
|
+ this.viewPageNum++;
|
|
|
+ } else {
|
|
|
+ uni.showToast({
|
|
|
+ title: res.msg,
|
|
|
+ icon: 'none'
|
|
|
+ });
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('获取观众列表失败:', error);
|
|
|
+ uni.showToast({
|
|
|
+ title: '获取数据失败',
|
|
|
+ icon: 'none'
|
|
|
+ });
|
|
|
+ } finally {
|
|
|
+ this.viewLoading = false;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 滚动到底部触发
|
|
|
+ handleScrollToLower() {
|
|
|
+ // 清除上一次未执行的定时器,避免重复请求
|
|
|
+ if (this.scrollTimer) {
|
|
|
+ clearTimeout(this.scrollTimer);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 延迟0.5秒(500毫秒)执行接口请求
|
|
|
+ this.scrollTimer = setTimeout(() => {
|
|
|
+ this.getliveUser(true); // 加载更多数据
|
|
|
+ }, 1000); // 延迟时间:500毫秒
|
|
|
+ },
|
|
|
+ // gettalklist() {
|
|
|
+ // if (!this.liveId) return;
|
|
|
+ // const param = {
|
|
|
+ // id: this.liveId
|
|
|
+ // }
|
|
|
+ // gettextlist(param).then(res => {
|
|
|
+ // if (res.code == 200) {
|
|
|
+ // this.talklist = res.data
|
|
|
+ // // console.log(res.data);
|
|
|
+ // this.$nextTick(() => {
|
|
|
+ // this.scrollIntoView = `list_${this.talklist.length-1}`
|
|
|
+ // })
|
|
|
+ // }
|
|
|
+ // })
|
|
|
+ // },
|
|
|
+ // 获取最近聊天记录
|
|
|
+ getLiveMsg() {
|
|
|
+ if (!this.liveId) return;
|
|
|
+ let pageSize = 30
|
|
|
+ let pageNum = 1
|
|
|
+ liveMsg(this.liveId, pageSize, pageNum).then(res => {
|
|
|
+ if (res.code == 200) {
|
|
|
+ // 使用Vue.set或直接赋值确保响应式更新
|
|
|
+ this.talklist = [...res.rows] // 使用展开运算符创建新数组
|
|
|
+ console.log("获取最近聊天记录>>", this.talklist)
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.scrollIntoView = `list_${this.talklist.length-1}`
|
|
|
+ console.log('应该滚动到:', this.scrollIntoView)
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ uni.showToast({
|
|
|
+ title: res.msg,
|
|
|
+ icon: 'none'
|
|
|
+ });
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+ // 点击红包
|
|
|
+ onRed() {
|
|
|
+ console.log("点了")
|
|
|
+
|
|
|
+ if (!this.liveId) return;
|
|
|
+ console.log("点了this.liveId", this.liveId)
|
|
|
+ if (!this.redInfo?.redId) return;
|
|
|
+ console.log("点了this.redInfo?.redId", this.redInfo.redId)
|
|
|
+ console.log("点了this.userinfo.userId", this.userinfo.userId)
|
|
|
+ let data = {
|
|
|
+ liveId: this.liveId,
|
|
|
+ userId: this.userinfo.userId,
|
|
|
+ redId: this.redInfo.redId,
|
|
|
+ }
|
|
|
+ liveRed(data).then(res => {
|
|
|
+ if (res.code == 200) {} else {
|
|
|
+ uni.showToast({
|
|
|
+ title: res.msg,
|
|
|
+ icon: 'none'
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
+ rej => {}
|
|
|
+ );
|
|
|
+ },
|
|
|
+ handleSearchInput() {
|
|
|
+ // 使用防抖优化性能,避免频繁请求
|
|
|
+ clearTimeout(this.searchTimer);
|
|
|
+ this.searchTimer = setTimeout(() => {
|
|
|
+ this.queryCollect();
|
|
|
+ }, 500); // 500毫秒延迟
|
|
|
+ },
|
|
|
+ // 显示购买提示信息
|
|
|
+ showPurchaseMessage() {
|
|
|
+ // 清除之前的定时器
|
|
|
+ if (this.purchasePromptTimer) {
|
|
|
+ clearTimeout(this.purchasePromptTimer);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 显示提示
|
|
|
+ this.showPurchasePrompt = true;
|
|
|
+
|
|
|
+ // 2秒后自动隐藏
|
|
|
+ this.purchasePromptTimer = setTimeout(() => {
|
|
|
+ this.showPurchasePrompt = false;
|
|
|
+ }, 2000);
|
|
|
+ },
|
|
|
+ truncateString(str, maxLength) {
|
|
|
+ return str.length > maxLength ? str.slice(0, maxLength) + '...' : str;
|
|
|
+ },
|
|
|
+ goStore() {
|
|
|
+ console.log("带过去storeId", this.storeId)
|
|
|
+ uni.navigateTo({
|
|
|
+ url: '/pages_shop/store?liveId=' + this.liveId + "&storeId=" + this.storeId
|
|
|
+ })
|
|
|
+ },
|
|
|
+ // 去订单列表
|
|
|
+ goOrderList() {
|
|
|
+ uni.navigateTo({
|
|
|
+ url: "/pages_shop/order"
|
|
|
+ })
|
|
|
+ },
|
|
|
+
|
|
|
+ // 初始化HLS播放器
|
|
|
+ initHlsPlayer() {
|
|
|
+ if (Hls.isSupported() && this.livingUrl) {
|
|
|
+ const video = document.getElementById('myVideo');
|
|
|
+ if (video) {
|
|
|
+ this.hlsPlayer = new Hls();
|
|
|
+ this.hlsPlayer.loadSource(this.livingUrl);
|
|
|
+ this.hlsPlayer.attachMedia(video);
|
|
|
+ this.hlsPlayer.on(Hls.Events.MANIFEST_PARSED, () => {
|
|
|
+ video.play();
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 视频错误处理
|
|
|
+ videoError(e) {
|
|
|
+ console.error('视频播放错误:', e.detail.errMsg);
|
|
|
+ // 尝试重新加载或切换到备用流
|
|
|
+ if (this.livingUrl) {
|
|
|
+ setTimeout(() => {
|
|
|
+ this.videoContext.play();
|
|
|
+ }, 2000);
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ getliving() {
|
|
|
+ if (!this.liveId) return;
|
|
|
+ console.log("获取直播信息");
|
|
|
+ const param = {
|
|
|
+ id: this.liveId
|
|
|
+ };
|
|
|
+
|
|
|
+ getlive(param).then(res => {
|
|
|
+ if (res.code !== 200) {
|
|
|
+ uni.showToast({
|
|
|
+ title: res.msg,
|
|
|
+ icon: 'none',
|
|
|
+ duration: 2000
|
|
|
+ });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ this.livedata = res.data;
|
|
|
+ this.showType = res.data.showType;
|
|
|
+ this.storeId = res.storeId
|
|
|
+ this.queryCollect() //查询店铺
|
|
|
+ console.log("横屏1或竖屏2", this.showType)
|
|
|
+ // 1. 根据直播类型设置播放地址
|
|
|
+ if (res.data.liveType === 2) {
|
|
|
+ // 回放视频
|
|
|
+ this.videoUrl = res.data.videoUrl;
|
|
|
+ this.autoplay = true;
|
|
|
+ // this.placeholderText = "观看回放中";
|
|
|
+ this.placeholderText = "说点什么...";
|
|
|
+ this.talkdisabled = false;
|
|
|
+ } else if (res.data.liveType === 1) {
|
|
|
+ // 直播流
|
|
|
+ this.livingUrl = res.data.flvHlsUrl;
|
|
|
+ this.autoplay = true;
|
|
|
+ this.placeholderText = "说点什么...";
|
|
|
+ this.talkdisabled = false;
|
|
|
+ } else {
|
|
|
+ // 未开播
|
|
|
+ this.livingUrl = "";
|
|
|
+ this.autoplay = false;
|
|
|
+ this.placeholderText = "直播未开始,暂时无法发言";
|
|
|
+ this.talkdisabled = true;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 根据不同平台处理播放逻辑
|
|
|
+ this.$nextTick(() => {
|
|
|
+ if (!this.livingUrl) return;
|
|
|
+
|
|
|
+ // H5 平台:使用 HLS.js 处理 .m3u8 流
|
|
|
+ // #ifdef H5
|
|
|
+ if (this.livingUrl.includes('.m3u8')) {
|
|
|
+ this.initHlsPlayer();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // #endif
|
|
|
+
|
|
|
+ // 小程序/App 平台:直接使用 video 组件
|
|
|
+ if (!this.videoContext) {
|
|
|
+ this.videoContext = uni.createVideoContext('myVideo', this);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (res.data.liveType === 1) {
|
|
|
+ // 回放:跳转到指定位置
|
|
|
+ this.videoContext.seek(res.data.nowDuration || 0);
|
|
|
+ } else {
|
|
|
+ // 直播:直接播放
|
|
|
+ this.videoContext.play();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }).catch(err => {
|
|
|
+ console.error("获取直播信息失败:", err);
|
|
|
+ uni.showToast({
|
|
|
+ title: "获取直播信息失败",
|
|
|
+ icon: 'none'
|
|
|
+ });
|
|
|
+ });
|
|
|
+ },
|
|
|
+ maskString(str, maskChar = '*') {
|
|
|
+ // 新增:如果str是undefined或null,直接返回空字符串
|
|
|
+ if (!str) return '';
|
|
|
+ // 确保str是字符串(如果是数字等类型,先转为字符串)
|
|
|
+ const strVal = String(str);
|
|
|
+ return strVal.split('').map((char, index) => (index === 0 ? char : maskChar)).join('');
|
|
|
+ },
|
|
|
+
|
|
|
+ getPureDecimal(num, precision = 6) {
|
|
|
+ const decimalPart = Math.abs(num).toFixed(precision).split('.')[1];
|
|
|
+ return decimalPart?.replace(/0+$/, '') || ''; // 移除末尾多余的0
|
|
|
+ },
|
|
|
+
|
|
|
+ // 返回上一个页面并关闭WebSocket
|
|
|
+ goBack() {
|
|
|
+ const pages = getCurrentPages();
|
|
|
+ // 如果页面栈长度大于1,说明有上一页可以返回
|
|
|
+ this.closeWebSocket();
|
|
|
+ if (pages.length > 1) {
|
|
|
+ uni.navigateBack();
|
|
|
+ } else {
|
|
|
+ // 如果没有上一页,跳转到默认页面(比如首页)
|
|
|
+ uni.switchTab({
|
|
|
+ url: '/pages/list/index' // 替换为你的首页路径
|
|
|
+ });
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ },
|
|
|
+
|
|
|
+ //直播间点赞、关注、在线人数数据
|
|
|
+ getliveViewData() {
|
|
|
+ if (!this.liveId) return;
|
|
|
+ getLiveViewData(this.liveId).then(res => {
|
|
|
+ if (res.code == 200) {
|
|
|
+ // console.log("直播间点赞、关注、在线人数数据>>>>", res)
|
|
|
+ this.liveViewData = res
|
|
|
+ } else {
|
|
|
+ uni.showToast({
|
|
|
+ title: res.msg,
|
|
|
+ icon: 'none'
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
+ rej => {}
|
|
|
+ );
|
|
|
+ },
|
|
|
+ //正在购买
|
|
|
+ getliveOrder() {
|
|
|
+ if (!this.liveId) return;
|
|
|
+ liveOrderUser(this.liveId).then(res => {
|
|
|
+ if (res.code == 200) {
|
|
|
+ console.log("正在购买>>>>", res)
|
|
|
+ this.orderUser = res
|
|
|
+
|
|
|
+ } else {
|
|
|
+ uni.showToast({
|
|
|
+ title: res.msg,
|
|
|
+ icon: 'none'
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
+ rej => {}
|
|
|
+ );
|
|
|
+ },
|
|
|
+ //小黄车 店铺展示
|
|
|
+ // getliveStore() {
|
|
|
+ // if (!this.liveId) return;
|
|
|
+ // let data = {
|
|
|
+ // pageSize: 10,
|
|
|
+ // page: 1
|
|
|
+ // }
|
|
|
+ // liveStore(this.liveId, data).then(res => {
|
|
|
+ // if (res.code == 200) {
|
|
|
+ // console.log("小黄车 店铺展示>>>>", res)
|
|
|
+ // this.products = res.data
|
|
|
+ // this.queryCollect()
|
|
|
+ // } else {
|
|
|
+ // uni.showToast({
|
|
|
+ // title: res.msg,
|
|
|
+ // icon: 'none'
|
|
|
+ // });
|
|
|
+ // }
|
|
|
+ // },
|
|
|
+ // rej => {}
|
|
|
+ // );
|
|
|
+ // },
|
|
|
+ // 获取直播间信息接口
|
|
|
+ getLiveinformation() {
|
|
|
+ if (!this.liveId) return;
|
|
|
+ getLiveInfo(this.liveId).then(res => {
|
|
|
+ if (res.code == 200) {
|
|
|
+ console.log("获取直播间信息接口>>>>", res)
|
|
|
+ this.livingUrl = res.livingUrl
|
|
|
+ } else {
|
|
|
+ uni.showToast({
|
|
|
+ title: res.msg,
|
|
|
+ icon: 'none'
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
+ rej => {}
|
|
|
+ );
|
|
|
+ },
|
|
|
+
|
|
|
+
|
|
|
+ // 点赞
|
|
|
+ onLike() {
|
|
|
+ if (!this.liveId) return;
|
|
|
+ liveDataLike(this.liveId).then(res => {
|
|
|
+ if (res.code == 200) {
|
|
|
+ if (res.like) {
|
|
|
+ this.liveViewData.like++
|
|
|
+ } else {
|
|
|
+ uni.showToast({
|
|
|
+ title: res.msg,
|
|
|
+ icon: 'none'
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ } else {
|
|
|
+ uni.showToast({
|
|
|
+ title: res.msg,
|
|
|
+ icon: 'none'
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
+ rej => {}
|
|
|
+ );
|
|
|
+
|
|
|
+ },
|
|
|
+ // 去购买,跳商品详情
|
|
|
+ goShop(productId, goodsId) {
|
|
|
+ uni.navigateTo({
|
|
|
+ url: '/pages_shop/goods?productId=' + productId + '&liveId=' + this.liveId + '&goodsId=' +
|
|
|
+ goodsId + '&storeId=' + this.storeId
|
|
|
+ })
|
|
|
+ },
|
|
|
+
|
|
|
+ // 查询店铺
|
|
|
+ queryCollect() {
|
|
|
+ if (!this.storeId) return;
|
|
|
+ store(this.storeId, this.inputInfo).then(res => {
|
|
|
+ if (res.code == 200) {
|
|
|
+ console.log("查询店铺>>", res)
|
|
|
+ this.products = res.data.goodsList
|
|
|
+ this.store = res.data
|
|
|
+ } else {
|
|
|
+ uni.showToast({
|
|
|
+ title: res.msg,
|
|
|
+ icon: 'none'
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
+ rej => {}
|
|
|
+ );
|
|
|
+ },
|
|
|
+ // 店铺收藏
|
|
|
+ onStoreCollect() {
|
|
|
+ if (!this.storeId) return;
|
|
|
+ collectStore(this.storeId).then(res => {
|
|
|
+ if (res.code == 200) {
|
|
|
+ uni.showToast({
|
|
|
+ title: res.msg,
|
|
|
+ icon: 'none'
|
|
|
+ });
|
|
|
+ this.store.isFavorite = !this.store.isFavorite
|
|
|
+ } else {
|
|
|
+ uni.showToast({
|
|
|
+ title: res.msg,
|
|
|
+ icon: 'none'
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
+ rej => {}
|
|
|
+ );
|
|
|
+ },
|
|
|
+ // 商品收藏
|
|
|
+ onGoodsCollect(item) {
|
|
|
+ if (!item || item.length === 0 || !item.goodsId) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ collectGoods(item.goodsId).then(res => {
|
|
|
+ if (res.code == 200) {
|
|
|
+ uni.showToast({
|
|
|
+ title: res.msg,
|
|
|
+ icon: 'none'
|
|
|
+ });
|
|
|
+ item.isFavorite = !item.isFavorite
|
|
|
+ } else {
|
|
|
+ uni.showToast({
|
|
|
+ title: res.msg,
|
|
|
+ icon: 'none'
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
+ rej => {}
|
|
|
+ );
|
|
|
+ },
|
|
|
+ // 关注
|
|
|
+ onFollow() {
|
|
|
+ if (!this.liveId) return;
|
|
|
+ follow(this.liveId).then(res => {
|
|
|
+ this.isFollow = !this.isFollow
|
|
|
+ uni.showToast({
|
|
|
+ title: res.msg,
|
|
|
+ icon: 'none'
|
|
|
+ });
|
|
|
+ },
|
|
|
+ rej => {}
|
|
|
+ );
|
|
|
+ },
|
|
|
+ // 时间戳
|
|
|
+ initTime() {
|
|
|
+ const now = new Date();
|
|
|
+ this.timestamp = now.getTime(); // 例如:'2023-04-01 12:00:00'
|
|
|
+ },
|
|
|
+ // initWebSocket() {
|
|
|
+ // const liveWS = new LiveWS('ws://your-server.com', 123, 456);
|
|
|
+
|
|
|
+ // // 从 URL 中解析 timestamp
|
|
|
+ // const urlParams = new URL(liveWS.url).searchParams;
|
|
|
+ // this.timestamp = urlParams.get('timestamp');
|
|
|
+ // // console.log('Timestamp:', timestamp);
|
|
|
+ // },
|
|
|
+
|
|
|
+ // 弹幕滚动
|
|
|
+ lowerChat: function(e) {
|
|
|
+ console.log(e)
|
|
|
+ },
|
|
|
+ scroll: function(e) {
|
|
|
+ console.log(e)
|
|
|
+ this.old.scrollTop = e.detail.scrollTop
|
|
|
+ },
|
|
|
+
|
|
|
+
|
|
|
+ loadmore() {
|
|
|
+ // for (let i = 0; i < 30; i++) {
|
|
|
+ // this.indexList.push({
|
|
|
+ // url: this.shopList[uni.$u.random(0, this.shopList.length - 1)],
|
|
|
+ // });
|
|
|
+ // }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 弹幕
|
|
|
+ scrollchat() {
|
|
|
+ // this.loadmore();
|
|
|
+ },
|
|
|
+
|
|
|
+
|
|
|
+ noredanswer() {
|
|
|
+ if (this.Answerlistall > 0) {
|
|
|
+ this.showAnswer = !this.this.showAnswer
|
|
|
+ } else {
|
|
|
+ uni.showToast({
|
|
|
+ title: '暂无题目',
|
|
|
+ icon: 'none',
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
+ redbagAnswer() {
|
|
|
+ this.showAnswerred = !this.showAnswerred
|
|
|
+ this.answerbtn = !this.answerbtn
|
|
|
+
|
|
|
+ },
|
|
|
+ submitAnswers() {
|
|
|
+ if (this.isSubmit) return;
|
|
|
+ this.isSubmit = true;
|
|
|
+ const data = {
|
|
|
+ questionId: this.answerlist.id,
|
|
|
+ answer: this.checkboxFormValue
|
|
|
+ }
|
|
|
+ submitAnswer(data).then(res => {
|
|
|
+ if (res.code == 200) {
|
|
|
+
|
|
|
+ uni.showToast({
|
|
|
+ title: res.msg,
|
|
|
+ icon: 'none',
|
|
|
+ });
|
|
|
+ }
|
|
|
+ // 本地切换下一题
|
|
|
+ if (this.answerfrist < this.Answerlistall - 1) {
|
|
|
+ this.answerfrist++;
|
|
|
+ this.answerlist = this.allAnswerLists[this.answerfrist];
|
|
|
+ this.answerlist.content = JSON.parse(this.answerlist.content);
|
|
|
+ } else {
|
|
|
+ uni.showToast({
|
|
|
+ title: '已是最后一题',
|
|
|
+ icon: 'none'
|
|
|
+ });
|
|
|
+ this.showAnswer = false; // 自动关闭弹窗
|
|
|
+ }
|
|
|
+ this.checkboxValue = []
|
|
|
+ this.checkboxFormValue = "";
|
|
|
+ uni.showToast({
|
|
|
+ title: res.msg,
|
|
|
+ icon: 'none'
|
|
|
+ });
|
|
|
+ }).finally(e => {
|
|
|
+ this.isSubmit = false;
|
|
|
+ })
|
|
|
+ },
|
|
|
+ // handleCheckboxSelect(value) {
|
|
|
+ // const index = this.checkboxValue.indexOf(value)
|
|
|
+ // console.log(value)
|
|
|
+ // if (this.answerlist.type == 1) {
|
|
|
+ // this.checkboxValue = [value]
|
|
|
+ // this.checkboxFormValue = this.checkboxValue.join(',')
|
|
|
+ // setTimeout(() => {
|
|
|
+ // uni.showToast({
|
|
|
+ // title: '准备下一题',
|
|
|
+ // icon: 'none',
|
|
|
+ // duration: 2000
|
|
|
+ // });
|
|
|
+ // }, 1000);
|
|
|
+ // this.submitAnswers()
|
|
|
+ // console.log(this.checkboxValue)
|
|
|
+ // } else if (this.answerlist.type == 2) {
|
|
|
+ // if (index > -1) {
|
|
|
+ // this.checkboxValue.splice(index, 1)
|
|
|
+ // this.checkboxFormValue = this.checkboxValue.join(',')
|
|
|
+ // } else {
|
|
|
+ // this.checkboxValue.push(value)
|
|
|
+ // this.checkboxFormValue = this.checkboxValue.join(',')
|
|
|
+ // }
|
|
|
+ // console.log(this.checkboxFormValue)
|
|
|
+ // }
|
|
|
+ // },
|
|
|
+ getAnswerlists() {
|
|
|
+ if (!this.liveId) return;
|
|
|
+ const data = {
|
|
|
+ liveId: this.liveId
|
|
|
+ }
|
|
|
+ getAnswerlist(data).then(res => {
|
|
|
+ if (res.code == 200) {
|
|
|
+ if (res.data.length > 0) {
|
|
|
+ this.allAnswerLists = res.data; // 存储所有题目
|
|
|
+ this.Answerlistall = res.data.length;
|
|
|
+ if (this.allAnswerLists.length > 0) {
|
|
|
+ this.answerlist = this.allAnswerLists[0];
|
|
|
+ this.answerlist.content = JSON.parse(this.allAnswerLists[0].content);
|
|
|
+ }
|
|
|
+ this.showAnswer = true
|
|
|
+ } else {
|
|
|
+ this.showAnswer = false
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ })
|
|
|
+ },
|
|
|
+
|
|
|
+ openShop() {
|
|
|
+
|
|
|
+ },
|
|
|
+ openViews() {
|
|
|
+
|
|
|
+ },
|
|
|
+ close() {
|
|
|
+ this.showadd = !this.showadd
|
|
|
+ },
|
|
|
+ closes() {
|
|
|
+ this.showziliao = !this.showziliao
|
|
|
+ },
|
|
|
+ closest() {
|
|
|
+ this.showAnswer = !this.showAnswer
|
|
|
+ },
|
|
|
+ closestred() {
|
|
|
+ this.showAnswerred = !this.showAnswerred
|
|
|
+ },
|
|
|
+ closeanswer() {
|
|
|
+ this.answerbtn = !this.answerbtn
|
|
|
+ },
|
|
|
+ // 关闭小黄车
|
|
|
+ closeShop() {
|
|
|
+ this.shopping = !this.shopping
|
|
|
+ },
|
|
|
+ // 关闭红包
|
|
|
+ closeRed() {
|
|
|
+ this.showRed = !this.showRed
|
|
|
+ },
|
|
|
+ // longPress() {
|
|
|
+ // this.timeout = setTimeout(() => {
|
|
|
+ // this.isLongPress = true;
|
|
|
+ // // 执行保存图片的操作
|
|
|
+ // uni.saveImageToPhotosAlbum({
|
|
|
+ // filePath: this.livedata.qwQrCode, // 图片的本地路径或网络路径
|
|
|
+ // success: () => {
|
|
|
+ // uni.showToast({
|
|
|
+ // title: '保存成功'
|
|
|
+ // });
|
|
|
+ // },
|
|
|
+ // fail: () => {
|
|
|
+ // uni.showToast({
|
|
|
+ // title: '',
|
|
|
+ // icon: 'none'
|
|
|
+ // });
|
|
|
+ // }
|
|
|
+ // });
|
|
|
+ // }, 500); // 设置长按的阈值时间,这里是500毫秒
|
|
|
+ // },
|
|
|
+ // cancelLongPress() {
|
|
|
+ // clearTimeout(this.timeout);
|
|
|
+ // this.isLongPress = false;
|
|
|
+ // },
|
|
|
+ // 触摸开始
|
|
|
+ handleTouchStart() {
|
|
|
+ this.isZoom = true; // 触发放大效果
|
|
|
+ },
|
|
|
+ // 触摸结束
|
|
|
+ handleTouchEnd() {
|
|
|
+ this.isZoom = false; // 恢复原状
|
|
|
+ },
|
|
|
+ getEWechatSdk() {
|
|
|
+ // 只在H5平台执行
|
|
|
+ // #ifdef H5
|
|
|
+ let eWechatSdk = '';
|
|
|
+ if (/(Android)/i.test(navigator.userAgent)) {
|
|
|
+ eWechatSdk = 'jWeixin';
|
|
|
+ } else if (/(iPhone|iPad|iPod|iOS)/i.test(navigator.userAgent)) {
|
|
|
+ eWechatSdk = 'wx';
|
|
|
+ } else {
|
|
|
+ eWechatSdk = 'jWeixin';
|
|
|
+ }
|
|
|
+ uni.setStorageSync("wxSdk", eWechatSdk);
|
|
|
+ // #endif
|
|
|
+ },
|
|
|
+ closeWebSocket() {
|
|
|
+ this.isManualClose = true;
|
|
|
+
|
|
|
+ // 清除所有定时器
|
|
|
+ clearInterval(this.pingpangTimes);
|
|
|
+ clearTimeout(this.reconnectTimer);
|
|
|
+
|
|
|
+ // 关闭 WebSocket 连接
|
|
|
+ if (this.socketInstance) {
|
|
|
+ try {
|
|
|
+ uni.closeSocket();
|
|
|
+ console.log('WebSocket 已安全关闭');
|
|
|
+ } catch (e) {
|
|
|
+ console.error('关闭 WebSocket 时出错:', e);
|
|
|
+ }
|
|
|
+ this.socketInstance = null;
|
|
|
+ }
|
|
|
+ isSocketOpen = false;
|
|
|
+ },
|
|
|
+ reConnect() {
|
|
|
+ var that = this;
|
|
|
+ try {
|
|
|
+ uni.closeSocket();
|
|
|
+ } catch (e) {
|
|
|
+
|
|
|
+ }
|
|
|
+ setTimeout(function() {
|
|
|
+ that.initSocket();
|
|
|
+ }, 10000);
|
|
|
+
|
|
|
+ },
|
|
|
+ startHeartbeat() {
|
|
|
+ clearInterval(this.pingpangTimes);
|
|
|
+
|
|
|
+ this.pingpangTimes = setInterval(() => {
|
|
|
+ if (!isSocketOpen) return;
|
|
|
+
|
|
|
+ const pingData = {
|
|
|
+ cmd: 'ping',
|
|
|
+ timestamp: Date.now()
|
|
|
+ };
|
|
|
+ uni.sendSocketMessage({
|
|
|
+ data: JSON.stringify(pingData),
|
|
|
+ success: () => console.debug('心跳发送成功'),
|
|
|
+ fail: (err) => {
|
|
|
+ console.error('心跳发送失败:', err);
|
|
|
+ this.scheduleReconnect();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }, 30000); // 30秒心跳间隔
|
|
|
+ },
|
|
|
+ initSocket() {
|
|
|
+ // 防止重复创建
|
|
|
+ if (isSocketOpen || this.reconnectCount >= this.maxReconnectAttempts) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ this.closeWebSocket(); // 确保先关闭旧连接
|
|
|
+ this.isManualClose = false;
|
|
|
+
|
|
|
+ // 参数校验
|
|
|
+ if (!this.liveId) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 生成签名
|
|
|
+ const signature = CryptoJS.HmacSHA256(
|
|
|
+ `${this.liveId}${this.userinfo.userId}${this.userType}${this.timestamp}`,
|
|
|
+ this.timestamp.toString()
|
|
|
+ ).toString(CryptoJS.enc.Hex);
|
|
|
+
|
|
|
+ // 创建连接
|
|
|
+ try {
|
|
|
+ this.socketInstance = uni.connectSocket({
|
|
|
+ url: `${wsUrl}?userId=${this.userinfo.userId}&liveId=${this.liveId}&userType=${this.userType}×tamp=${this.timestamp}&signature=${signature}`,
|
|
|
+ success: () => {
|
|
|
+ // 事件监听统一管理
|
|
|
+ this.setupSocketListeners();
|
|
|
+ },
|
|
|
+ fail: (err) => {
|
|
|
+ console.error('WebSocket 连接失败:', err);
|
|
|
+ this.scheduleReconnect();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ } catch (e) {
|
|
|
+ console.error('创建 WebSocket 异常:', e);
|
|
|
+ this.scheduleReconnect();
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 设置 WebSocket 监听器
|
|
|
+ setupSocketListeners() {
|
|
|
+ uni.onSocketOpen(() => {
|
|
|
+ isSocketOpen = true;
|
|
|
+ this.reconnectCount = 0;
|
|
|
+ console.log('WebSocket 连接成功');
|
|
|
+ this.startHeartbeat();
|
|
|
+ });
|
|
|
+
|
|
|
+ uni.onSocketClose(() => {
|
|
|
+ isSocketOpen = false;
|
|
|
+ console.log('WebSocket 连接关闭');
|
|
|
+ if (!this.isManualClose) {
|
|
|
+ this.scheduleReconnect();
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ uni.onSocketError((err) => {
|
|
|
+ isSocketOpen = false;
|
|
|
+ console.error('WebSocket 错误:', err);
|
|
|
+ if (!this.isManualClose) {
|
|
|
+ this.scheduleReconnect();
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ uni.onSocketMessage((res) => {
|
|
|
+ try {
|
|
|
+ const redata = JSON.parse(res.data);
|
|
|
+ this.talklist.push(redata.data);
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.scrollIntoView = `list_${this.talklist.length - 1}`; // 最后一项的id
|
|
|
+ });
|
|
|
+ if (redata.data.cmd == 'deleteId') {
|
|
|
+ uni.$emit('deleteId');
|
|
|
+ }
|
|
|
+
|
|
|
+ // else if (redata.data.cmd == 'init') {
|
|
|
+ // uni.$emit('init', redata.data);
|
|
|
+ // } else if (redata.data.cmd == 'reload') {
|
|
|
+ // uni.$emit('reload');
|
|
|
+ // }
|
|
|
+ else if (redata.data.cmd == 'red') {
|
|
|
+ // 领红包
|
|
|
+ this.redInfo = JSON.parse(redata.data.data)
|
|
|
+ console.log(" 领红包1redata.data>>", redata.data)
|
|
|
+ console.log(" 领红包2redata.data.data>>", redata.data.data)
|
|
|
+ console.log(" 领红包3this.redInfo>>", this.redInfo)
|
|
|
+ console.log(" 领红包4this.redInfo.redId>>", this.redInfo.redId)
|
|
|
+ // let redId= this.redInfo.redId
|
|
|
+ // this.redInfo.redNum=redata.data.redNum
|
|
|
+ // this.redInfo.redStatus=redata.data.redStatus
|
|
|
+ // this.redInfo.duration=redata.data.duration
|
|
|
+
|
|
|
+ } else if (redata.data.cmd == 'sendRedPacketQuestion') {
|
|
|
+ const list = JSON.parse(redata.data.data)
|
|
|
+ this.redanswerAll = [...this.redanswerAll, ...list]
|
|
|
+ if (this.redanswerAll[1].randomAmount !== null) {
|
|
|
+ this.redanswertips = JSON.parse(this.redanswerAll[0]
|
|
|
+ .randomAmount)
|
|
|
+ }
|
|
|
+ console.log(this.redanswertips)
|
|
|
+ console.log(this.redanswerAll)
|
|
|
+ } else if (redata.data.cmd == 'entry') {
|
|
|
+ this.showWelcomeMessage = true
|
|
|
+ // setTimeout(() => {
|
|
|
+ // this.showWelcomeMessage = false;
|
|
|
+ // }, 1000);
|
|
|
+ uni.$emit('entry', redata.data);
|
|
|
+ } else if (redata.data.cmd == 'live_start') {
|
|
|
+ // 直播开始
|
|
|
+ } else if (redata.data.cmd == 'live_end') {
|
|
|
+ // 直播结束
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ console.error('解析消息失败:', e);
|
|
|
+ }
|
|
|
+ })
|
|
|
+ },
|
|
|
+ scheduleReconnect() {
|
|
|
+ if (this.isManualClose || this.reconnectCount >= this.maxReconnectAttempts) return;
|
|
|
+
|
|
|
+ this.reconnectCount++;
|
|
|
+ const delay = Math.min(3000 * this.reconnectCount, 30000);
|
|
|
+
|
|
|
+ console.log(`将在 ${delay}ms 后重连 (尝试 ${this.reconnectCount}/${this.maxReconnectAttempts})`);
|
|
|
+
|
|
|
+ this.reconnectTimer = setTimeout(() => {
|
|
|
+ this.initSocket();
|
|
|
+ }, delay);
|
|
|
+ },
|
|
|
+ sendMsg() {
|
|
|
+ if (!isSocketOpen) {
|
|
|
+ uni.showToast({
|
|
|
+ title: "连接未建立,请稍后再试",
|
|
|
+ icon: 'none'
|
|
|
+ });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!this.socketInstance) {
|
|
|
+ uni.showToast({
|
|
|
+ title: "连接异常",
|
|
|
+ icon: 'none'
|
|
|
+ });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const data = {
|
|
|
+ liveId: this.liveId,
|
|
|
+ userId: this.userinfo.userId,
|
|
|
+ userType: 0,
|
|
|
+ cmd: "sendMsg",
|
|
|
+ msg: this.value,
|
|
|
+ nickName: this.userinfo.nickName,
|
|
|
+ avatar: this.userinfo.avatar
|
|
|
+ };
|
|
|
+
|
|
|
+ if (!this.value.trim()) { // 优化空消息判断
|
|
|
+ uni.showToast({
|
|
|
+ title: "不能发送空消息",
|
|
|
+ icon: 'none'
|
|
|
+ });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 使用正确的实例 this.socketInstance 发送消息
|
|
|
+ this.socketInstance.send({
|
|
|
+ data: JSON.stringify(data),
|
|
|
+ success: () => {
|
|
|
+ console.log("发送成功");
|
|
|
+ this.value = ''; // 清空输入框
|
|
|
+ },
|
|
|
+ fail: (err) => {
|
|
|
+ console.error("发送失败:", err);
|
|
|
+ uni.showToast({
|
|
|
+ title: "发送失败,请重试",
|
|
|
+ icon: 'none'
|
|
|
+ });
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+ },
|
|
|
+ };
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped lang="scss">
|
|
|
+ .loading-more,
|
|
|
+ .no-more {
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ padding: 20rpx 0;
|
|
|
+ color: #999;
|
|
|
+ font-size: 24rpx;
|
|
|
+ }
|
|
|
+
|
|
|
+ .loading-more {
|
|
|
+ flex-direction: column;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* button自带样式清除 */
|
|
|
+ .student-orther-icon button::after {
|
|
|
+ border: none !important;
|
|
|
+ padding: 0 !important;
|
|
|
+ margin: 0 !important;
|
|
|
+ }
|
|
|
+
|
|
|
+ .student-orther-icon button {
|
|
|
+ background-color: transparent !important;
|
|
|
+ padding: 0 !important;
|
|
|
+ line-height: inherit !important;
|
|
|
+ margin: 0 !important;
|
|
|
+ width: auto !important;
|
|
|
+ font-weight: 500 !important;
|
|
|
+ border-radius: none !important;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 抽奖
|
|
|
+ // .answerpop {
|
|
|
+ // background: linear-gradient(to right, #fff7f8, #fee1e2);
|
|
|
+ // margin-top: 30rpx;
|
|
|
+ // padding: 10rpx 20rpx;
|
|
|
+ // width: calc(100% - 40rpx);
|
|
|
+ // border-radius: 20rpx;
|
|
|
+ // display: flex;
|
|
|
+ // align-items: center;
|
|
|
+ // justify-content: space-between;
|
|
|
+ // box-shadow: 2px 2px 4px 0 rgba(255, 124, 126, 0.1);
|
|
|
+
|
|
|
+ // .answera {
|
|
|
+ // display: flex;
|
|
|
+ // justify-content: center;
|
|
|
+ // background-color: #fff;
|
|
|
+ // padding: 20rpx 0;
|
|
|
+ // width: 35%;
|
|
|
+ // border-radius: 40rpx;
|
|
|
+ // align-items: center;
|
|
|
+ // margin: 10rpx 0;
|
|
|
+ // box-shadow: 2px 2px 4px 0 rgba(72, 72, 72, 0.1);
|
|
|
+
|
|
|
+ // image {
|
|
|
+ // width: 40rpx;
|
|
|
+ // height: 40rpx;
|
|
|
+ // margin-right: 10rpx;
|
|
|
+ // }
|
|
|
+ // }
|
|
|
+ // }
|
|
|
+
|
|
|
+ // .submitbtn {
|
|
|
+ // width: 260rpx;
|
|
|
+ // margin: 0 auto;
|
|
|
+ // text-align: center;
|
|
|
+ // border-radius: 80rpx;
|
|
|
+ // padding: 20rpx 0;
|
|
|
+ // color: #fff;
|
|
|
+ // background-color: #ff5c03;
|
|
|
+ // }
|
|
|
+
|
|
|
+ // .itemanswer {
|
|
|
+ // padding: 20rpx 20rpx;
|
|
|
+ // text-align: center;
|
|
|
+ // border-radius: 80rpx;
|
|
|
+ // border: 2rpx solid #dddddd;
|
|
|
+ // margin: 12rpx 0;
|
|
|
+ // color: #555;
|
|
|
+ // width: calc(100% - 40rpx);
|
|
|
+ // }
|
|
|
+
|
|
|
+ // .answeract {
|
|
|
+ // background-color: rgba(0, 202, 166, 1);
|
|
|
+ // color: #fff;
|
|
|
+ // }
|
|
|
+
|
|
|
+ // .answerbox {
|
|
|
+ // width: 600rpx;
|
|
|
+ // border-radius: 20rpx;
|
|
|
+ // }
|
|
|
+
|
|
|
+ .welcome-message {
|
|
|
+ position: fixed;
|
|
|
+ width: 100%;
|
|
|
+ bottom: 460rpx;
|
|
|
+ left: 50%;
|
|
|
+ transform: translateX(-50%);
|
|
|
+ color: white;
|
|
|
+ padding: 10px 20px;
|
|
|
+ border-radius: 20px;
|
|
|
+ animation: fadeOut 1s ease 1s forwards;
|
|
|
+ z-index: 1000;
|
|
|
+ }
|
|
|
+
|
|
|
+ @keyframes fadeOut {
|
|
|
+ from {
|
|
|
+ opacity: 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ to {
|
|
|
+ opacity: 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // .container {
|
|
|
+ // position: relative;
|
|
|
+ // width: 100%;
|
|
|
+ // height: 100vh;
|
|
|
+ // overflow: hidden;
|
|
|
+ // }
|
|
|
+
|
|
|
+ // .talktext {
|
|
|
+ // border-radius: 8rpx;
|
|
|
+ // background-color: rgba(255, 255, 255, 0.1);
|
|
|
+
|
|
|
+ // view {
|
|
|
+ // width: 100%;
|
|
|
+ // }
|
|
|
+ // }
|
|
|
+
|
|
|
+ .talk-list {
|
|
|
+ border-radius: 30rpx;
|
|
|
+ background-color: rgba(33, 33, 33, 0.5);
|
|
|
+ padding: 10rpx 30rpx;
|
|
|
+ }
|
|
|
+
|
|
|
+ // .zoom-button-active {
|
|
|
+ // transform: scale(1.5);
|
|
|
+ // }
|
|
|
+
|
|
|
+ // .background-image {
|
|
|
+ // position: absolute;
|
|
|
+ // top: 0;
|
|
|
+ // left: -40rpx;
|
|
|
+ // width: 110%;
|
|
|
+ // height: 110%;
|
|
|
+ // object-fit: cover;
|
|
|
+ // filter: blur(20px);
|
|
|
+ // z-index: 0;
|
|
|
+ // }
|
|
|
+
|
|
|
+ // .background-images {
|
|
|
+ // position: absolute;
|
|
|
+ // top: 0;
|
|
|
+ // left: -40rpx;
|
|
|
+ // width: 110%;
|
|
|
+ // height: 110%;
|
|
|
+ // object-fit: cover;
|
|
|
+ // filter: blur(20px);
|
|
|
+ // z-index: 6;
|
|
|
+ // }
|
|
|
+
|
|
|
+ // .blackbg {
|
|
|
+ // position: absolute;
|
|
|
+ // top: 0;
|
|
|
+ // left: 0;
|
|
|
+ // width: 100%;
|
|
|
+ // height: 100%;
|
|
|
+ // background: rgba(0, 0, 0, 0.7);
|
|
|
+ // object-fit: cover;
|
|
|
+ // filter: blur(10px);
|
|
|
+ // z-index: 1;
|
|
|
+ // }
|
|
|
+
|
|
|
+ .content {
|
|
|
+ position: relative;
|
|
|
+ z-index: 2;
|
|
|
+ height: 100%;
|
|
|
+ width: 100%;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ justify-content: space-between;
|
|
|
+
|
|
|
+ .content-top {
|
|
|
+ width: 100%;
|
|
|
+ margin-top: 150rpx;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ padding: 0 24rpx;
|
|
|
+ box-sizing: border-box;
|
|
|
+
|
|
|
+ .sum {
|
|
|
+ width: 80rpx;
|
|
|
+ height: 52rpx;
|
|
|
+ background: rgba(0, 0, 0, 0.5);
|
|
|
+ border-radius: 26rpx 26rpx 26rpx 26rpx;
|
|
|
+ font-size: 24rpx;
|
|
|
+ color: #FFFFFF;
|
|
|
+ text-align: center;
|
|
|
+ line-height: 52rpx;
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .follow-btn {
|
|
|
+ padding: 8rpx 16rpx;
|
|
|
+ background: linear-gradient(270deg, #FF5C03 0%, #FFAC64 100%);
|
|
|
+ border-radius: 26rpx;
|
|
|
+ font-weight: 500;
|
|
|
+ font-size: 26rpx;
|
|
|
+ color: #FFFFFF;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .videolist {
|
|
|
+ position: relative;
|
|
|
+ }
|
|
|
+
|
|
|
+ .video {
|
|
|
+ height: 100vh;
|
|
|
+ /* 占屏幕高度的80% */
|
|
|
+ width: 100%;
|
|
|
+ background-color: rgba(0, 0, 0, 0.8);
|
|
|
+ }
|
|
|
+
|
|
|
+ .videotop {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ }
|
|
|
+
|
|
|
+ .video_row {
|
|
|
+ width: 100%;
|
|
|
+ max-height: 500rpx;
|
|
|
+ overflow: hidden;
|
|
|
+ margin-top: 360rpx;
|
|
|
+ }
|
|
|
+
|
|
|
+ .popup-video {
|
|
|
+ position: absolute;
|
|
|
+ top: 30%;
|
|
|
+ height: 500rpx;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ width: 100%;
|
|
|
+ color: #fff;
|
|
|
+ z-index: 9;
|
|
|
+
|
|
|
+ .more {
|
|
|
+ background-color: #3280fe;
|
|
|
+ border-radius: 80rpx;
|
|
|
+ width: 280rpx;
|
|
|
+ text-align: center;
|
|
|
+ height: 60rpx;
|
|
|
+ line-height: 60rpx;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .icon-bg {
|
|
|
+ background-color: rgba(157, 157, 157, 0.8);
|
|
|
+ border-radius: 50%;
|
|
|
+ width: 72rpx;
|
|
|
+ height: 72rpx;
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ transition: transform 0.2s ease;
|
|
|
+ }
|
|
|
+
|
|
|
+ .list {
|
|
|
+ width: 80%;
|
|
|
+ margin-bottom: 20rpx;
|
|
|
+ animation: xxxawdawd .2s;
|
|
|
+ }
|
|
|
+
|
|
|
+ @keyframes xxxawdawd {
|
|
|
+ from {
|
|
|
+ margin-top: 0rpx;
|
|
|
+ opacity: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ to {
|
|
|
+ margin-top: 20rpx;
|
|
|
+ opacity: 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .shop-prompt {
|
|
|
+ position: absolute;
|
|
|
+ bottom: 600rpx;
|
|
|
+ left: 24rpx;
|
|
|
+ padding: 6rpx 20rpx;
|
|
|
+ background: rgba(230, 154, 34, 0.7);
|
|
|
+ border-radius: 24rpx;
|
|
|
+ z-index: 9;
|
|
|
+ font-weight: 500;
|
|
|
+ font-size: 26rpx;
|
|
|
+ color: #FFFFFF;
|
|
|
+ transition: opacity 0.3s ease;
|
|
|
+ }
|
|
|
+
|
|
|
+ .siderow-group {
|
|
|
+ position: absolute;
|
|
|
+ top: 56%;
|
|
|
+ right: 50rpx;
|
|
|
+ z-index: 9;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+
|
|
|
+ .side-item {
|
|
|
+ font-weight: 500;
|
|
|
+ font-size: 24rpx;
|
|
|
+ color: #FFFFFF;
|
|
|
+ margin-bottom: 32rpx;
|
|
|
+ text-align: center;
|
|
|
+
|
|
|
+ button {
|
|
|
+ background-color: transparent;
|
|
|
+ margin: 0;
|
|
|
+ line-height: 1;
|
|
|
+ padding: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ image {
|
|
|
+ width: 72rpx;
|
|
|
+ height: 72rpx;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .side-group {
|
|
|
+ position: absolute;
|
|
|
+ // top: 30%;
|
|
|
+ top: 56%;
|
|
|
+ right: 50rpx;
|
|
|
+ z-index: 9;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+
|
|
|
+ .side-item {
|
|
|
+ font-weight: 500;
|
|
|
+ font-size: 24rpx;
|
|
|
+ color: #FFFFFF;
|
|
|
+ margin-bottom: 32rpx;
|
|
|
+ text-align: center;
|
|
|
+
|
|
|
+ button {
|
|
|
+ background-color: transparent;
|
|
|
+ margin: 0;
|
|
|
+ line-height: 1;
|
|
|
+ padding: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ image {
|
|
|
+ width: 72rpx;
|
|
|
+ height: 72rpx;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ .red-box {
|
|
|
+ .button {
|
|
|
+ margin: 0 auto;
|
|
|
+ width: 80%;
|
|
|
+ line-height: 88rpx;
|
|
|
+ height: 88rpx;
|
|
|
+ background: linear-gradient(90deg, #FF5701 0%, #FFB501 100%);
|
|
|
+ box-shadow: 0rpx 8rpx 8rpx 0rpx rgba(238, 124, 80, 0.2);
|
|
|
+ border-radius: 44rpx 44rpx 44rpx 44rpx;
|
|
|
+ font-weight: 600;
|
|
|
+ font-size: 32rpx;
|
|
|
+ color: #FFFFFF;
|
|
|
+ text-align: center;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .view-box {
|
|
|
+ position: relative;
|
|
|
+ height: 40vh;
|
|
|
+ /* 设置弹出层高度为视窗高度的70% */
|
|
|
+ padding: 40rpx 0rpx 120rpx;
|
|
|
+ box-sizing: border-box;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+
|
|
|
+ .scroll-content {
|
|
|
+ flex: 1;
|
|
|
+ overflow-y: auto;
|
|
|
+ padding: 0 40rpx;
|
|
|
+ }
|
|
|
+
|
|
|
+ .bottom {
|
|
|
+ padding: 20rpx 40rpx;
|
|
|
+ position: absolute;
|
|
|
+ bottom: 0;
|
|
|
+ width: 100%;
|
|
|
+ box-shadow: 0rpx -4rpx 10rpx 0rpx rgba(195, 195, 195, 0.3);
|
|
|
+ background: #fff;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .shoppop {
|
|
|
+ padding: 22rpx 16rpx;
|
|
|
+
|
|
|
+ .shoppop-top {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ padding: 0 16rpx 22rpx;
|
|
|
+
|
|
|
+ .search-input {
|
|
|
+ width: 414rpx;
|
|
|
+ height: 76rpx;
|
|
|
+ background: #FFFFFF;
|
|
|
+ border-radius: 36rpx;
|
|
|
+ margin-left: 20rpx;
|
|
|
+ padding: 0 32rpx;
|
|
|
+ box-sizing: border-box;
|
|
|
+ font-size: 24rpx;
|
|
|
+ margin-right: 24rpx;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ .search-top {
|
|
|
+ font-size: 18rpx;
|
|
|
+ color: #222222;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ .shop-list {
|
|
|
+ overflow: hidden;
|
|
|
+
|
|
|
+ .list-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ padding: 20rpx 16rpx;
|
|
|
+ background: #FFFFFF;
|
|
|
+ border-radius: 16rpx;
|
|
|
+ margin-bottom: 16rpx;
|
|
|
+
|
|
|
+ .goods-img {
|
|
|
+ width: 200rpx;
|
|
|
+ height: 200rpx;
|
|
|
+ border-radius: 16rpx;
|
|
|
+ overflow: hidden;
|
|
|
+ position: relative;
|
|
|
+ margin-right: 24rpx;
|
|
|
+
|
|
|
+ image {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ }
|
|
|
+
|
|
|
+ .goods-label {
|
|
|
+ position: absolute;
|
|
|
+ top: 0;
|
|
|
+ width: 64rpx;
|
|
|
+ height: 40rpx;
|
|
|
+ background: rgba(0, 0, 0, 0.5);
|
|
|
+ border-radius: 16rpx 0rpx 16rpx 0rpx;
|
|
|
+ text-align: center;
|
|
|
+ font-weight: 500;
|
|
|
+ font-size: 28rpx;
|
|
|
+ color: #FFFFFF;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .goods-right {
|
|
|
+ flex: 1;
|
|
|
+
|
|
|
+ .goods-title {
|
|
|
+ font-weight: 500;
|
|
|
+ font-size: 28rpx;
|
|
|
+ color: #000000;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ .goods-details {
|
|
|
+ font-size: 24rpx;
|
|
|
+ color: #999999;
|
|
|
+ margin: 10rpx 0 20rpx;
|
|
|
+ }
|
|
|
+
|
|
|
+ .goods-people {
|
|
|
+ font-size: 22rpx;
|
|
|
+ color: #E69A22;
|
|
|
+ height: 56rpx;
|
|
|
+ }
|
|
|
+
|
|
|
+ .goods-shop {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+
|
|
|
+ .nummber {
|
|
|
+ color: #FF5C03;
|
|
|
+ font-size: 22rpx;
|
|
|
+ font-weight: 500;
|
|
|
+ }
|
|
|
+
|
|
|
+ .btn-group {
|
|
|
+ text-align: center;
|
|
|
+ line-height: 56rpx;
|
|
|
+
|
|
|
+ .collect-btn {
|
|
|
+ width: 72rpx;
|
|
|
+ background: #F5F7FA;
|
|
|
+ border-radius: 8rpx 0rpx 0rpx 8rpx;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ .shop-btn {
|
|
|
+ width: 152rpx;
|
|
|
+ background: linear-gradient(270deg, #FF5C03 0%, #FFAC64 100%);
|
|
|
+ border-radius: 0rpx 8rpx 8rpx 0rpx;
|
|
|
+ font-weight: 500;
|
|
|
+ font-size: 26rpx;
|
|
|
+ color: #FFFFFF;
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep(.u-list-item) {
|
|
|
+ width: 100%;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep(.u-safe-area-inset-bottom) {
|
|
|
+ padding-bottom: 0
|
|
|
+ }
|
|
|
+</style>
|