566 lines
18 KiB
JavaScript
566 lines
18 KiB
JavaScript
/**
|
||
* 任务处理模块
|
||
* 负责所有任务相关的处理逻辑
|
||
*/
|
||
|
||
import { campApi } from '../../../config/camp_api.js';
|
||
import { questionApi } from '../../../config/question_api.js';
|
||
import * as videoController from './video-controller.js';
|
||
|
||
/**
|
||
* 处理任务点击
|
||
* @param {Object} context - 页面上下文
|
||
* @param {Object} e - 事件对象
|
||
*/
|
||
export function handleTaskClick(context, e) {
|
||
var that = context;
|
||
// 修改已发送标记
|
||
that.setData({
|
||
hasVideoPlayed: false,
|
||
isAutoPlay: true,
|
||
hasLogged10Percent: false
|
||
});
|
||
var taskId = e.currentTarget.dataset.taskId;
|
||
var sectionId = e.currentTarget.dataset.sectionId;
|
||
var number = e.currentTarget.dataset.number;
|
||
var courseList = context.data.courseList;
|
||
var hasJoined = context.data.hasJoined;
|
||
var currentSection = context.data.currentSection; // 使用维护的当前小节信息
|
||
|
||
// 展示等待
|
||
wx.showLoading({
|
||
title: '请稍等...',
|
||
});
|
||
|
||
// 查找点击的小节
|
||
var clickedCourse = null;
|
||
var targetId = sectionId;
|
||
|
||
if (courseList && courseList.length > 0) {
|
||
// 优先通过 ID 匹配
|
||
if (targetId) {
|
||
clickedCourse = courseList.find(function (t) {
|
||
return (
|
||
String(t.id) === String(targetId) ||
|
||
String(t.course_id) === String(targetId) ||
|
||
String(t.section_id) === String(targetId)
|
||
);
|
||
});
|
||
}
|
||
|
||
// 如果通过 ID 找不到,再通过 section_number 匹配
|
||
if (!clickedCourse && number !== undefined && number !== null) {
|
||
clickedCourse = courseList.find(function (t) {
|
||
var tNumber = t.section_number || t.number;
|
||
return tNumber === number;
|
||
});
|
||
}
|
||
}
|
||
|
||
if (!clickedCourse) {
|
||
wx.hideLoading();
|
||
wx.showToast({
|
||
title: '未找到对应小节',
|
||
icon: 'none'
|
||
});
|
||
return;
|
||
}
|
||
|
||
var actualSectionId = clickedCourse.id || clickedCourse.course_id || clickedCourse.section_id || targetId;
|
||
|
||
// 时间间隔未到:若该小节在 sectionUnlockAtMap 中(有解锁倒计时),则禁止进入并提示
|
||
var sectionUnlockAtMap = context.data.sectionUnlockAtMap || {};
|
||
if (sectionUnlockAtMap[actualSectionId]) {
|
||
var unlockAt = sectionUnlockAtMap[actualSectionId];
|
||
var nowSec = Math.floor(Date.now() / 1000);
|
||
var left = unlockAt - nowSec;
|
||
var msg = '时间到了才能开启';
|
||
if (left > 0) {
|
||
// 自然天(明日解锁):不显示具体倒计时
|
||
var unlockDate = new Date(unlockAt * 1000);
|
||
var today = new Date();
|
||
var tomorrowStart = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 1, 0, 0, 0, 0);
|
||
var tomorrowEnd = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 2, 0, 0, 0, 0) - 1;
|
||
if (unlockDate.getTime() >= tomorrowStart.getTime() && unlockDate.getTime() < tomorrowEnd) {
|
||
msg = '任务将于明日才能开启';
|
||
} else {
|
||
var h = Math.floor(left / 3600);
|
||
var m = Math.floor((left % 3600) / 60);
|
||
if (h > 0) {
|
||
msg = '时间到了才能开启,该小节将在 ' + h + ' 小时 ' + m + ' 分钟后解锁';
|
||
} else if (m > 0) {
|
||
msg = '时间到了才能开启,该小节将在 ' + m + ' 分钟后解锁';
|
||
} else {
|
||
msg = '时间到了才能开启,请稍候';
|
||
}
|
||
}
|
||
}
|
||
wx.hideLoading();
|
||
wx.showToast({ title: msg, icon: 'none', duration: 2500 });
|
||
return;
|
||
}
|
||
|
||
// 检查打卡营是否开启
|
||
if (!hasJoined) {
|
||
wx.showModal({
|
||
title: '提示',
|
||
content: '确认开启打卡营😘?',
|
||
complete: function (res) {
|
||
if (res.confirm) {
|
||
if (context.toggleCardType) {
|
||
context.toggleCardType();
|
||
}
|
||
}
|
||
}
|
||
});
|
||
wx.hideLoading();
|
||
return;
|
||
}
|
||
|
||
// 验证1:是否检查上一小节仅看后台配置 require_previous_section(默认 1、2、3 都可直接做,不按 section_number 推断)
|
||
var needCheckPrevious = !!(clickedCourse.require_previous_section === true);
|
||
|
||
// 如果不需要检查上一小节,跳过;否则按下面逻辑验证
|
||
if (needCheckPrevious) {
|
||
var prevCourse = null;
|
||
var currentIndex = courseList.findIndex(function (t) {
|
||
return t === clickedCourse;
|
||
});
|
||
if (currentIndex > 0) {
|
||
prevCourse = courseList[currentIndex - 1];
|
||
} else {
|
||
var clickedSectionNumber = clickedCourse.section_number || clickedCourse.number || 0;
|
||
var prevSectionNumber = clickedSectionNumber - 1;
|
||
prevCourse = courseList.find(function (t) {
|
||
var tSectionNumber = t.section_number || t.number || 0;
|
||
return tSectionNumber === prevSectionNumber;
|
||
});
|
||
}
|
||
if (!prevCourse) {
|
||
wx.hideLoading();
|
||
wx.showToast({
|
||
title: '数据异常,无法找到上一小节',
|
||
icon: 'error'
|
||
});
|
||
return;
|
||
}
|
||
if (!prevCourse.is_completed) {
|
||
var prevSectionTitle = prevCourse.title || prevCourse.course_title || '上一小节';
|
||
wx.hideLoading();
|
||
wx.showModal({
|
||
title: '提示',
|
||
content: '请先完成上一小节\n👇👇👇\n《' + prevSectionTitle + '》',
|
||
showCancel: false,
|
||
confirmText: '知道了'
|
||
});
|
||
return;
|
||
}
|
||
}
|
||
|
||
// 当前小节开启则进行前置任务判断
|
||
if (clickedCourse.is_started) {
|
||
that.setData({
|
||
currentSelectedTaskId: taskId,
|
||
});
|
||
|
||
// 验证2:判断是否有前置任务未完成
|
||
var prerequisites = e.currentTarget.dataset.task.prerequisites;
|
||
var isCompleted = true;
|
||
var taskTitle = '';
|
||
|
||
if (prerequisites && prerequisites.length > 0) {
|
||
prerequisites.forEach(function (item) {
|
||
var course = that.data.courseList.find(function (t) {
|
||
return String(t.course_id) === String(actualSectionId) ||
|
||
String(t.id) === String(actualSectionId) ||
|
||
String(t.section_id) === String(actualSectionId);
|
||
});
|
||
if (!course || !course.tasks) return;
|
||
course.tasks.forEach(function (task) {
|
||
var taskId = task.id || task.task_id;
|
||
if (taskId && (String(taskId) === String(item) || parseInt(taskId) === parseInt(item))) {
|
||
if (task.status !== 'Completed') {
|
||
isCompleted = false;
|
||
taskTitle = task.task_title || task.title || '前置任务';
|
||
}
|
||
}
|
||
});
|
||
});
|
||
}
|
||
|
||
if (!isCompleted) {
|
||
wx.hideLoading();
|
||
wx.showModal({
|
||
title: '提示',
|
||
content: '请先完成前置任务\n👇👇👇\n《' + taskTitle + '》',
|
||
showCancel: false,
|
||
confirmText: '知道了'
|
||
});
|
||
return;
|
||
}
|
||
|
||
// 处理任务跳转
|
||
that.navigateToTaskPage(e);
|
||
return;
|
||
} else {
|
||
// 当前小节未开启:需要先开启小节
|
||
// 如果上一小节验证通过(或不需要验证),则进入开启流程
|
||
// 通过 navigateToTaskPage 中的 checkAndUnlockSection 来处理
|
||
that.navigateToTaskPage(e);
|
||
return;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 导航到任务页面
|
||
* @param {Object} context - 页面上下文
|
||
* @param {Object} e - 事件对象
|
||
*/
|
||
export function navigateToTaskPage(context, e) {
|
||
// 注意:这里不暂停视频,因为如果是视频任务需要自动播放
|
||
// 暂停逻辑会在 executeTaskNavigation 中根据任务类型处理
|
||
|
||
var campId = context.data.campId;
|
||
var sectionId = e.currentTarget.dataset.sectionId || context.data.currentSelectedCourseId;
|
||
var taskId = e.currentTarget.dataset.taskId;
|
||
var taskType = e.currentTarget.dataset.type;
|
||
var taskData = e.currentTarget.dataset.task || {};
|
||
var taskTitle = e.currentTarget.dataset.taskTitle || taskData.title || taskData.task_title || '';
|
||
var taskStatus = taskData.status || (e.currentTarget.dataset.task && e.currentTarget.dataset.task.status);
|
||
var taskDetail = taskData.detail || (e.currentTarget.dataset.task && e.currentTarget.dataset.task.detail);
|
||
|
||
// 检查小节是否在访问列表中,如果不在则尝试开启
|
||
if (sectionId) {
|
||
if (context.checkAndUnlockSection) {
|
||
context.checkAndUnlockSection(sectionId, function (hasAccess) {
|
||
if (!hasAccess) {
|
||
return;
|
||
}
|
||
executeTaskNavigation(context, campId, sectionId, taskId, taskType, taskData, taskTitle, taskStatus, taskDetail);
|
||
}, true);
|
||
} else {
|
||
executeTaskNavigation(context, campId, sectionId, taskId, taskType, taskData, taskTitle, taskStatus, taskDetail);
|
||
}
|
||
} else {
|
||
executeTaskNavigation(context, campId, sectionId, taskId, taskType, taskData, taskTitle, taskStatus, taskDetail);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 执行任务跳转逻辑
|
||
* @param {Object} context - 页面上下文
|
||
* @param {String} campId - 打卡营ID
|
||
* @param {String} sectionId - 小节ID
|
||
* @param {String} taskId - 任务ID
|
||
* @param {String} taskType - 任务类型
|
||
* @param {Object} taskData - 任务数据
|
||
* @param {String} taskTitle - 任务标题
|
||
* @param {String} taskStatus - 任务状态
|
||
* @param {Object} taskDetail - 任务详情
|
||
*/
|
||
export function executeTaskNavigation(context, campId, sectionId, taskId, taskType, taskData, taskTitle, taskStatus, taskDetail) {
|
||
var sendParams = {
|
||
campId: String(campId),
|
||
sectionId: sectionId,
|
||
courseId: sectionId,
|
||
taskId: taskId,
|
||
taskType: taskType,
|
||
taskTitle: taskTitle,
|
||
taskStatus: taskStatus,
|
||
taskDetail: taskDetail,
|
||
task: taskData
|
||
};
|
||
|
||
// 规范化任务类型
|
||
var normalizedTaskType = (taskType || '').toString().trim().toUpperCase();
|
||
if (!normalizedTaskType && taskData) {
|
||
var rawType = taskData.task_type || taskData.type;
|
||
if (rawType) {
|
||
normalizedTaskType = String(rawType).trim().toUpperCase();
|
||
}
|
||
}
|
||
|
||
// 如果还是没有识别出类型,尝试通过 content 判断
|
||
if (!normalizedTaskType && taskData) {
|
||
var content = taskData.content || {};
|
||
// 如果有 essay 相关字段,判断为申论题
|
||
if (content.essay || (content.essay_question && content.essay_question.exam_id)) {
|
||
normalizedTaskType = 'ESSAY';
|
||
}
|
||
// 如果有 exam_id 或 paper_id,判断为客观题
|
||
else if (content.exam_id || content.paper_id || (content.objective && content.objective.exam_id)) {
|
||
normalizedTaskType = 'OBJECTIVE';
|
||
}
|
||
// 如果有 video_url,判断为视频任务
|
||
else if (content.video_url || (content.video && content.video.video_url)) {
|
||
normalizedTaskType = 'VIDEO';
|
||
}
|
||
// 如果有 image_text 相关字段,判断为图文任务
|
||
else if (content.image_text || (content.image_text && content.image_text.image_url)) {
|
||
normalizedTaskType = 'IMAGE_TEXT';
|
||
}
|
||
// 如果有 pdf_url 或 description,判断为主观题
|
||
else if (content.pdf_url || content.description || (content.subjective && (content.subjective.pdf_url || content.subjective.description))) {
|
||
normalizedTaskType = 'SUBJECTIVE';
|
||
}
|
||
}
|
||
|
||
console.log('任务类型判断 - taskType:', taskType, 'taskData.task_type:', taskData?.task_type, 'normalizedTaskType:', normalizedTaskType, 'taskData:', taskData);
|
||
|
||
// 根据任务类型处理
|
||
switch (normalizedTaskType) {
|
||
case 'VIDEO':
|
||
// 视频任务:自动播放
|
||
if (context.handleVideoTask) {
|
||
context.handleVideoTask(sendParams);
|
||
} else {
|
||
videoController.handleVideoTask(context, sendParams);
|
||
}
|
||
break;
|
||
case 'IMAGE_TEXT':
|
||
// 非视频任务:暂停并隐藏视频
|
||
videoController.pauseAndHideVideo(context);
|
||
handleTextImageTask(context, sendParams);
|
||
break;
|
||
case 'OBJECTIVE':
|
||
// 非视频任务:暂停并隐藏视频
|
||
videoController.pauseAndHideVideo(context);
|
||
handleObjectiveTask(context, sendParams);
|
||
break;
|
||
case 'SUBJECTIVE':
|
||
// 非视频任务:暂停并隐藏视频
|
||
videoController.pauseAndHideVideo(context);
|
||
handleSubjectiveTask(context, sendParams);
|
||
break;
|
||
case 'ESSAY':
|
||
// 非视频任务:暂停并隐藏视频
|
||
videoController.pauseAndHideVideo(context);
|
||
handleEssayTask(context, sendParams);
|
||
break;
|
||
default:
|
||
// 非视频任务:暂停并隐藏视频
|
||
videoController.pauseAndHideVideo(context);
|
||
console.warn('未知任务类型:', normalizedTaskType, 'taskData:', taskData);
|
||
wx.showToast({
|
||
title: '暂不支持该类型任务: ' + (normalizedTaskType || '未知'),
|
||
icon: 'none',
|
||
duration: 3000
|
||
});
|
||
}
|
||
wx.hideLoading();
|
||
}
|
||
|
||
/**
|
||
* 处理客观题任务
|
||
* @param {Object} context - 页面上下文
|
||
* @param {Object} sendParams - 任务参数
|
||
*/
|
||
export function handleObjectiveTask(context, sendParams) {
|
||
console.log('handleObjectiveTask 被调用,sendParams:', sendParams);
|
||
campApi.getTaskDetailById(sendParams.taskId)
|
||
.then(function (res) {
|
||
console.log('getTaskDetailById 返回:', res);
|
||
var paperId = null;
|
||
var task = (res && res.task) || (res && res.data && res.data.task);
|
||
if (res && (res.success === true || res.code === 200) && task) {
|
||
var content = task.content || {};
|
||
paperId = content.exam_id || content.paper_id ||
|
||
(content.objective && (content.objective.exam_id || content.objective.paper_id)) || null;
|
||
}
|
||
|
||
if (!paperId) {
|
||
wx.showToast({ title: '试卷ID不存在', icon: 'none' });
|
||
return;
|
||
}
|
||
|
||
var userId = wx.getStorageSync('wxuserid');
|
||
if (!userId) {
|
||
wx.showToast({
|
||
title: '请先登录',
|
||
icon: 'none'
|
||
});
|
||
return;
|
||
}
|
||
|
||
// 请求答题记录接口(用于判断跳转答题页还是结果页)
|
||
console.log('请求答题记录 getAnswerRecord:', { userId: userId, paperId: paperId });
|
||
return questionApi.getAnswerRecord(userId, paperId, 1, 361)
|
||
.then(function(recordRes) {
|
||
var hasRecord = (recordRes.success === true && recordRes.records && recordRes.records.length > 0) ||
|
||
(recordRes.code === 200 && recordRes.data && recordRes.data.length > 0) ||
|
||
(recordRes.data && Array.isArray(recordRes.data) && recordRes.data.length > 0) ||
|
||
(recordRes.records && Array.isArray(recordRes.records) && recordRes.records.length > 0);
|
||
|
||
var query = [];
|
||
query.push('paper_id=' + encodeURIComponent(paperId));
|
||
query.push('task_id=' + encodeURIComponent(sendParams.taskId));
|
||
query.push('camp_id=' + encodeURIComponent(sendParams.campId));
|
||
var title = sendParams.taskTitle || '客观题';
|
||
query.push('task_title=' + encodeURIComponent(title));
|
||
if (sendParams.sectionId !== undefined && sendParams.sectionId !== null && sendParams.sectionId !== 'undefined') {
|
||
query.push('course_id=' + encodeURIComponent(sendParams.sectionId));
|
||
}
|
||
|
||
if (hasRecord) {
|
||
// 如果有答题记录,跳转到结果页面
|
||
wx.navigateTo({
|
||
url: `/pages/camp_task_objective_questions_result/index?${query.join('&')}`,
|
||
success: function() {
|
||
console.log('跳转到结果页面成功');
|
||
},
|
||
fail: function(err) {
|
||
console.error('跳转失败:', err);
|
||
wx.showToast({
|
||
title: '跳转失败: ' + (err.errMsg || '未知错误'),
|
||
icon: 'none',
|
||
duration: 3000
|
||
});
|
||
}
|
||
});
|
||
} else {
|
||
// 如果没有答题记录,跳转到答题页面
|
||
wx.navigateTo({
|
||
url: `/pages/camp_task_objective_questions/index?${query.join('&')}`,
|
||
success: function() {
|
||
console.log('跳转到答题页面成功');
|
||
},
|
||
fail: function(err) {
|
||
console.error('跳转失败:', err);
|
||
wx.showToast({
|
||
title: '跳转失败: ' + (err.errMsg || '未知错误'),
|
||
icon: 'none',
|
||
duration: 3000
|
||
});
|
||
}
|
||
});
|
||
}
|
||
})
|
||
.catch(function(error) {
|
||
console.error('获取答题记录失败:', error);
|
||
var query = [];
|
||
query.push('paper_id=' + encodeURIComponent(paperId));
|
||
query.push('task_id=' + encodeURIComponent(sendParams.taskId));
|
||
query.push('camp_id=' + encodeURIComponent(sendParams.campId));
|
||
var title = sendParams.taskTitle || '客观题';
|
||
query.push('task_title=' + encodeURIComponent(title));
|
||
if (sendParams.sectionId !== undefined && sendParams.sectionId !== null && sendParams.sectionId !== 'undefined') {
|
||
query.push('course_id=' + encodeURIComponent(sendParams.sectionId));
|
||
}
|
||
// 获取答题记录失败,直接跳转到答题页面
|
||
wx.navigateTo({
|
||
url: `/pages/camp_task_objective_questions/index?${query.join('&')}`,
|
||
success: function() {
|
||
console.log('跳转到答题页面成功');
|
||
},
|
||
fail: function(err) {
|
||
console.error('跳转失败:', err);
|
||
wx.showToast({
|
||
title: '跳转失败: ' + (err.errMsg || '未知错误'),
|
||
icon: 'none',
|
||
duration: 3000
|
||
});
|
||
}
|
||
});
|
||
});
|
||
})
|
||
.catch(function (err) {
|
||
console.error('getTaskDetailById 或 getAnswerRecord 失败:', err);
|
||
wx.showToast({ title: '获取任务信息失败', icon: 'none' });
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 处理主观题任务
|
||
* @param {Object} context - 页面上下文
|
||
* @param {Object} sendParams - 任务参数
|
||
*/
|
||
export function handleSubjectiveTask(context, sendParams) {
|
||
var query = [];
|
||
query.push('campId=' + encodeURIComponent(sendParams.campId || ''));
|
||
query.push('taskId=' + encodeURIComponent(sendParams.taskId || ''));
|
||
var title = sendParams.taskTitle || '主观题';
|
||
query.push('taskTitle=' + encodeURIComponent(title));
|
||
if (sendParams.sectionId !== undefined && sendParams.sectionId !== null && sendParams.sectionId !== 'undefined') {
|
||
query.push('courseId=' + encodeURIComponent(sendParams.sectionId || sendParams.courseId || ''));
|
||
}
|
||
wx.navigateTo({
|
||
url: '/pages/camp_task_subjective_question/index?' + query.join('&')
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 处理图文任务
|
||
* @param {Object} context - 页面上下文
|
||
* @param {Object} sendParams - 任务参数
|
||
*/
|
||
export function handleTextImageTask(context, sendParams) {
|
||
wx.navigateTo({
|
||
url: '/pages/camp_task_text_image/index?task_id=' + sendParams.taskId + '&task_title=' + sendParams.taskTitle + '&course_id=' + sendParams.sectionId + '&camp_id=' + sendParams.campId + '&task_status=' + sendParams.taskStatus
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 处理申论题任务
|
||
* @param {Object} context - 页面上下文
|
||
* @param {Object} sendParams - 任务参数
|
||
*/
|
||
export function handleEssayTask(context, sendParams) {
|
||
campApi.getTaskDetailById(sendParams.taskId).then(function (res) {
|
||
if (!res || res.success !== true || !res.task) {
|
||
wx.showToast({
|
||
title: '获取申论题信息失败',
|
||
icon: 'none'
|
||
});
|
||
return;
|
||
}
|
||
|
||
var taskInfo = res.task;
|
||
if (!taskInfo) {
|
||
wx.showToast({
|
||
title: '任务数据异常',
|
||
icon: 'none'
|
||
});
|
||
return;
|
||
}
|
||
|
||
// 兼容多种字段结构获取 paperId
|
||
var paperId = null;
|
||
var content = taskInfo.content || {};
|
||
if (content) {
|
||
if (content.essay) {
|
||
paperId = content.essay.paper_id || content.essay.exam_id || null;
|
||
}
|
||
if (!paperId) {
|
||
paperId = content.paper_id || content.exam_id || null;
|
||
}
|
||
}
|
||
if (!paperId) {
|
||
paperId = taskInfo.paper_id || taskInfo.exam_id || null;
|
||
}
|
||
|
||
if (!paperId) {
|
||
wx.showToast({
|
||
title: '试卷ID不存在',
|
||
icon: 'none'
|
||
});
|
||
return;
|
||
}
|
||
|
||
sendParams.paperId = paperId;
|
||
|
||
wx.navigateTo({
|
||
url: '/pages/camp_task_essay_question/index',
|
||
events: {
|
||
onDataFromChild: function (data) {
|
||
},
|
||
onParamsReceived: function (params) {
|
||
}
|
||
},
|
||
success: function (res) {
|
||
res.eventChannel.emit('sendParams', sendParams);
|
||
}
|
||
});
|
||
});
|
||
}
|
||
|