320 lines
9.1 KiB
JavaScript
320 lines
9.1 KiB
JavaScript
/**
|
||
* 视频控制模块
|
||
* 负责所有视频播放相关的逻辑
|
||
*/
|
||
|
||
import { campApi } from '../../../config/camp_api.js';
|
||
import { resolveVideoCompletionPercent } from './utils.js';
|
||
import * as dataFetcher from './data-fetcher.js';
|
||
|
||
/**
|
||
* 视频播放结束
|
||
* @param {Object} context - 页面上下文
|
||
*/
|
||
export function onVideoEnded(context) {
|
||
context.setData({
|
||
isVideoTaskPlaying: false
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 视频播放错误
|
||
* @param {Object} context - 页面上下文
|
||
*/
|
||
export function onVideoError(context) {
|
||
context.setData({
|
||
showVideo: false,
|
||
isVideoTaskPlaying: false
|
||
});
|
||
wx.showToast({
|
||
title: '视频播放失败',
|
||
icon: 'none'
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 视频开始播放
|
||
* 区分任务视频和介绍视频:只有视频任务才标记 isVideoTaskPlaying
|
||
* @param {Object} context - 页面上下文
|
||
*/
|
||
export function onVideoPlay(context) {
|
||
var isTaskVideo = !!context.data.currentPlayingTaskId;
|
||
context.setData({
|
||
hasVideoPlayed: true,
|
||
isAutoPlay: false,
|
||
_pendingVideoPlay: false,
|
||
isVideoTaskPlaying: isTaskVideo // 只有视频任务才标记为 true
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 视频暂停
|
||
* @param {Object} context - 页面上下文
|
||
*/
|
||
export function onVideoPause(context) {
|
||
context.setData({
|
||
isVideoTaskPlaying: false
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 监听视频播放进度 请求接口
|
||
* @param {Object} context - 页面上下文
|
||
* @param {Object} e - 事件对象
|
||
*/
|
||
export async function onVideoTimeUpdate(context, e) {
|
||
var currentVideoCompletionPercent = context.data.currentVideoCompletionPercent;
|
||
var hasLogged10Percent = context.data.hasLogged10Percent;
|
||
var currentPlayingTaskId = context.data.currentPlayingTaskId;
|
||
var currentSelectedCourseId = context.data.currentSelectedCourseId;
|
||
var currentSelectedCourseIndex = context.data.currentSelectedCourseIndex;
|
||
var requiredPercent = 100;
|
||
|
||
if (typeof currentVideoCompletionPercent === 'number' && !isNaN(currentVideoCompletionPercent) && currentVideoCompletionPercent > 0) {
|
||
requiredPercent = currentVideoCompletionPercent;
|
||
}
|
||
if (requiredPercent <= 0) {
|
||
requiredPercent = 100;
|
||
}
|
||
if (requiredPercent > 100) {
|
||
requiredPercent = 100;
|
||
}
|
||
if (requiredPercent > 0 && requiredPercent <= 1) {
|
||
requiredPercent = requiredPercent * 100;
|
||
}
|
||
var persent = requiredPercent / 100;
|
||
var { currentTime, duration } = e.detail;
|
||
|
||
// 检查是否已经发送过请求
|
||
if (hasLogged10Percent) {
|
||
return;
|
||
}
|
||
|
||
// 检查是否达到进度要求或视频结束
|
||
var isProgressReached = duration > 0 && currentTime / duration >= persent;
|
||
var isVideoEnded = duration > 0 && currentTime >= duration;
|
||
|
||
if ((isProgressReached || isVideoEnded) && currentPlayingTaskId) {
|
||
var userId = String(wx.getStorageSync('wxuserid') || '');
|
||
var taskId = String(currentPlayingTaskId || '');
|
||
|
||
if (!userId) {
|
||
return;
|
||
}
|
||
if (!taskId) {
|
||
return;
|
||
}
|
||
|
||
try {
|
||
var payload = {
|
||
user_id: userId,
|
||
task_id: taskId,
|
||
is_completed: true,
|
||
completed_at: String(Math.floor(Date.now() / 1000))
|
||
};
|
||
var res = await campApi.updateCampProgress(payload);
|
||
if ((res && res.success === true) || (res && res.code === 200)) {
|
||
// 先立即更新任务状态(立即更新UI),不中断视频播放
|
||
if (context.updateTaskStatus) {
|
||
context.updateTaskStatus(taskId, 'Completed');
|
||
}
|
||
|
||
// 视频任务进度已上报成功,静默请求一次营地详情接口,拉取最新任务列表与 can_start,下一任务可解锁
|
||
var campId = context.data.campId;
|
||
if (campId && dataFetcher.fetchCampData) {
|
||
dataFetcher.fetchCampData(context, campId, true);
|
||
} else if (context.refreshTaskListStatus) {
|
||
context.refreshTaskListStatus(currentSelectedCourseId, currentSelectedCourseIndex, true);
|
||
}
|
||
}
|
||
} catch (error) {
|
||
}
|
||
|
||
// 标记已发送请求
|
||
context.setData({ hasLogged10Percent: true });
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 处理视频任务
|
||
* 注意:无论任务是否已完成,都允许用户观看视频
|
||
* @param {Object} context - 页面上下文
|
||
* @param {Object} sendParams - 任务参数
|
||
*/
|
||
export function handleVideoTask(context, sendParams) {
|
||
var taskIdStr = sendParams.taskId ? String(sendParams.taskId) : '';
|
||
var completionPercent = resolveVideoCompletionPercent(sendParams.task);
|
||
var videoCompletionMap = Object.assign({}, context.data.videoCompletionMap || {});
|
||
if (completionPercent === null && taskIdStr && videoCompletionMap.hasOwnProperty(taskIdStr)) {
|
||
completionPercent = videoCompletionMap[taskIdStr];
|
||
}
|
||
|
||
// 使用按任务ID查询的新接口(仅保留新结构)
|
||
// 注意:不检查任务状态,即使任务已完成也允许播放视频
|
||
campApi.getTaskDetailById(sendParams.taskId).then(function(res){
|
||
var videoUrl = '';
|
||
if (!res || res.success !== true || !res.task) {
|
||
wx.showToast({
|
||
title: '获取视频任务失败',
|
||
icon: 'none'
|
||
});
|
||
wx.hideLoading();
|
||
return;
|
||
}
|
||
|
||
var task = res.task || {};
|
||
// 兼容多种格式:task.content.video_url 或 task.content.video.video_url
|
||
var content = task.content || {};
|
||
var videoContent = content.video || {};
|
||
videoUrl = content.video_url || videoContent.video_url || '';
|
||
var detailPercent = resolveVideoCompletionPercent(task);
|
||
if (detailPercent !== null) {
|
||
completionPercent = detailPercent;
|
||
}
|
||
|
||
if (completionPercent === null) {
|
||
completionPercent = 100;
|
||
}
|
||
if (typeof completionPercent !== 'number' || isNaN(completionPercent)) {
|
||
completionPercent = 100;
|
||
}
|
||
if (completionPercent <= 0) {
|
||
completionPercent = 100;
|
||
}
|
||
completionPercent = Math.min(100, Math.max(1, Math.round(completionPercent)));
|
||
|
||
if (taskIdStr) {
|
||
videoCompletionMap[taskIdStr] = completionPercent;
|
||
}
|
||
|
||
if (videoUrl) {
|
||
// 先暂停当前视频(如果有),避免播放冲突
|
||
try {
|
||
if (context.videoContext) {
|
||
context.videoContext.pause();
|
||
}
|
||
} catch (e) {
|
||
}
|
||
|
||
// 准备新的视频数据(注意:不覆盖 introType,保留打卡营原始介绍类型)
|
||
var newVideoData = {
|
||
topContent: videoUrl,
|
||
topContentType: 'INTRO_TYPE_VIDEO',
|
||
// 注意:不设置 introType,保留打卡营原始的介绍类型,以便退出时正确恢复
|
||
showVideo: true,
|
||
hasLogged10Percent: false,
|
||
currentPlayingTaskId: sendParams.taskId,
|
||
currentPlayingTaskType: 'TASK_TYPE_VIDEO',
|
||
videoTitle: sendParams.taskTitle,
|
||
isAutoPlay: true,
|
||
currentVideoCompletionPercent: completionPercent,
|
||
videoCompletionMap: videoCompletionMap,
|
||
_pendingVideoPlay: true, // 标记需要自动播放
|
||
hasVideoPlayed: false // 重置播放状态
|
||
};
|
||
|
||
// 如果当前已经有视频在播放(比如介绍视频),需要先销毁再重建 video 组件
|
||
// 否则微信小程序的 <video> 组件可能不会正确切换视频源
|
||
var isVideoAlreadyShowing = context.data.showVideo && context.data.topContentType === 'INTRO_TYPE_VIDEO';
|
||
|
||
if (isVideoAlreadyShowing) {
|
||
// 先隐藏视频组件(销毁),再显示新的视频
|
||
context.setData({ showVideo: false }, function() {
|
||
setTimeout(function() {
|
||
context.setData(newVideoData, function() {
|
||
// 重新创建 videoContext(因为旧的组件已销毁)
|
||
context.videoContext = wx.createVideoContext('bannerVideo', context);
|
||
setTimeout(function() {
|
||
try {
|
||
if (context.videoContext) {
|
||
var playResult = context.videoContext.play();
|
||
if (playResult && typeof playResult.catch === 'function') {
|
||
playResult.catch(function(err) {});
|
||
}
|
||
}
|
||
} catch (e) {}
|
||
}, 300);
|
||
});
|
||
}, 100); // 等待组件销毁完成
|
||
});
|
||
} else {
|
||
// 当前没有视频在播放,直接设置
|
||
// 确保 videoContext 已初始化
|
||
if (!context.videoContext) {
|
||
context.videoContext = wx.createVideoContext('bannerVideo', context);
|
||
}
|
||
context.setData(newVideoData, function() {
|
||
setTimeout(function() {
|
||
try {
|
||
// 重新获取 videoContext(组件可能是新创建的)
|
||
context.videoContext = wx.createVideoContext('bannerVideo', context);
|
||
if (context.videoContext) {
|
||
var playResult = context.videoContext.play();
|
||
if (playResult && typeof playResult.catch === 'function') {
|
||
playResult.catch(function(err) {});
|
||
}
|
||
}
|
||
} catch (e) {}
|
||
}, 500);
|
||
});
|
||
}
|
||
wx.hideLoading();
|
||
} else {
|
||
wx.showToast({
|
||
title: '视频地址不存在',
|
||
icon: 'none'
|
||
});
|
||
wx.hideLoading();
|
||
}
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 开始播放顶部视频
|
||
* @param {Object} context - 页面上下文
|
||
*/
|
||
export function startTopVideo(context) {
|
||
context.setData({
|
||
showVideo: true,
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 暂停视频(用于页面隐藏或跳转时)
|
||
* @param {Object} context - 页面上下文
|
||
*/
|
||
export function pauseVideo(context) {
|
||
try {
|
||
if (context.videoContext) {
|
||
context.videoContext.pause();
|
||
}
|
||
} catch (e) {
|
||
}
|
||
context.setData({
|
||
isVideoTaskPlaying: false
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 暂停并隐藏视频(用于切换到非视频任务时)
|
||
* @param {Object} context - 页面上下文
|
||
*/
|
||
export function pauseAndHideVideo(context) {
|
||
try {
|
||
// 先暂停视频播放
|
||
if (context.videoContext) {
|
||
context.videoContext.pause();
|
||
}
|
||
} catch (e) {
|
||
}
|
||
|
||
// 隐藏视频组件并清除自动播放标记
|
||
context.setData({
|
||
showVideo: false,
|
||
_pendingVideoPlay: false,
|
||
isAutoPlay: false,
|
||
isVideoTaskPlaying: false
|
||
});
|
||
}
|
||
|