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); }); } });