|
@@ -1,74 +1,67 @@
|
|
|
<template>
|
|
|
- <view class=" content ">
|
|
|
+ <view class="content">
|
|
|
<mescroll-body bottom="0" ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback"
|
|
|
:down="downOption" :up="upOption">
|
|
|
<view class="list">
|
|
|
<view class="list-item" @click="goLive(item)" v-for="(item,index) in list" :key="index">
|
|
|
-
|
|
|
- <!-- <image :src="item.liveImgUrl"></image> -->
|
|
|
-
|
|
|
-
|
|
|
- <video v-if="item.flvHlsUrl && item.liveId" :id="'myVideo_' + item.liveId" :src="item.flvHlsUrl"
|
|
|
- :autoplay="false" :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="item.videoUrl" :src="item.videoUrl" :autoplay="false" :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>
|
|
|
+ <image v-if="!item._isInView && item.liveImgUrl" :src="item.liveImgUrl"></image>
|
|
|
+ <!-- 直播流 -->
|
|
|
+ <video v-if="item._isInView && item.liveType == 1 && item.flvHlsUrl" :id="'myVideo_' + item.liveId"
|
|
|
+ :src="item.flvHlsUrl" :autoplay="false" :controls="false" object-fit="contain"
|
|
|
+ :custom-cache="false" :enable-progress-gesture="false" :show-center-play-btn="false"
|
|
|
+ :http-cache="false" :muted="true" @error="onVideoError(item, $event)"></video>
|
|
|
+
|
|
|
+ <!-- 录播视频 -->
|
|
|
+ <video v-if="item._isInView && item.liveType == 2 && item.videoUrl " :id="'myVideo_' + item.liveId"
|
|
|
+ :src="item.videoUrl" :autoplay="false" :controls="false" object-fit="contain"
|
|
|
+ :custom-cache="false" :enable-progress-gesture="false" :show-center-play-btn="false"
|
|
|
+ :http-cache="false" :loop="true" :muted="true" @error="onVideoError(item, $event)"
|
|
|
+ @loadedmetadata="onVideoLoaded(item)"></video>
|
|
|
|
|
|
<view class="info">
|
|
|
+ <!-- <text v-if="item.liveType == 1" class="live-badge">直播</text>
|
|
|
+ <text v-else class="record-badge">录播</text> -->
|
|
|
<text>{{item.liveName}}</text>
|
|
|
</view>
|
|
|
- </view>
|
|
|
|
|
|
+ <!-- 添加错误提示 -->
|
|
|
+ <!-- <view v-if="item._error" class="error-tip">
|
|
|
+ 视频加载失败
|
|
|
+ </view> -->
|
|
|
+ </view>
|
|
|
</view>
|
|
|
</mescroll-body>
|
|
|
</view>
|
|
|
-
|
|
|
</template>
|
|
|
+
|
|
|
<script>
|
|
|
import Hls from 'hls.js';
|
|
|
import {
|
|
|
liveList
|
|
|
} from '@/api/list'
|
|
|
import MescrollMixin from "@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js";
|
|
|
+
|
|
|
export default {
|
|
|
- mixins: [MescrollMixin], // 使用mixin
|
|
|
+ mixins: [MescrollMixin],
|
|
|
data() {
|
|
|
return {
|
|
|
|
|
|
- hlsPlayer: null, // HLS播放器实例,
|
|
|
- // 统一直播/录播类型标识
|
|
|
- LIVE_TYPE: {
|
|
|
- LIVE: 1, // 直播
|
|
|
- RECORD: 2 // 录播
|
|
|
- },
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
list: [],
|
|
|
- liveId: null, // mescroll配置
|
|
|
downOption: {
|
|
|
offset: 80,
|
|
|
use: true,
|
|
|
- auto: false // 是否在初始化后自动执行下拉回调
|
|
|
+ auto: false
|
|
|
},
|
|
|
upOption: {
|
|
|
use: true,
|
|
|
- auto: true, // 是否在初始化时自动执行上拉回调
|
|
|
+ auto: true,
|
|
|
page: {
|
|
|
- num: 0, // 当前页码
|
|
|
- size: 10 // 每页数据条数
|
|
|
+ num: 0,
|
|
|
+ size: 10
|
|
|
}
|
|
|
-
|
|
|
},
|
|
|
- mescroll: null // mescroll实例
|
|
|
+ mescroll: null,
|
|
|
+ observer: null // 存储IntersectionObserver实例
|
|
|
}
|
|
|
},
|
|
|
onLoad() {
|
|
@@ -76,235 +69,203 @@
|
|
|
uni.navigateTo({
|
|
|
url: '/pages/auth/login'
|
|
|
});
|
|
|
-
|
|
|
- } // 销毁HLS播放器(如果使用了hls.js)
|
|
|
- if (this.hlsPlayer) {
|
|
|
- this.hlsPlayer.destroy();
|
|
|
- this.hlsPlayer = null;
|
|
|
}
|
|
|
},
|
|
|
onUnload() {
|
|
|
- this.list.forEach(item => {
|
|
|
- this.$set(item, 'isDestroyed', true); // 标记已销毁,阻止播放
|
|
|
- this.pauseAndCleanVideo(item); // 统一清理
|
|
|
- });
|
|
|
+ // 清理所有观察器和视频资源
|
|
|
+ this.cleanupAllVideos();
|
|
|
},
|
|
|
- methods: { // 处理单个视频项,适配m3u8流
|
|
|
- initVideoPlayer(item) {
|
|
|
- // 基础校验:无地址/无ID,不初始化
|
|
|
- const isLive = item.liveType === 1;
|
|
|
- const hasValidUrl = isLive ? (item.flvHlsUrl?.includes('.m3u8')) : !!item.videoUrl;
|
|
|
- if (!hasValidUrl || !item.liveId) return;
|
|
|
+ methods: {
|
|
|
+ // 视频加载错误处理
|
|
|
+ onVideoError(item, e) {
|
|
|
+ // console.error('视频加载错误:', e.detail, item);
|
|
|
+ this.$set(item, '_error', true);
|
|
|
+ this.pauseVideo(item);
|
|
|
+ },
|
|
|
+
|
|
|
+ // 视频元数据加载完成
|
|
|
+ onVideoLoaded(item) {
|
|
|
+ console.log('视频元数据加载完成:', item.liveId);
|
|
|
+ this.$set(item, '_error', false);
|
|
|
+ },
|
|
|
|
|
|
+ // 初始化所有视频观察器
|
|
|
+ initAllVideoObservers() {
|
|
|
+ // 先清理旧的
|
|
|
+ this.cleanupAllVideos();
|
|
|
+
|
|
|
+ // 使用setTimeout确保DOM已渲染
|
|
|
+ setTimeout(() => {
|
|
|
+ this.list.forEach(item => {
|
|
|
+ if (item.liveId) {
|
|
|
+ this.initVideoObserver(item);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }, 300);
|
|
|
+ },
|
|
|
+
|
|
|
+ // 初始化单个视频的观察器
|
|
|
+ initVideoObserver(item) {
|
|
|
const videoId = `myVideo_${item.liveId}`;
|
|
|
- console.log(`[视口监听] 初始化:${videoId}`);
|
|
|
|
|
|
- // 1. 创建交叉观察器(监听是否进入视口)
|
|
|
- const observer = uni.createIntersectionObserver(this, {
|
|
|
- thresholds: [0.2], // 20% 进入视口触发
|
|
|
- observeAll: false
|
|
|
- });
|
|
|
+ // 创建交叉观察器(监听视频项是否进入视口)
|
|
|
+ const observer = uni.createIntersectionObserver(this);
|
|
|
+
|
|
|
+ observer.relativeToViewport({
|
|
|
+ top: 100, // 顶部提前100px触发(优化体验,避免刚进入就播放)
|
|
|
+ bottom: 100 // 底部延迟100px触发(避免快速滑动时频繁切换)
|
|
|
+ }).observe(`#${videoId}`, (res) => {
|
|
|
+ const isInView = res.intersectionRatio > 0; // 是否进入视口(交叉比例>0)
|
|
|
|
|
|
- // 2. 监听视口状态,触发按需播放/暂停
|
|
|
- observer.relativeTo('.list').observe(`#${videoId}`, (res) => {
|
|
|
- const isInView = res.intersectionRatio > 0; // 是否在视口内
|
|
|
+ // 1. 动态更新 _isInView 状态(确保响应式)
|
|
|
+ if (isInView !== item._isInView) {
|
|
|
+ this.$set(item, '_isInView', isInView);
|
|
|
+ }
|
|
|
|
|
|
+ // 2. 联动播放/暂停:进入视口播放,离开暂停
|
|
|
if (isInView) {
|
|
|
- console.log(`[视口监听] 进入视口,触发按需播放:${videoId}`);
|
|
|
- // 标记为在视口,避免重复调用
|
|
|
- this.$set(item, 'isInView', true);
|
|
|
- // 调用统一播放入口,实现按需播放
|
|
|
- this.setupVideoPlayback(item);
|
|
|
+ this.playVideo(item); // 进入视口:播放视频
|
|
|
} else {
|
|
|
- console.log(`[视口监听] 离开视口,暂停播放:${videoId}`);
|
|
|
- // 标记为离开视口
|
|
|
- this.$set(item, 'isInView', false);
|
|
|
- // 暂停播放并清理资源
|
|
|
- this.pauseAndCleanVideo(item);
|
|
|
+ this.pauseVideo(item); // 离开视口:暂停视频
|
|
|
}
|
|
|
});
|
|
|
|
|
|
- // 3. 存储观察器到 item,便于后续销毁
|
|
|
- this.$set(item, 'observer', observer);
|
|
|
- // 标记未销毁,避免 setupVideoPlayback 跳过
|
|
|
- this.$set(item, 'isDestroyed', false);
|
|
|
+ // 存储观察器引用,便于后续清理
|
|
|
+ this.$set(item, '_observer', observer);
|
|
|
},
|
|
|
- pauseAndCleanVideo(item) {
|
|
|
- // 1. 暂停录播/小程序直播(通过 videoContext)
|
|
|
- if (item.videoContext) {
|
|
|
- item.videoContext.pause();
|
|
|
- this.$set(item, 'videoContext', null); // 清空上下文,避免重复调用
|
|
|
- }
|
|
|
|
|
|
- // 2. 销毁 HLS 实例(直播 H5 场景)
|
|
|
- if (item.hlsInstance) {
|
|
|
- item.hlsInstance.destroy();
|
|
|
- this.$set(item, 'hlsInstance', null);
|
|
|
- }
|
|
|
-
|
|
|
- // 3. 销毁视口监听(避免列表刷新后仍监听)
|
|
|
- if (item.observer) {
|
|
|
- item.observer.disconnect();
|
|
|
- this.$set(item, 'observer', null);
|
|
|
- }
|
|
|
-
|
|
|
- // 4. 标记为已销毁(防止重复初始化)
|
|
|
- this.$set(item, 'isDestroyed', true);
|
|
|
- },
|
|
|
-
|
|
|
- setupVideoPlayback(item) {
|
|
|
- // 1. 基础校验:先判断类型,再校验 URL(避免无效执行)
|
|
|
- const isLive = item.liveType === this.LIVE_TYPE.LIVE;
|
|
|
- const videoUrl = isLive ? item.flvHlsUrl : item.videoUrl;
|
|
|
-
|
|
|
- // 无效场景直接返回(录播无URL、直播无m3u8、已销毁)
|
|
|
- if (!videoUrl || item.isDestroyed || (isLive && !videoUrl.includes('.m3u8'))) {
|
|
|
- console.warn(`[播放校验] 跳过无效播放:`, item.liveId, '类型:', isLive ? '直播' : '录播');
|
|
|
- return;
|
|
|
- }
|
|
|
+ // 播放视频
|
|
|
+ playVideo(item) {
|
|
|
+ // 仅当:进入视口(_isInView=true)、未在播放(_isPlaying=false)、无错误(_error=false)时播放
|
|
|
+ if (!item._isInView || item._isPlaying || item._error) return;
|
|
|
|
|
|
const videoId = `myVideo_${item.liveId}`;
|
|
|
- console.log(`[按需播放] 触发:${isLive ? '直播' : '录播'},ID:${videoId}`);
|
|
|
-
|
|
|
- // 2. 先清理旧资源(避免重复初始化)
|
|
|
- this.pauseAndCleanVideo(item);
|
|
|
-
|
|
|
- // 3. 直播逻辑:仅 H5 平台用 HLS,其他平台(小程序/App)用原生 video
|
|
|
- if (isLive) {
|
|
|
- // #ifdef H5
|
|
|
- // 直播 + H5 + m3u8:初始化 HLS
|
|
|
- this.initHlsPlayer(item);
|
|
|
- // #endif
|
|
|
-
|
|
|
- // #ifndef H5
|
|
|
- // 小程序/App 直播:直接用原生 videoContext(无需 HLS,平台自带 m3u8 支持)
|
|
|
- this.initNativeVideo(item, videoId, isLive);
|
|
|
- // #endif
|
|
|
- } else {
|
|
|
- // 4. 录播逻辑:所有平台统一用原生 videoContext(无需 HLS)
|
|
|
- this.initNativeVideo(item, videoId, isLive);
|
|
|
- }
|
|
|
- },
|
|
|
- /**
|
|
|
- * 初始化原生视频(录播 + 小程序/App 直播)
|
|
|
- * @param {Object} item - 列表项数据
|
|
|
- * @param {String} videoId - 视频DOM ID
|
|
|
- * @param {Boolean} isLive - 是否为直播
|
|
|
- */
|
|
|
- initNativeVideo(item, videoId, isLive) {
|
|
|
+ const isLive = item.liveType == 1;
|
|
|
+
|
|
|
+ // 获取video上下文
|
|
|
uni.createSelectorQuery().in(this)
|
|
|
.select(`#${videoId}`)
|
|
|
.fields({
|
|
|
context: true
|
|
|
- }) // 仅获取视频上下文
|
|
|
+ })
|
|
|
.exec((res) => {
|
|
|
- if (!res || !res[0]?.context) {
|
|
|
- console.warn(`[原生视频] 未找到上下文:${videoId}`);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- const videoContext = res[0].context;
|
|
|
- this.$set(item, 'videoContext', videoContext); // 存储上下文,用于后续暂停
|
|
|
-
|
|
|
- // 直播:直接播放(需用户交互,失败时提示)
|
|
|
- if (isLive) {
|
|
|
- videoContext.play().catch(err => {
|
|
|
- console.log(`[直播播放] 自动播放失败(需点击):${item.liveId}`, err);
|
|
|
- // 可选:添加「播放按钮」,点击后调用 videoContext.play()
|
|
|
- });
|
|
|
- }
|
|
|
- // 录播:支持断点续播(从上次进度开始)
|
|
|
- else {
|
|
|
- const startDuration = item.nowDuration || 0; // 从接口获取的上次播放进度
|
|
|
- videoContext.seek(startDuration); // 跳转到指定进度
|
|
|
- videoContext.play().catch(err => {
|
|
|
- console.log(`[录播播放] 失败:${item.liveId}`, err);
|
|
|
- });
|
|
|
+ if (res && res[0] && res[0].context) {
|
|
|
+ const videoContext = res[0].context;
|
|
|
+
|
|
|
+ try {
|
|
|
+ if (isLive) {
|
|
|
+ // 直播流处理
|
|
|
+ // #ifdef H5
|
|
|
+ if (item.flvHlsUrl && item.flvHlsUrl.includes('.m3u8') && Hls.isSupported()) {
|
|
|
+ this.setupHlsPlayback(item, videoContext);
|
|
|
+ } else {
|
|
|
+ videoContext.play();
|
|
|
+ }
|
|
|
+ //#else
|
|
|
+ videoContext.play();
|
|
|
+ //#endif
|
|
|
+ } else {
|
|
|
+ // 录播视频处理 - 添加重试机制
|
|
|
+ const playAttempt = () => {
|
|
|
+ videoContext.play().then(() => {
|
|
|
+ console.log('录播视频播放成功:', item.liveId);
|
|
|
+ this.$set(item, '_isPlaying', true);
|
|
|
+ this.$set(item, '_videoContext', videoContext);
|
|
|
+ }).catch(err => {
|
|
|
+ console.error('录播视频播放失败:', err);
|
|
|
+ this.$set(item, '_error', true);
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ // 如果视频已加载,直接播放;否则监听加载事件
|
|
|
+ if (videoContext.duration > 0) {
|
|
|
+ playAttempt();
|
|
|
+ } else {
|
|
|
+ videoContext.onloadedmetadata = playAttempt;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 播放成功后标记 _isPlaying=true
|
|
|
+ this.$set(item, '_isPlaying', true);
|
|
|
+ this.$set(item, '_videoContext', videoContext);
|
|
|
+ } catch (err) {
|
|
|
+ console.error(`播放失败 ${videoId}:`, err);
|
|
|
+ this.$set(item, '_error', true);
|
|
|
+ }
|
|
|
}
|
|
|
});
|
|
|
},
|
|
|
|
|
|
- // 初始化HLS播放器
|
|
|
- initHlsPlayer(item) {
|
|
|
- // 兜底校验:确保是直播且有 m3u8 地址
|
|
|
- if (item.liveType !== this.LIVE_TYPE.LIVE || !item.flvHlsUrl?.includes('.m3u8')) {
|
|
|
- console.error(`[HLS 错误] 非直播场景调用:${item.liveId}`);
|
|
|
- return;
|
|
|
+
|
|
|
+
|
|
|
+ // 暂停视频
|
|
|
+ pauseVideo(item) {
|
|
|
+ if (!item._isPlaying) return;
|
|
|
+
|
|
|
+ if (item._videoContext) {
|
|
|
+ item._videoContext.pause();
|
|
|
}
|
|
|
|
|
|
- const videoUrl = item.flvHlsUrl;
|
|
|
- if (!Hls.isSupported()) {
|
|
|
- console.error(`[HLS 错误] 浏览器不支持 HLS:${item.liveId}`);
|
|
|
- return;
|
|
|
+ // 清理HLS实例(如果是直播且使用了HLS)
|
|
|
+ // #ifdef H5
|
|
|
+ if (item.liveType == 1 && item._hlsInstance) {
|
|
|
+ item._hlsInstance.destroy();
|
|
|
+ this.$set(item, '_hlsInstance', null);
|
|
|
}
|
|
|
+ //#endif
|
|
|
+ // 暂停后标记 _isPlaying=false(_isInView 已由视口监听控制)
|
|
|
+ this.$set(item, '_isPlaying', false);
|
|
|
+ },
|
|
|
|
|
|
- // 获取 H5 视频 DOM 节点
|
|
|
- const video = document.getElementById(`myVideo_${item.liveId}`);
|
|
|
- if (!video) {
|
|
|
- console.warn(`[HLS 错误] 未找到视频节点:myVideo_${item.liveId}`);
|
|
|
- return;
|
|
|
+ // H5平台的HLS播放设置
|
|
|
+ // #ifdef H5
|
|
|
+ setupHlsPlayback(item, videoElement) {
|
|
|
+ if (item._hlsInstance) {
|
|
|
+ item._hlsInstance.destroy();
|
|
|
}
|
|
|
|
|
|
- // 初始化 HLS(低延迟配置,适合直播)
|
|
|
const hls = new Hls({
|
|
|
enableWorker: true,
|
|
|
- lowLatencyMode: true, // 直播低延迟关键配置
|
|
|
- debug: false,
|
|
|
- maxBufferLength: 3, // 减少缓冲,降低延迟(根据需求调整)
|
|
|
- maxMaxBufferLength: 10
|
|
|
+ lowLatencyMode: true,
|
|
|
+ debug: false
|
|
|
});
|
|
|
|
|
|
- // 绑定视频节点 + 加载流
|
|
|
- hls.attachMedia(video);
|
|
|
- hls.loadSource(videoUrl);
|
|
|
+ hls.attachMedia(videoElement);
|
|
|
+ hls.loadSource(item.flvHlsUrl);
|
|
|
|
|
|
- // 解析成功后播放
|
|
|
hls.on(Hls.Events.MANIFEST_PARSED, () => {
|
|
|
- console.log(`[HLS 直播] 解析成功:${item.liveId}`);
|
|
|
- video.play().catch(err => {
|
|
|
- console.log(`[HLS 播放] 自动播放失败(需点击):${item.liveId}`, err);
|
|
|
- });
|
|
|
+ videoElement.play();
|
|
|
});
|
|
|
|
|
|
- // 错误处理(自动恢复或销毁)
|
|
|
- hls.on(Hls.Events.ERROR, (event, data) => {
|
|
|
- console.error(`[HLS 错误] ${item.liveId}:`, data);
|
|
|
- if (data.fatal) {
|
|
|
- switch (data.type) {
|
|
|
- case Hls.ErrorTypes.NETWORK_ERROR:
|
|
|
- hls.startLoad(); // 网络错误:重试加载
|
|
|
- break;
|
|
|
- default:
|
|
|
- hls.destroy(); // 其他致命错误:销毁实例
|
|
|
- this.$set(item, 'hlsInstance', null);
|
|
|
- break;
|
|
|
- }
|
|
|
+ this.$set(item, '_hlsInstance', hls);
|
|
|
+ },
|
|
|
+ // #endif
|
|
|
+
|
|
|
+ // 清理所有视频资源
|
|
|
+ cleanupAllVideos() {
|
|
|
+ this.list.forEach(item => {
|
|
|
+ this.pauseVideo(item);
|
|
|
+ // 重置视口状态:未进入视口
|
|
|
+ this.$set(item, '_isInView', false);
|
|
|
+ // 销毁观察器
|
|
|
+ if (item._observer) {
|
|
|
+ item._observer.disconnect();
|
|
|
+ this.$set(item, '_observer', null);
|
|
|
}
|
|
|
});
|
|
|
-
|
|
|
- // 存储 HLS 实例到 item(便于后续销毁)
|
|
|
- this.$set(item, 'hlsInstance', hls);
|
|
|
},
|
|
|
|
|
|
-
|
|
|
// mescroll初始化
|
|
|
mescrollInit(mescroll) {
|
|
|
this.mescroll = mescroll;
|
|
|
},
|
|
|
+
|
|
|
// 下拉刷新回调
|
|
|
downCallback(mescroll) {
|
|
|
- // 1. 先清理所有旧列表的视频资源(直播 HLS + 录播上下文)
|
|
|
- this.list.forEach(item => {
|
|
|
- this.pauseAndCleanVideo(item);
|
|
|
- });
|
|
|
-
|
|
|
- // 2. 重置列表和分页(必须在清理后执行)
|
|
|
+ this.cleanupAllVideos();
|
|
|
this.list = [];
|
|
|
- mescroll.resetUpScroll(); // 重置上拉加载的页码
|
|
|
-
|
|
|
- // 3. 可选:下拉后自动加载第一页(符合用户预期)
|
|
|
- this.mescroll.triggerUpScroll();
|
|
|
+ mescroll.resetUpScroll();
|
|
|
},
|
|
|
+
|
|
|
// 上拉加载回调
|
|
|
upCallback(mescroll) {
|
|
|
const pageNum = mescroll.num;
|
|
@@ -318,24 +279,32 @@
|
|
|
liveList(data).then(res => {
|
|
|
if (res.code == 200) {
|
|
|
let curPageData = res.rows || [];
|
|
|
+ console.log("curPageData在这里>>>>", curPageData)
|
|
|
let totalSize = res.total || 0;
|
|
|
|
|
|
+ // 预处理数据,添加状态字段
|
|
|
+ curPageData = curPageData.map(item => {
|
|
|
+ return {
|
|
|
+ ...item,
|
|
|
+ _error: false,
|
|
|
+ _isPlaying: false,
|
|
|
+ _isInView: false // 关键:初始化为未进入视口
|
|
|
+ };
|
|
|
+ });
|
|
|
+
|
|
|
if (pageNum === 1) {
|
|
|
this.list = [];
|
|
|
}
|
|
|
|
|
|
- // 追加新数据
|
|
|
- this.list = this.list.concat(curPageData);
|
|
|
-
|
|
|
- // 关键修改:用 $nextTick 等待DOM渲染完成
|
|
|
- this.$nextTick(() => {
|
|
|
- curPageData.forEach(item => {
|
|
|
- this.initVideoPlayer(item); // 此时视频节点已存在
|
|
|
- });
|
|
|
- });
|
|
|
+ this.list = this.list.concat(curPageData);
|
|
|
+ console.log("list在这里>>>>", list)
|
|
|
+ // DOM更新后初始化视频观察器
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.initAllVideoObservers();
|
|
|
+ });
|
|
|
|
|
|
- mescroll.endBySize(curPageData.length, totalSize);
|
|
|
- } else {
|
|
|
+ mescroll.endBySize(curPageData.length, totalSize);
|
|
|
+ } else {
|
|
|
mescroll.endErr();
|
|
|
uni.showToast({
|
|
|
title: res.msg,
|
|
@@ -344,48 +313,14 @@
|
|
|
}
|
|
|
}).catch(err => {
|
|
|
mescroll.endErr();
|
|
|
- console.log("请求异常:" + JSON.stringify(err));
|
|
|
});
|
|
|
},
|
|
|
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
goLive(item) {
|
|
|
- this.liveId = item.liveId
|
|
|
- console.log("要传的liveId", this.liveId)
|
|
|
uni.navigateTo({
|
|
|
url: `/pages/home/living?liveId=${item.liveId}&immediate=true`
|
|
|
});
|
|
|
- },
|
|
|
-
|
|
|
- // 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();
|
|
|
- // });
|
|
|
- // }
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
</script>
|
|
@@ -406,31 +341,64 @@
|
|
|
width: 340rpx;
|
|
|
height: 600rpx;
|
|
|
background-color: #0d0d0d;
|
|
|
- margin-right: 10rpx;
|
|
|
margin-bottom: 24rpx;
|
|
|
overflow: hidden;
|
|
|
position: relative;
|
|
|
|
|
|
+ image {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ }
|
|
|
+
|
|
|
video {
|
|
|
width: 100%;
|
|
|
height: 100%;
|
|
|
- /* 视频填满列表项,确保可视区域判断准确 */
|
|
|
+ object-fit: cover;
|
|
|
}
|
|
|
|
|
|
.info {
|
|
|
position: absolute;
|
|
|
left: 20rpx;
|
|
|
bottom: 14rpx;
|
|
|
+ right: 20rpx;
|
|
|
color: #ffffff;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+
|
|
|
+ .live-badge {
|
|
|
+ background-color: #e74c3c;
|
|
|
+ padding: 4rpx 12rpx;
|
|
|
+ border-radius: 8rpx;
|
|
|
+ font-size: 20rpx;
|
|
|
+ margin-right: 12rpx;
|
|
|
+ }
|
|
|
+
|
|
|
+ .record-badge {
|
|
|
+ background-color: #3498db;
|
|
|
+ padding: 4rpx 12rpx;
|
|
|
+ border-radius: 8rpx;
|
|
|
+ font-size: 20rpx;
|
|
|
+ margin-right: 12rpx;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- image {
|
|
|
- width: 100%;
|
|
|
- height: 100%;
|
|
|
+ .error-tip {
|
|
|
+ position: absolute;
|
|
|
+ top: 50%;
|
|
|
+ left: 50%;
|
|
|
+ transform: translate(-50%, -50%);
|
|
|
+ color: #fff;
|
|
|
+ background-color: rgba(0, 0, 0, 0.7);
|
|
|
+ padding: 16rpx 24rpx;
|
|
|
+ border-radius: 8rpx;
|
|
|
+ font-size: 24rpx;
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
+ // 使列表项均匀分布
|
|
|
+ .list-item:nth-child(2n) {
|
|
|
+ margin-right: 0;
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
}
|
|
|
</style>
|