|
|
@@ -112,13 +112,13 @@
|
|
|
<!-- 主要内容区域 -->
|
|
|
<view class="content"
|
|
|
:style="{height:liveItem.status!=1&&liveItem.showType==1?'calc(100% - 150rpx)':''}"
|
|
|
- :class="{ 'horizontal-content': isFocus==1, 'trailer-content': liveItem.status==1, 'fullscreen-mode': isFullscreen }">
|
|
|
+ :class="{ 'horizontal-content': isFocus==1, 'trailer-content': liveItem.status==1, 'fullscreen-mode': isFullscreen && liveItem.showType != 1 }">
|
|
|
<!-- 顶部信息栏(无视频时降级为普通 view,如直播结束) -->
|
|
|
<!-- 视频区域 -->
|
|
|
- <view class="videolist" v-if="liveItem.status == 2" :key="'live-videolist-' + livePlayerMountKey" :class="isFullscreen ? 'screen' : ''">
|
|
|
+ <view class="videolist" v-if="liveItem.status == 2" :key="'live-videolist-' + livePlayerMountKey" :class="isFullscreen && liveItem.showType != 1 ? 'screen' : ''">
|
|
|
<view class="video-area-wrap video-container"
|
|
|
- :style="{ height:liveItem.showType==2?'100vh':isFullscreen?'auto':'400rpx',width:isFullscreen?'0':'100%'}"
|
|
|
- :class="{'horizontal-layout': liveItem.showType == 1,'fullscreen-mode': isFullscreen}"
|
|
|
+ :style="liveVideoAreaStyle"
|
|
|
+ :class="{'horizontal-layout': liveItem.showType == 1,'fullscreen-mode': isFullscreen && liveItem.showType != 1}"
|
|
|
:key="'live-player-' + livePlayerMountKey"
|
|
|
:liveVideoConfig="liveVideoConfig"
|
|
|
:change:liveVideoConfig="xgplayer.initJs"
|
|
|
@@ -133,38 +133,37 @@
|
|
|
:class="'xg-live-player-el video-player xg-hide-play-btn' + (liveItem.showType === 2 ? ' xg-hide-player-controls' : '')"
|
|
|
:style="{transform:liveItem.showType!==2?`scale(${scale})`:'', transformOrigin: 'center center', bottom:isFullscreen?'0':''}">
|
|
|
</view>
|
|
|
- <template v-if="liveItem.videoUrl && liveItem.liveType == 2 && !generating">
|
|
|
- <!-- 顶部信息栏:叠加在西瓜播放器之上 -->
|
|
|
- <view v-if="!isFullscreen && liveItem.showType == 2" class="cover-top-info-bar"
|
|
|
- :class="liveItem.showType == 1 ? 'cover-top-info-bar--light' : 'cover-top-info-bar--dark'"
|
|
|
- :style="coverTopBarStyle">
|
|
|
- <view class="cover-user-info-section">
|
|
|
- <image v-if="!scene && liveItem.showType==2" class="cover-back-icon"
|
|
|
- src="https://bjzmky-1323137866.cos.ap-chongqing.myqcloud.com/userapp/images/return3.png"
|
|
|
- @tap="goBack" />
|
|
|
- <image v-if="!scene && liveItem.showType==1" class="cover-back-icon-sm"
|
|
|
- src="/static/images/live/return_black.png" @tap="goBack" />
|
|
|
- <view class="cover-user-avatar-container">
|
|
|
- <view class="cover-avatar-round" style="width:32px;height: 32px;border-radius: 50px;overflow: hidden;">
|
|
|
- <image class="cover-avatar-round__img" :src="liveAvatarUrl" style="width:32px;height: 32px;display:block;"></image>
|
|
|
- </view>
|
|
|
- <view class="cover-user-name">{{ liveItem.liveName ? truncateString(liveItem.liveName, 8) : '未命名' }}</view>
|
|
|
+ <!-- 竖屏顶部信息栏:直播/回放生成中均使用 cover-top-info-bar -->
|
|
|
+ <view
|
|
|
+ v-if="!isFullscreen && liveItem.showType == 2 && liveItem.videoUrl && liveItem.liveType == 2"
|
|
|
+ class="cover-top-info-bar cover-top-info-bar--dark"
|
|
|
+ :style="coverTopBarStyle">
|
|
|
+ <view class="cover-user-info-section">
|
|
|
+ <image v-if="!scene && liveItem.showType==2" class="cover-back-icon"
|
|
|
+ src="https://bjzmky-1323137866.cos.ap-chongqing.myqcloud.com/userapp/images/return3.png"
|
|
|
+ @tap="goBack" />
|
|
|
+ <view class="cover-user-avatar-container">
|
|
|
+ <view class="cover-avatar-round" style="width:32px;height: 32px;border-radius: 50px;overflow: hidden;">
|
|
|
+ <image class="cover-avatar-round__img" :src="liveAvatarUrl" style="width:32px;height: 32px;display:block;"></image>
|
|
|
</view>
|
|
|
+ <view class="cover-user-name">{{ liveItem.liveName ? truncateString(liveItem.liveName, 8) : '未命名' }}</view>
|
|
|
</view>
|
|
|
- <view v-if="Array.isArray(filteredViewers)" class="cover-viewers-section">
|
|
|
- <view class="cover-viewers-inner">
|
|
|
- <view v-for="(item, index) in (filteredViewers || [])"
|
|
|
- v-if="item" :key="'cv-' + index" class="cover-avatar-round cover-avatar-round--sm"
|
|
|
- :style="coverAvatarClipStyleSm">
|
|
|
- <image class="cover-avatar-round__img" :src="item" />
|
|
|
- </view>
|
|
|
- <view class="cover-viewer-count">{{ formattedWatchCount || 0 }}</view>
|
|
|
+ </view>
|
|
|
+ <view v-if="Array.isArray(filteredViewers)" class="cover-viewers-section">
|
|
|
+ <view class="cover-viewers-inner">
|
|
|
+ <view v-for="(item, index) in (filteredViewers || [])"
|
|
|
+ v-if="item" :key="'cv-' + index" class="cover-avatar-round cover-avatar-round--sm"
|
|
|
+ :style="coverAvatarClipStyleSm">
|
|
|
+ <image class="cover-avatar-round__img" :src="item" />
|
|
|
</view>
|
|
|
+ <view class="cover-viewer-count">{{ formattedWatchCount || 0 }}</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
- <!-- 积分倒计时 -->
|
|
|
+ </view>
|
|
|
+ <template v-if="liveItem.videoUrl && liveItem.liveType == 2 && !generating">
|
|
|
+ <!-- 积分倒计时(竖屏/全屏叠在视频上;横屏小屏在 slide-group 上方) -->
|
|
|
<view
|
|
|
- v-if="!isFocus && countdownPercentage!=100&&liveItem.completionPointsEnabled&&!receiveStatus&&!hasReceived&&!isPreview"
|
|
|
+ v-if="!isFocus && !shouldPausePointsCountdown && countdownPercentage!=100&&liveItem.completionPointsEnabled&&!receiveStatus&&!hasReceived&&!isPreview && (liveItem.showType==2 || isFullscreen)"
|
|
|
class="cover-progress-countdown"
|
|
|
:class="liveItem.showType==2 ? 'cover-progress-countdown--vertical' : ''"
|
|
|
:style="coverProgressCountdownStyle">
|
|
|
@@ -299,7 +298,8 @@
|
|
|
</LiveCommentInput>
|
|
|
<view
|
|
|
v-if="useVerticalVideoCover && !isFocus && liveActionButtonsVisible && !commentInputVisible"
|
|
|
- class="cover-input-actions-container cover-input-actions-container--actions-only">
|
|
|
+ class="cover-input-actions-container cover-input-actions-container--actions-only"
|
|
|
+ :style="coverInputActionsContainerStyle">
|
|
|
<LiveActionButtons
|
|
|
size="large"
|
|
|
:show-type="liveItem.showType"
|
|
|
@@ -311,7 +311,8 @@
|
|
|
<view class="txt">回放生成中...</view>
|
|
|
<view
|
|
|
v-if="!isFocus && liveActionButtonsVisible"
|
|
|
- class="cover-input-actions-container cover-input-actions-container--actions-only">
|
|
|
+ class="cover-input-actions-container cover-input-actions-container--actions-only"
|
|
|
+ :style="coverInputActionsContainerStyle">
|
|
|
<LiveActionButtons
|
|
|
size="large"
|
|
|
:show-type="liveItem.showType"
|
|
|
@@ -393,7 +394,8 @@
|
|
|
<view class="cover-replay-label cover-replay-label--status4">直播回放</view>
|
|
|
<view
|
|
|
v-if="useVerticalVideoCover && !isFocus && liveActionButtonsVisible"
|
|
|
- class="cover-input-actions-container cover-input-actions-container--actions-only">
|
|
|
+ class="cover-input-actions-container cover-input-actions-container--actions-only"
|
|
|
+ :style="coverInputActionsContainerStyle">
|
|
|
<LiveActionButtons
|
|
|
size="large"
|
|
|
:show-type="liveItem.showType"
|
|
|
@@ -406,6 +408,18 @@
|
|
|
<!-- v-if="!hasReachedTarget" -->
|
|
|
<!-- &&liveItem.completionPointsEnabled -->
|
|
|
|
|
|
+ <!-- 横屏小屏完课倒计时:与点赞同列,位于点赞上方 -->
|
|
|
+ <view
|
|
|
+ v-if="!isFocus && !shouldPausePointsCountdown && countdownPercentage!=100&&liveItem.completionPointsEnabled&&!receiveStatus&&!hasReceived&&!isPreview && liveItem.showType==1 && !isFullscreen && !useVerticalVideoCover"
|
|
|
+ class="cover-progress-countdown cover-progress-countdown--horizontal"
|
|
|
+ :style="coverProgressCountdownStyle">
|
|
|
+ <image class="cover-progress-title" src="/static/images/live/points_title.png" />
|
|
|
+ <view class="cover-progress-bar-bg">
|
|
|
+ <view class="cover-progress-bar-fill" :style="{ width: countdownPercentage + '%' }"></view>
|
|
|
+ </view>
|
|
|
+ <view class="cover-progress-text">倒计时{{ formattedCountdown.hours||'00' }}:{{ formattedCountdown.minutes||'00' }}:{{ formattedCountdown.seconds||"00" }}</view>
|
|
|
+ </view>
|
|
|
+
|
|
|
<view
|
|
|
class="slide-group"
|
|
|
:class="{
|
|
|
@@ -459,7 +473,7 @@
|
|
|
</view>
|
|
|
</view>
|
|
|
<view class="chat-content-wrapper"
|
|
|
- :style="{ 'height':liveItem.status == 1 && !liveItem.previewUrl && liveItem.showType == 2?'48vh':''}"
|
|
|
+
|
|
|
:class="{ 'chat-content-focused': isFocus }">
|
|
|
<view class="notice-message" v-if="isShowNotice"
|
|
|
:style="{ 'display':isFocus&&liveItem.showType == 2?'none':''}"
|
|
|
@@ -926,9 +940,7 @@
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
- import {
|
|
|
- CustomToast
|
|
|
- } from '@/components/custom-toast.vue';
|
|
|
+ import CustomToast from '@/components/custom-toast.vue';
|
|
|
import LikeButton from './components/like.vue';
|
|
|
import LiveGoods from './components/liveGoods.vue';
|
|
|
import LiveActionButtons from './components/liveActionButtons.vue';
|
|
|
@@ -1040,6 +1052,8 @@ import {
|
|
|
isHeight: false,
|
|
|
isFullscreen: false,
|
|
|
isVideoRotated: false,
|
|
|
+ fullscreenToggleLock: false,
|
|
|
+ fullscreenToggleTimer: null,
|
|
|
showNonVideoElementsFlag: true,
|
|
|
videoRatio: 56.25, // 默认16:9比例
|
|
|
scale: 1,
|
|
|
@@ -1488,6 +1502,20 @@ import {
|
|
|
shouldShowFullscreenButton() {
|
|
|
return this.liveItem.showType == 1 && !this.isFullscreen;
|
|
|
},
|
|
|
+ /** 横屏小屏走播放器原生全屏,页面容器保持小屏尺寸,避免小米等机型闪动后回弹 */
|
|
|
+ liveVideoAreaStyle() {
|
|
|
+ const showType = this.liveItem && this.liveItem.showType;
|
|
|
+ if (showType === 2) {
|
|
|
+ return { height: '100vh', width: '100%' };
|
|
|
+ }
|
|
|
+ if (this.isFullscreen && showType === 1) {
|
|
|
+ return { height: '400rpx', width: '100%' };
|
|
|
+ }
|
|
|
+ if (this.isFullscreen) {
|
|
|
+ return { height: 'auto', width: '0' };
|
|
|
+ }
|
|
|
+ return { height: '400rpx', width: '100%' };
|
|
|
+ },
|
|
|
...mapGetters(['coureLogin']),
|
|
|
appid() {
|
|
|
return this.$store.state.appid
|
|
|
@@ -1502,11 +1530,19 @@ import {
|
|
|
!this.isReplayPlayback() &&
|
|
|
!this.generating;
|
|
|
},
|
|
|
+ // 直播结束/回放生成中:暂停完课积分倒计时
|
|
|
+ shouldPausePointsCountdown() {
|
|
|
+ const item = this.liveItem;
|
|
|
+ if (!item) return true;
|
|
|
+ if (item.status === 3) return true;
|
|
|
+ if (this.generating) return true;
|
|
|
+ if (this.hasLiveEnd && item.status === 2 && !this.isReplayPlayback()) return true;
|
|
|
+ return false;
|
|
|
+ },
|
|
|
liveActionButtonsVisible() {
|
|
|
- if (!this.liveItem || this.liveItem.status == 3) return false;
|
|
|
- // 竖屏回放生成中也需要显示更多和小黄车
|
|
|
- if (this.generating && this.liveItem.showType === 2) return true;
|
|
|
- return !this.generating;
|
|
|
+ if (!this.liveItem) return false;
|
|
|
+ // 回放生成中也需展示更多和小黄车
|
|
|
+ return true;
|
|
|
},
|
|
|
coverTopBarStyle() {
|
|
|
const top = (this.menuButtonInfo && this.menuButtonInfo.top) ? this.menuButtonInfo.top : '0px';
|
|
|
@@ -1539,6 +1575,19 @@ import {
|
|
|
if (this.liveItem && this.liveItem.showType === 2) {
|
|
|
return { top: '15%', left: '24rpx', right: 'auto' };
|
|
|
}
|
|
|
+ if (this.isFullscreen) {
|
|
|
+ return { top: '20rpx', right: '20rpx' };
|
|
|
+ }
|
|
|
+ // 横屏小屏:与 slide-group--horizontal 对齐,位于点赞上方
|
|
|
+ if (this.liveItem && this.liveItem.showType === 1) {
|
|
|
+ const likeBottom = this.isShowGoods ? '480rpx' : '380rpx';
|
|
|
+ return {
|
|
|
+ right: '20rpx',
|
|
|
+ left: 'auto',
|
|
|
+ top: 'auto',
|
|
|
+ bottom: `calc(${likeBottom} + 144rpx)`,
|
|
|
+ };
|
|
|
+ }
|
|
|
return { top: '45%', right: '20rpx' };
|
|
|
},
|
|
|
coverComplaintStyle() {
|
|
|
@@ -1571,13 +1620,18 @@ import {
|
|
|
}
|
|
|
return false;
|
|
|
},
|
|
|
- // 无 video 节点时顶部栏降级为普通 view(如直播结束、回放生成中)
|
|
|
+ // 无 video 节点时顶部栏降级为普通 view(如直播结束、横屏回放生成中)
|
|
|
showTopInfoBarOutside() {
|
|
|
if (this.isFullscreen || !this.liveItem) return false;
|
|
|
if (this.liveItem.status == 1) return false;
|
|
|
if (this.showTopInfoBarAboveVideo) return true;
|
|
|
if (this.liveItem.status == 3) return true;
|
|
|
if (this.liveItem.status == 2) {
|
|
|
+ // 竖屏回放生成中已使用 cover-top-info-bar,不再展示外部降级栏
|
|
|
+ if (this.generating && this.liveItem.showType === 2 &&
|
|
|
+ this.liveItem.videoUrl && this.liveItem.liveType === 2) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
return !this.liveItem.videoUrl || this.generating || this.liveItem.liveType != 2;
|
|
|
}
|
|
|
if (this.liveItem.status == 4) {
|
|
|
@@ -1599,19 +1653,23 @@ import {
|
|
|
const item = this.liveItem || {};
|
|
|
let url = '';
|
|
|
let mode = '';
|
|
|
+ const normalizeUrl = (rawUrl) => {
|
|
|
+ if (rawUrl == null || rawUrl === '') return '';
|
|
|
+ return String(rawUrl).trim();
|
|
|
+ };
|
|
|
if (item.status === 1 && item.previewUrl) {
|
|
|
- url = item.previewUrl;
|
|
|
+ url = normalizeUrl(item.previewUrl);
|
|
|
mode = 'preview';
|
|
|
} else if (item.status === 2 && item.liveType === 2 && item.videoUrl && !this.generating) {
|
|
|
- url = item.originalVideoUrl || item.videoUrl;
|
|
|
- if (this.videoReloadKey > 0 && url) {
|
|
|
- const base = url.split(/[?&]t=/)[0];
|
|
|
- const sep = base.includes('?') ? '&' : '?';
|
|
|
- url = base + sep + 't=' + this.videoReloadKey;
|
|
|
- }
|
|
|
+ url = normalizeUrl(item.originalVideoUrl || item.videoUrl);
|
|
|
+ // if (this.videoReloadKey > 0 && url) {
|
|
|
+ // const base = url.split(/[?&]t=/)[0];
|
|
|
+ // const sep = base.includes('?') ? '&' : '?';
|
|
|
+ // url = base + sep + 't=' + this.videoReloadKey;
|
|
|
+ // }
|
|
|
mode = 'live';
|
|
|
} else if (item.status === 4 && item.liveType === 3 && item.videoUrl) {
|
|
|
- url = item.videoUrl;
|
|
|
+ url = normalizeUrl(item.videoUrl);
|
|
|
mode = 'replay';
|
|
|
}
|
|
|
const showType = item.showType;
|
|
|
@@ -1670,6 +1728,31 @@ import {
|
|
|
backgroundColor: 'rgba(0,0,0,0.01)'
|
|
|
};
|
|
|
},
|
|
|
+ // 竖屏底部操作栏(仅按钮 / 输入+按钮),动态控制尺寸避免 height:100% 撑满视频区
|
|
|
+ coverInputActionsContainerStyle() {
|
|
|
+ const item = this.liveItem;
|
|
|
+ if (!item || item.showType !== 2 || this.isFullscreen) return {};
|
|
|
+ const isVerticalOverlay = this.useVerticalVideoCover ||
|
|
|
+ (this.generating && item.videoUrl);
|
|
|
+ if (!isVerticalOverlay) return {};
|
|
|
+ const m = this.getCoverChatMetrics();
|
|
|
+ const pad = this.getCoverPx(24);
|
|
|
+ const style = {
|
|
|
+ left: '0',
|
|
|
+ right: '0',
|
|
|
+ bottom: m.safeBottom + 'px',
|
|
|
+ width: '100%',
|
|
|
+ height: 'auto',
|
|
|
+ padding: `${pad}px`,
|
|
|
+ boxSizing: 'border-box',
|
|
|
+ };
|
|
|
+ // 回放生成中无播放器,fixed 定位避免受 video 容器 100vh 影响
|
|
|
+ if (this.generating) {
|
|
|
+ style.position = 'fixed';
|
|
|
+ style.zIndex = 99999;
|
|
|
+ }
|
|
|
+ return style;
|
|
|
+ },
|
|
|
// 横屏/竖屏小窗聊天区 scroll-view 高度(随公告显隐调整)
|
|
|
chatMessageScrollStyle() {
|
|
|
const noticeH = Number(this.commonViewHeight) || 0;
|
|
|
@@ -1852,6 +1935,7 @@ import {
|
|
|
}
|
|
|
// 清除所有定时器(使用增强清理)
|
|
|
this.clearAllTimersEnhanced();
|
|
|
+ this._clearFullscreenToggleLock();
|
|
|
|
|
|
this.exitFullscreenSignal = 'yes' + new Date().getTime();
|
|
|
this.pauseVideo();
|
|
|
@@ -1963,6 +2047,18 @@ import {
|
|
|
});
|
|
|
}
|
|
|
},
|
|
|
+ generating(val, oldVal) {
|
|
|
+ if (val) {
|
|
|
+ this.stopCountdown();
|
|
|
+ } else if (oldVal && this.liveItem?.completionPointsEnabled && !this.shouldPausePointsCountdown) {
|
|
|
+ this.startCountdown();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ hasLiveEnd(val) {
|
|
|
+ if (val && this.shouldPausePointsCountdown) {
|
|
|
+ this.stopCountdown();
|
|
|
+ }
|
|
|
+ },
|
|
|
},
|
|
|
methods: {
|
|
|
// 查询当前用户当前直播间领取积分的剩余时长
|
|
|
@@ -2191,6 +2287,9 @@ import {
|
|
|
if (!this.liveItem.completionPointsEnabled) {
|
|
|
return;
|
|
|
}
|
|
|
+ if (this.shouldPausePointsCountdown) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
// 如果倒计时已经在运行,先清除旧的定时器
|
|
|
if (this.countdownTimer) {
|
|
|
clearInterval(this.countdownTimer);
|
|
|
@@ -2218,6 +2317,13 @@ import {
|
|
|
if (!this.liveItem) {
|
|
|
return;
|
|
|
}
|
|
|
+ if (this.shouldPausePointsCountdown) {
|
|
|
+ if (this.countdownTimer) {
|
|
|
+ clearInterval(this.countdownTimer);
|
|
|
+ this.countdownTimer = null;
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
//console.log("this.completionPointsEnabled",this.completionTime)
|
|
|
this.completionTime = this.liveItem.duration * this.completionRate;
|
|
|
//console.log("qxj watchDuration:"+this.watchDuration+" completionTime:"+this.completionTime+" pointsRemainingTime:"+this.pointsRemainingTime);
|
|
|
@@ -2597,9 +2703,31 @@ import {
|
|
|
onXgVideoError(e) {
|
|
|
this.videoError(e || {}, this.liveItem);
|
|
|
},
|
|
|
+ _armFullscreenToggleLock(ms = 1000) {
|
|
|
+ this.fullscreenToggleLock = true;
|
|
|
+ if (this.fullscreenToggleTimer) {
|
|
|
+ clearTimeout(this.fullscreenToggleTimer);
|
|
|
+ }
|
|
|
+ this.fullscreenToggleTimer = setTimeout(() => {
|
|
|
+ this.fullscreenToggleLock = false;
|
|
|
+ this.fullscreenToggleTimer = null;
|
|
|
+ }, ms);
|
|
|
+ },
|
|
|
+ armFullscreenToggleLockFromRender(e) {
|
|
|
+ const ms = e && e.ms != null ? Number(e.ms) : 1000;
|
|
|
+ this._armFullscreenToggleLock(ms);
|
|
|
+ },
|
|
|
+ _clearFullscreenToggleLock() {
|
|
|
+ if (this.fullscreenToggleTimer) {
|
|
|
+ clearTimeout(this.fullscreenToggleTimer);
|
|
|
+ this.fullscreenToggleTimer = null;
|
|
|
+ }
|
|
|
+ this.fullscreenToggleLock = false;
|
|
|
+ },
|
|
|
// 退出全屏
|
|
|
exitFullscreen() {
|
|
|
console.log('执行退出全屏');
|
|
|
+ this._clearFullscreenToggleLock();
|
|
|
this.exitFullscreenSignal = 'yes' + new Date().getTime();
|
|
|
this.isFullscreen = false;
|
|
|
this.restoreVideoList();
|
|
|
@@ -2701,6 +2829,8 @@ import {
|
|
|
const isLandscape = windowWidth > windowHeight;
|
|
|
if (this.isFullscreen) {
|
|
|
if (!isLandscape) {
|
|
|
+ // 小米等机型进入原生全屏时,旋转过渡期 window 尺寸会短暂仍为竖屏,勿立即退出
|
|
|
+ if (this.fullscreenToggleLock) return;
|
|
|
this.exitFullscreenSignal = 'yes' + new Date().getTime();
|
|
|
this.exitFullscreen();
|
|
|
} else {
|
|
|
@@ -2726,6 +2856,7 @@ import {
|
|
|
if (this.isFullscreen) {
|
|
|
this.exitFullscreen();
|
|
|
} else {
|
|
|
+ this._armFullscreenToggleLock(1000);
|
|
|
this._lockAppLandscape();
|
|
|
this.fullscreenRequest = 'yes' + new Date().getTime();
|
|
|
this.enterFullscreen();
|
|
|
@@ -2769,15 +2900,20 @@ import {
|
|
|
} else if (e && e.detail && typeof e.detail.fullscreen !== 'undefined') {
|
|
|
fullScreen = !!e.detail.fullscreen;
|
|
|
}
|
|
|
+ if (!fullScreen && this.fullscreenToggleLock) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
this.isFullscreen = fullScreen;
|
|
|
this.showCustomControls = !this.isFullscreen;
|
|
|
this.$forceUpdate();
|
|
|
|
|
|
if (this.isFullscreen) {
|
|
|
+ this._armFullscreenToggleLock(1000);
|
|
|
this._lockAppLandscape();
|
|
|
this.hideNonVideoElements();
|
|
|
} else {
|
|
|
+ this._armFullscreenToggleLock(400);
|
|
|
this._lockAppPortrait();
|
|
|
this.showNonVideoElements();
|
|
|
}
|
|
|
@@ -4411,6 +4547,7 @@ import {
|
|
|
this.isPreview = false;
|
|
|
this.isEnd = true;
|
|
|
this.generating = false;
|
|
|
+ this.stopCountdown();
|
|
|
if (this.liveStartTimer) {
|
|
|
clearInterval(this.liveStartTimer);
|
|
|
this.liveStartTimer = null;
|
|
|
@@ -5580,14 +5717,10 @@ import {
|
|
|
num: 1
|
|
|
});
|
|
|
if (res.code !== 200) {
|
|
|
- this.$refs.customToast.show({
|
|
|
- title: res.msg || '该商品限购,目前剩余可购买数量不足',
|
|
|
- duration: 2000
|
|
|
- });
|
|
|
- // uni.showToast({
|
|
|
- // icon: 'none',
|
|
|
- // title: res.msg || '该商品限购,目前剩余可购买数量不足'
|
|
|
- // });
|
|
|
+ this.$refs.customToast.show({
|
|
|
+ title: res.msg || '该商品限购,目前剩余可购买数量不足',
|
|
|
+ duration: 2000
|
|
|
+ });
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
@@ -6264,6 +6397,7 @@ import {
|
|
|
}
|
|
|
if (socketMessage.cmd == 'live_end') {
|
|
|
this.hasLiveEnd = true;
|
|
|
+ this.stopCountdown();
|
|
|
}
|
|
|
// 请求最新直播间数据
|
|
|
this.$nextTick(() => {
|
|
|
@@ -6482,6 +6616,7 @@ import {
|
|
|
<script module="xgplayer" lang="renderjs">
|
|
|
const XG_SCRIPT = 'https://unpkg.byted-static.com/xgplayer/3.0.20/dist/index.min.js';
|
|
|
const XG_CSS = 'https://unpkg.byted-static.com/xgplayer/3.0.20/dist/index.min.css';
|
|
|
+ const XG_HLS_SCRIPT = 'https://unpkg.byted-static.com/xgplayer-hls/3.0.20/dist/index.min.js';
|
|
|
|
|
|
export default {
|
|
|
data() {
|
|
|
@@ -6531,6 +6666,85 @@ import {
|
|
|
}
|
|
|
return document.querySelector('.video-container') || document.body;
|
|
|
},
|
|
|
+ _isHlsUrl(url) {
|
|
|
+ if (!url || typeof url !== 'string') return false;
|
|
|
+ const path = url.split('?')[0].split('#')[0].toLowerCase();
|
|
|
+ return path.endsWith('.m3u8') || path.endsWith('.m3u') ||
|
|
|
+ url.toLowerCase().indexOf('mpegurl') > -1;
|
|
|
+ },
|
|
|
+ _canNativeHls() {
|
|
|
+ try {
|
|
|
+ const video = document.createElement('video');
|
|
|
+ return !!video.canPlayType('application/vnd.apple.mpegurl') ||
|
|
|
+ !!video.canPlayType('application/x-mpegURL');
|
|
|
+ } catch (e) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ _needsHlsPlugin(url) {
|
|
|
+ return this._isHlsUrl(url) && !this._canNativeHls();
|
|
|
+ },
|
|
|
+ _ensureHlsPlugin(callback) {
|
|
|
+ if (window.HlsPlugin) {
|
|
|
+ callback();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const existing = document.querySelector('script[data-xg-hls-js]');
|
|
|
+ if (existing) {
|
|
|
+ const waitForPlugin = (attempt = 0) => {
|
|
|
+ if (window.HlsPlugin || attempt >= 50) {
|
|
|
+ callback();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ setTimeout(() => waitForPlugin(attempt + 1), 100);
|
|
|
+ };
|
|
|
+ waitForPlugin();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const script = document.createElement('script');
|
|
|
+ script.src = XG_HLS_SCRIPT;
|
|
|
+ script.setAttribute('data-xg-hls-js', '1');
|
|
|
+ script.onload = callback;
|
|
|
+ script.onerror = callback;
|
|
|
+ document.head.appendChild(script);
|
|
|
+ },
|
|
|
+ _ensurePlayerAssets(url, callback) {
|
|
|
+ const runAfterPlayer = () => {
|
|
|
+ if (this._needsHlsPlugin(url)) {
|
|
|
+ this._ensureHlsPlugin(callback);
|
|
|
+ } else {
|
|
|
+ callback();
|
|
|
+ }
|
|
|
+ };
|
|
|
+ if (typeof window.Player === 'function') {
|
|
|
+ runAfterPlayer();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const script = document.createElement('script');
|
|
|
+ script.src = XG_SCRIPT;
|
|
|
+ script.onload = runAfterPlayer;
|
|
|
+ script.onerror = runAfterPlayer;
|
|
|
+ document.head.appendChild(script);
|
|
|
+ if (!document.querySelector('link[data-xg-live-css]')) {
|
|
|
+ const link = document.createElement('link');
|
|
|
+ link.rel = 'stylesheet';
|
|
|
+ link.href = XG_CSS;
|
|
|
+ link.setAttribute('data-xg-live-css', '1');
|
|
|
+ document.body.appendChild(link);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ _buildPlayerPlugins(url) {
|
|
|
+ const plugins = [];
|
|
|
+ if (!this._needsHlsPlugin(url) || !window.HlsPlugin) {
|
|
|
+ return plugins;
|
|
|
+ }
|
|
|
+ if (typeof window.HlsPlugin.isSupported === 'function' &&
|
|
|
+ !window.HlsPlugin.isSupported()) {
|
|
|
+ return plugins;
|
|
|
+ }
|
|
|
+ plugins.push(window.HlsPlugin);
|
|
|
+ return plugins;
|
|
|
+ },
|
|
|
_ensureXgVideoStyles() {
|
|
|
if (document.querySelector('style[data-xg-live-video-fix]')) return;
|
|
|
const style = document.createElement('style');
|
|
|
@@ -6590,27 +6804,12 @@ import {
|
|
|
}
|
|
|
return;
|
|
|
}
|
|
|
- const bootstrap = () => {
|
|
|
+ this._ensurePlayerAssets(newValue.url, () => {
|
|
|
this._ensureXgVideoStyles();
|
|
|
this._waitForPlayerDom(newValue.playerId || 'live-detail-video', () => {
|
|
|
this.initPlayer(newValue, oldValue, ownerInstance);
|
|
|
});
|
|
|
- };
|
|
|
- if (typeof window.Player === 'function') {
|
|
|
- bootstrap();
|
|
|
- return;
|
|
|
- }
|
|
|
- const script = document.createElement('script');
|
|
|
- script.src = XG_SCRIPT;
|
|
|
- script.onload = bootstrap;
|
|
|
- document.head.appendChild(script);
|
|
|
- if (!document.querySelector('link[data-xg-live-css]')) {
|
|
|
- const link = document.createElement('link');
|
|
|
- link.rel = 'stylesheet';
|
|
|
- link.href = XG_CSS;
|
|
|
- link.setAttribute('data-xg-live-css', '1');
|
|
|
- document.body.appendChild(link);
|
|
|
- }
|
|
|
+ });
|
|
|
},
|
|
|
_waitForPlayerDom(playerId, callback, attempt = 0) {
|
|
|
const el = document.getElementById(playerId);
|
|
|
@@ -6684,12 +6883,13 @@ import {
|
|
|
|
|
|
const playerId = newValue.playerId || 'live-detail-video';
|
|
|
const fit = newValue.objectFit === 'contain' ? 'contain' : 'fill';
|
|
|
- const isLiveMode = newValue.mode === 'live' || newValue.mode === 'preview';
|
|
|
+ // 预告为点播 m3u8/mp4,仅录播进行中按直播 UI 隐藏进度条
|
|
|
+ const isLiveMode = newValue.mode === 'live';
|
|
|
const hideControls = !!newValue.hideControls;
|
|
|
const hidePlayBtn = newValue.mode === 'live';
|
|
|
const allowFullscreen = newValue.showType === 1 && (newValue.mode === 'live' || newValue.mode === 'replay');
|
|
|
const ignores = ['volume', 'miniscreen', 'keyboard', 'playbackrate'];
|
|
|
- if (isLiveMode) {
|
|
|
+ if (isLiveMode || newValue.mode === 'preview') {
|
|
|
ignores.push('progress', 'time');
|
|
|
}
|
|
|
if (hidePlayBtn) {
|
|
|
@@ -6709,10 +6909,12 @@ import {
|
|
|
oldValue.mountKey !== newValue.mountKey ||
|
|
|
oldValue.liveStatus !== newValue.liveStatus
|
|
|
);
|
|
|
- if (needRecreate) {
|
|
|
+ const needsHlsPlugin = this._needsHlsPlugin(newValue.url);
|
|
|
+ if (needRecreate || needsHlsPlugin) {
|
|
|
this._destroyPlayer();
|
|
|
}
|
|
|
const fsTarget = this._getFullscreenTarget(playerId);
|
|
|
+ const plugins = this._buildPlayerPlugins(newValue.url);
|
|
|
const option = {
|
|
|
lang: 'zh',
|
|
|
url: newValue.url,
|
|
|
@@ -6745,6 +6947,10 @@ import {
|
|
|
disable: true
|
|
|
}
|
|
|
};
|
|
|
+ if (plugins.length) {
|
|
|
+ option.plugins = plugins;
|
|
|
+ option.isLive = false;
|
|
|
+ }
|
|
|
|
|
|
if (this.player) {
|
|
|
this.player.loop = !!newValue.loop;
|
|
|
@@ -6818,6 +7024,9 @@ import {
|
|
|
});
|
|
|
this.player.on(Events.FULLSCREEN_CHANGE, (isFullscreen) => {
|
|
|
if (this.isFullscreen == isFullscreen) return;
|
|
|
+ if (isFullscreen) {
|
|
|
+ this.ownerInstance.callMethod('armFullscreenToggleLockFromRender', { ms: 1000 });
|
|
|
+ }
|
|
|
this.isFullscreen = isFullscreen;
|
|
|
// #ifdef APP-PLUS
|
|
|
try {
|
|
|
@@ -6834,6 +7043,9 @@ import {
|
|
|
});
|
|
|
this.player.on(Events.CSS_FULLSCREEN_CHANGE, (isFullscreen) => {
|
|
|
if (this.isFullscreen == isFullscreen) return;
|
|
|
+ if (isFullscreen) {
|
|
|
+ this.ownerInstance.callMethod('armFullscreenToggleLockFromRender', { ms: 1000 });
|
|
|
+ }
|
|
|
this.isCSSFull = true;
|
|
|
this.isFullscreen = isFullscreen;
|
|
|
// #ifdef APP-PLUS
|
|
|
@@ -6862,6 +7074,7 @@ import {
|
|
|
requestFullscreen(newValue, oldValue, ownerInstance) {
|
|
|
if (!this.ownerInstance) this.ownerInstance = ownerInstance;
|
|
|
if (this.player && newValue && newValue.indexOf('yes') > -1) {
|
|
|
+ this.ownerInstance.callMethod('armFullscreenToggleLockFromRender', { ms: 1000 });
|
|
|
// #ifdef APP-PLUS
|
|
|
try {
|
|
|
plus.screen.lockOrientation('landscape-primary');
|
|
|
@@ -7143,6 +7356,10 @@ import {
|
|
|
transform: rotate(90deg) !important;
|
|
|
transform-origin: center center !important;
|
|
|
|
|
|
+ &.horizontal-layout {
|
|
|
+ transform: none !important;
|
|
|
+ }
|
|
|
+
|
|
|
// .video-player {
|
|
|
// width: 100vh !important;
|
|
|
// height: 100vw !important;
|
|
|
@@ -7293,6 +7510,13 @@ import {
|
|
|
right: auto;
|
|
|
}
|
|
|
|
|
|
+ .cover-progress-countdown--horizontal {
|
|
|
+ //position: fixed;
|
|
|
+ //top: auto;
|
|
|
+ bottom: 50% !important;
|
|
|
+ z-index: 10001;
|
|
|
+ }
|
|
|
+
|
|
|
.cover-progress-title {
|
|
|
width: 148rpx;
|
|
|
height: 28rpx;
|
|
|
@@ -7601,6 +7825,7 @@ import {
|
|
|
left: 0;
|
|
|
bottom: 0;
|
|
|
width: 100%;
|
|
|
+ height: auto;
|
|
|
padding: 24rpx;
|
|
|
display: flex;
|
|
|
flex-direction: row;
|
|
|
@@ -8069,7 +8294,7 @@ import {
|
|
|
|
|
|
|
|
|
&.horizontal-content {
|
|
|
- z-index: 999;
|
|
|
+ //z-index: 999;
|
|
|
}
|
|
|
|
|
|
&.trailer-content {
|