import * as resultService from './modules/result-service.js';
import { questionApi } from '../../config/question_api.js';
import { campApi } from '../../config/camp_api.js';
// 引入 TWEEN(已注释,改用简单动画)
// const TWEEN = require('tween.js');
Page({
data: {
paperId: null,
taskId: null,
paperInfo: {
title: '',
description: '',
duration_minutes: 0
},
questions: [],
currentQuestionIndex: 0,
loading: true,
answers: [],
// 材料相关(与答题页保持一致的双层布局)
materials: [],
currentQuestionMaterial: null,
materialScrollHeight: 400,
currentMaterialIndex: 0,
// 面板拖拽相关
swiperHeight: 0,
panelY: 0,
panelHeight: 400,
panelYMin: 0,
panelYMax: 400,
movableAreaHeight: 600,
showFullMaterial: false,
materialHeight: 400,
isResizing: false,
startY: 0,
startHeight: 500,
lastHeight: 500,
pauseStartTime: null,
currentAnswers: [],
isMultiple: false,
optionMarkers: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'],
optionsClasses: [],
remainingTime: 0,
pausedTime: null,
elapsedSeconds: 0,
tweenHeight: 500,
materialStyle: 'transition: none;',
tagStyle: {
img: 'max-width: 100% !important; width: auto !important; height: auto !important; display: block !important; margin: 20rpx auto !important;',
'img.inline': 'display: inline-block !important; vertical-align: middle !important; max-height: 1.5em !important; width: auto !important; margin: 0 4rpx !important;',
p: 'margin: 0 !important; padding: 0 !important; max-width: 100% !important; box-sizing: border-box !important;',
table: 'border-collapse: collapse !important; margin: 0 !important; max-width: 100% !important; box-sizing: border-box !important;',
th: 'border: 1px solid #ccc !important; padding: 4px 8px !important; max-width: 100% !important; box-sizing: border-box !important;',
td: 'border: 1px solid #ccc !important; padding: 4px 8px !important; max-width: 100% !important; box-sizing: border-box !important;',
},
totalPausedTime: 0,
questionAnsweredStatus: [],
courseId: null,
showExplanation: false,
currentExplanation: '',
showDetail: false,
correctCount: 0,
totalCount: 0,
correctRate: 0,
usedTime: '00:00',
stats: {},
domain: 'https://your-domain.com',
},
onLoad(options) {
const { paper_id, task_id, course_id, task_title, camp_id } = options;
const decodedTitle = task_title ? decodeURIComponent(task_title) : '';
wx.setNavigationBarTitle({
title: decodedTitle || '答题结果'
});
if (!paper_id) {
wx.showToast({
title: '试卷ID不存在',
icon: 'none'
});
return;
}
// 计算主内容区高度、问题面板高度、拖动边界(与答题页一致)
try {
const sys = wx.getSystemInfoSync();
const w = sys.windowWidth || 375;
const h = Math.max(0, sys.windowHeight);
const materialHeaderPx = Math.round(160 * (w / 750));
const panelYMin = materialHeaderPx;
const panelHeight = Math.max(200, h - panelYMin);
const panelYMax = Math.max(panelYMin, h - 80);
const movableAreaHeight = panelYMax + panelHeight;
const defaultPanelY = Math.max(panelYMin, Math.min(panelYMax, Math.round(h / 2)));
this.setData({
swiperHeight: h,
panelHeight,
panelY: defaultPanelY,
panelYMin,
panelYMax,
movableAreaHeight
});
} catch (e) {}
this.setData({
campId: camp_id,
paperId: paper_id,
taskId: task_id,
taskTitle: decodedTitle || '答题结果',
courseId: course_id,
startTime: new Date().getTime(),
totalPausedTime: 0,
pauseStartTime: null,
timerText: '00:00:00',
loading: true
});
// 添加错误处理
this.fetchQuestionPaper(paper_id)
.then(() => {
// 延迟设置 loading 为 false,确保加载动画有足够时间显示
setTimeout(() => {
this.setData({ loading: false });
}, 500);
})
.catch(error => {
console.error('加载试卷失败:', error);
wx.showToast({
title: '加载试卷失败',
icon: 'none'
});
this.setData({ loading: false });
});
},
// 获取试卷及问题数据
fetchQuestionPaper(paperId) {
this.setData({ loading: true });
const userId = wx.getStorageSync('wxuserid');
if (!userId) {
wx.showToast({
title: '请先登录',
icon: 'none'
});
return Promise.reject(new Error('用户未登录'));
}
return resultService.fetchQuestionPaper(paperId, this.data.taskId, userId).then(resultData => {
const isQualified = resultData.stats.correctRate >= 60;
// 若无答题记录(如刚重置),应进入答题页而非结果页
const answeredCount = resultData.stats.answeredCount != null ? resultData.stats.answeredCount : resultData.stats.totalCount;
if (answeredCount === 0) {
const q = [
'paper_id=' + encodeURIComponent(this.data.paperId || paperId),
'task_id=' + encodeURIComponent(this.data.taskId || ''),
'camp_id=' + encodeURIComponent(this.data.campId || ''),
'task_title=' + encodeURIComponent(this.data.taskTitle || ''),
'course_id=' + encodeURIComponent(this.data.courseId || '')
].filter(Boolean).join('&');
wx.redirectTo({
url: `/pages/camp_task_objective_questions/index?${q}`,
fail: () => {
this.setData({
paperInfo: resultData.paperInfo,
questions: resultData.questions,
currentQuestionIndex: 0,
correctCount: 0,
totalCount: 0,
correctRate: '0',
usedTime: '00:00',
isQualified: false,
loading: false
});
}
});
return resultData;
}
// 处理 paper 级别的材料(与答题页保持一致)
const materials = resultData.materials || [];
const h = this.data.swiperHeight || 400;
const w = (() => { try { return wx.getSystemInfoSync().windowWidth; } catch (e) { return 375; } })();
const titleBarPx = materials.length > 1
? Math.round(140 * (w / 750))
: Math.round(100 * (w / 750));
const materialScrollHeight = materials.length > 0 ? Math.max(0, h - titleBarPx) : h;
const panelYMin = this.data.panelYMin ?? Math.round(160 * (375 / 750));
const panelHeight = Math.max(200, h - panelYMin);
const panelYMax = Math.max(panelYMin, h - 80);
const movableAreaHeight = panelYMax + panelHeight;
const defaultPanelY = Math.max(panelYMin, Math.min(panelYMax, Math.round(h / 2)));
// 根据第一道题目的 material_id 查找对应材料
const firstQuestion = resultData.questions[0] || null;
const currentQuestionMaterial = this.findMaterialForQuestion(firstQuestion, materials);
const actualPanelY = currentQuestionMaterial ? defaultPanelY : 0;
this.setData({
paperInfo: resultData.paperInfo,
questions: resultData.questions,
currentQuestionIndex: 0,
correctCount: resultData.stats.correctCount,
totalCount: resultData.stats.totalCount,
correctRate: resultData.stats.correctRate.toFixed(2),
usedTime: resultData.stats.usedTime,
isQualified: isQualified,
materials,
materialScrollHeight,
currentQuestionMaterial,
panelHeight,
panelY: actualPanelY,
panelYMin,
panelYMax,
movableAreaHeight,
loading: false
});
return resultData;
}).catch(err => {
console.error('API调用失败:', err);
this.setData({ loading: false });
throw err;
});
},
/**
* 根据题目的 material_id 查找对应的材料(与答题页一致)
*/
findMaterialForQuestion(question, materials) {
const mats = materials || this.data.materials || [];
if (!question || !question.material_id) return null;
return mats.find(m => m.id === question.material_id) || null;
},
/**
* 切换题目时更新当前题目的材料和面板位置
*/
updateQuestionMaterial(questionIndex) {
const question = this.data.questions[questionIndex];
const currentQuestionMaterial = this.findMaterialForQuestion(question);
const { panelYMin, panelYMax, swiperHeight } = this.data;
const defaultPanelY = Math.max(panelYMin, Math.min(panelYMax, Math.round(swiperHeight / 2)));
const newPanelY = currentQuestionMaterial ? defaultPanelY : 0;
this.setData({
currentQuestionIndex: questionIndex,
currentQuestionMaterial,
panelY: newPanelY
});
},
// 面板拖拽相关方法(与答题页一致)
onPanelDragStart(e) {
if (!e.touches || !e.touches.length) return;
this._panelDragStartY = e.touches[0].clientY;
this._panelDragStartPanelY = this.data.panelY;
},
onPanelDragMove(e) {
if (!e.touches || !e.touches.length || this._panelDragStartY == null) return;
const { panelYMin, panelYMax } = this.data;
const deltaY = e.touches[0].clientY - this._panelDragStartY;
let newY = this._panelDragStartPanelY + deltaY;
newY = Math.max(panelYMin, Math.min(panelYMax, newY));
this.setData({ panelY: newY });
},
onPanelDragEnd(e) {
this._panelDragStartY = null;
this._panelDragStartPanelY = null;
},
// 移除HTML标签的辅助方法
stripHtmlTags(html) {
if (!html) return '';
return html.replace(/<\/?[^>]+(>|$)/g, "");
},
// 材料切换事件
onMaterialChange(e) {
this.setData({
currentMaterialIndex: e.detail.current
});
},
// 更新选项样式
updateOptionsClasses() {
const { questions, answers } = this.data;
const newOptionsClasses = questions.map((question, qIndex) => {
const questionAnswers = answers[qIndex] || [];
return question.options.map((_, optIndex) =>
questionAnswers.includes(optIndex) ? 'option-selected-single' : ''
);
});
this.setData({
optionsClasses: newOptionsClasses
});
},
// 获取选项样式
getOptionClass(optionIndex) {
const { currentQuestionIndex, questions, answers } = this.data;
if (!answers[currentQuestionIndex]) return '';
const currentQuestion = questions[currentQuestionIndex];
const currentAnswers = answers[currentQuestionIndex];
if (currentAnswers.includes(optionIndex)) {
return currentQuestion.type === 'single' ? 'option-selected-single' : 'option-selected-multiple';
}
return '';
},
// 题目切换动画开始时
onQuestionTransition() {
// 保持为空
},
// 题目切换动画完成时
onQuestionAnimationFinish(e) {
const index = e.detail.current;
this.updateQuestionMaterial(index);
},
// 修改拖动相关的方法
startResize(e) {
const currentQuestion = this.data.questions[this.data.currentQuestionIndex];
if (!currentQuestion?.materials?.length) return;
// 停止之前的动画(已注释 TWEEN,改用简单清理)
// if (this.tween) {
// this.tween.stop();
// this.tween = null;
// }
if (this.animationTimer) {
clearTimeout(this.animationTimer);
this.animationTimer = null;
}
this.initialY = e.touches[0].clientY;
this.initialHeight = this.data.materialHeight;
if (!this.systemInfo) {
this.systemInfo = wx.getSystemInfoSync();
this.rpxRatio = 750 / this.systemInfo.windowWidth;
}
this.setData({
isResizing: true,
materialStyle: 'transition: none;'
});
},
onResize(e) {
if (!this.data.isResizing) return;
if (!this.resizeAnimationFrame) {
this.resizeAnimationFrame = setTimeout(() => {
const deltaY = e.touches[0].clientY - this.initialY;
const newHeight = this.initialHeight + deltaY * this.rpxRatio;
// 限制高度范围
const minHeight = 200;
const maxHeight = 800;
const clampedHeight = Math.min(Math.max(newHeight, minHeight), maxHeight);
this.setData({
materialHeight: clampedHeight,
materialStyle: 'transition: none;'
});
this.resizeAnimationFrame = null;
}, 16);
}
},
endResize() {
if (!this.data.isResizing) return;
// 清理 requestAnimationFrame
if (this.resizeAnimationFrame) {
clearTimeout(this.resizeAnimationFrame);
this.resizeAnimationFrame = null;
}
const currentHeight = this.data.materialHeight;
const targetHeight = Math.round(currentHeight / 50) * 50;
// 使用简单动画替代 TWEEN
this.setData({
materialHeight: targetHeight,
materialStyle: 'transition: height 0.3s ease-out;',
isResizing: false
});
// 动画完成后清理
setTimeout(() => {
this.setData({
materialStyle: '' // 恢复默认样式
});
this.initialY = null;
this.initialHeight = null;
this.tween = null;
}, 300);
},
// 在页面卸载时清理所有定时器和状态
onUnload() {
// if (this.tween) {
// this.tween.stop();
// this.tween = null;
// }
if (this.animationTimer) {
clearTimeout(this.animationTimer);
this.animationTimer = null;
}
if (this.resizeAnimationFrame) {
clearTimeout(this.resizeAnimationFrame);
this.resizeAnimationFrame = null;
}
this.initialY = null;
this.initialHeight = null;
// if (this.timerInterval) {
// clearInterval(this.timerInterval);
// this.timerInterval = null;
// }
},
// 修改富文本点击处理方法
onRichTextTap(e) {
const nodes = e.currentTarget.dataset.content;
if (!nodes || !Array.isArray(nodes)) return;
// 收集所有图片URL
const images = nodes
.filter(node => node.name === 'img')
.map(node => node.attrs.src)
.filter(url => url && url.startsWith('http'));
// 如果找到图片,则打开预览
if (images.length > 0) {
wx.previewImage({
current: images[0],
urls: images
});
}
},
// 添加单独的图片预览方法
previewImage(e) {
const { url, urls } = e.currentTarget.dataset;
wx.previewImage({
current: url,
urls: urls || [url]
});
},
// 如果需要处理材料中的图片预览,也可以添加一个专门的方法
onMaterialImageTap(e) {
const url = e.currentTarget.dataset.url;
if (url) {
wx.previewImage({
current: url,
urls: [url]
});
}
},
// 修改富文本处理方法
processRichText(content) {
if (!content) return '';
// 为行内图片添加 class
return content.replace(/
/g, (match, attrs) => {
// 检查图片是否在段落中且没有换行符
const isInline = match.includes('style="display: inline') ||
match.includes('vertical-align: middle');
if (isInline) {
// 如果是行内图片,添加 inline class
return match.replace('
{
this.updateQuestionMaterial(index);
});
},
// 添加 nextQuestion 方法(如果还没有的话)
nextQuestion: function () {
if (this.data.currentQuestionIndex < this.data.questions.length - 1) {
this.setData({
currentQuestionIndex: this.data.currentQuestionIndex + 1
});
}
},
// 添加数据变化监听器
onAnswersChange: function () {
console.log('Current answers state:', this.data.answers);
},
// 添加一个方法用于判断选项是否被选中
isOptionSelected: function (questionIndex, optionIndex) {
const answers = this.data.answers[questionIndex] || [];
return answers.indexOf(optionIndex) !== -1;
},
// 修改判断题目是否已答的方法
isQuestionAnswered: function (index) {
// 获取当前题目的答案数组
const answers = this.data.answers;
console.log('answers:', answers);
if (!answers || !answers[index]) return false;
// 直接遍历答案数组,检查是否有 true 值
return answers[index].some(answer => answer === true);
},
// 监听材料区域高度变化
onMaterialResize(height) {
// height 参数是rpx值,需要转换为px
const systemInfo = wx.getSystemInfoSync();
const pxHeight = height * systemInfo.windowWidth / 750;
this.setData({
materialHeight: pxHeight
});
},
// 在拖动手柄时实时更新高度
onResizeMove(e) {
// ... 现有的拖动逻辑 ...
const newHeight = this.calculateNewHeight(e);
this.onMaterialResize(newHeight);
},
// 拖动结束时保存最终高度
onResizeEnd(e) {
// ... 现有的结束拖动逻辑 ...
const finalHeight = this.calculateNewHeight(e);
this.onMaterialResize(finalHeight);
},
// 重新答题
retakeQuiz() {
wx.showModal({
title: '提示',
content: '重新答题会清空当前答题记录,是否继续?',
success: (res) => {
if (res.confirm) {
// 获取用户ID
const userId = wx.getStorageSync('wxuserid');
if (!userId) {
wx.showToast({
title: '请先登录',
icon: 'none'
});
return;
}
// 显示加载提示
wx.showLoading({
title: '处理中...',
mask: true
});
// 调用重置任务进度接口
campApi.resetTaskProgress({
user_id: String(userId),
task_id: String(this.data.taskId)
})
.then(resetRes => {
console.log('重置任务进度接口返回:', resetRes);
// 判断重置是否成功(兼容多种返回格式)
const isSuccess = resetRes.success === true ||
resetRes.code === 200 ||
(resetRes.data && resetRes.data.success === true);
if (isSuccess) {
wx.hideLoading();
wx.showToast({
title: '已清空答题记录',
icon: 'success',
duration: 1500
});
// 延迟跳转,确保提示显示
setTimeout(() => {
// 构建跳转参数
const params = {
paper_id: this.data.paperId,
task_id: this.data.taskId,
course_id: this.data.courseId,
task_title: this.data.taskTitle,
camp_id: this.data.campId
};
const queryString = Object.keys(params)
.map(key => `${key}=${encodeURIComponent(params[key] || '')}`)
.join('&');
// 跳转到答题页面
wx.redirectTo({
url: `/pages/camp_task_objective_questions/index?${queryString}`
});
}, 1500);
} else {
wx.hideLoading();
wx.showToast({
title: resetRes.message || resetRes.msg || '重置失败',
icon: 'none'
});
}
})
.catch(err => {
console.error('重置任务进度失败:', err);
wx.hideLoading();
wx.showToast({
title: '重置失败,请重试',
icon: 'none'
});
});
}
}
});
},
// 查看结算
showSummary() {
this.setData({
showDetail: false
});
},
// 查看全部解析
showAnalysis() {
this.setData({ showDetail: true }, () => {
this.updateQuestionMaterial(0);
});
}
});