794 lines
33 KiB
JavaScript
794 lines
33 KiB
JavaScript
import * as paperService from './modules/paper-service.js';
|
||
import * as timerService from './modules/timer-service.js';
|
||
import { questionApi } from '../../config/question_api.js';
|
||
import { campApi } from '../../config/camp_api.js';
|
||
|
||
Page({
|
||
data: {
|
||
paperId: null,
|
||
taskId: null,
|
||
paperInfo: {
|
||
title: '',
|
||
description: '',
|
||
duration_minutes: 0
|
||
},
|
||
questions: [],
|
||
currentQuestionIndex: 0,
|
||
loading: true,
|
||
answers: [],
|
||
materials: [],
|
||
materialTitle: '资料',
|
||
materialTabLabels: [],
|
||
materialScrollHeight: 400,
|
||
currentMaterialIndex: 0,
|
||
currentQuestionMaterial: null, // 当前题目对应的材料(根据题目的 material_id 查找)
|
||
panelY: 0,
|
||
panelHeight: 400,
|
||
panelYMin: 0,
|
||
panelYMax: 400,
|
||
movableAreaHeight: 600,
|
||
showFullMaterial: false,
|
||
materialHeight: 400,
|
||
isResizing: false,
|
||
startY: 0,
|
||
startHeight: 200,
|
||
minHeight: 200,
|
||
maxHeight: 1000,
|
||
currentAnswers: [],
|
||
isMultiple: false,
|
||
optionMarkers: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'],
|
||
optionsClasses: [],
|
||
materialStyle: '',
|
||
tagStyle: {
|
||
img: 'max-width: 100%; height: auto;',
|
||
'img.inline': 'display: inline-block; vertical-align: middle; max-height: 1.5em;',
|
||
p: 'margin: 0; padding: 0;',
|
||
table: 'border-collapse: collapse; margin: 0;',
|
||
th: 'border: 1px solid #ccc; padding: 4px 8px;',
|
||
td: 'border: 1px solid #ccc; padding: 4px 8px;',
|
||
},
|
||
courseId: null,
|
||
showExplanation: false,
|
||
currentExplanation: '',
|
||
currentQuestion: null,
|
||
questionAnsweredStatus: [],
|
||
showQuestionCard: false,
|
||
startTime: '',
|
||
systemInfo: null,
|
||
animation: null,
|
||
currentHeight: 400,
|
||
resizeAnimation: null,
|
||
isPaused: false,
|
||
timerText: '00:00:00',
|
||
pauseStartTime: null,
|
||
showCustomModal: false,
|
||
pendingSubmit: false,
|
||
swiperHeight: 0,
|
||
},
|
||
|
||
onLoad(options) {
|
||
// console.log('页面加载参数:', 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;
|
||
}
|
||
|
||
// 计算主内容区高度、问题面板高度、拖动边界(单位 px)
|
||
// 内容区全屏,工具栏为固定覆盖层不占位;问题面板可很高(从资料标题到屏幕底)
|
||
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);
|
||
// 向下拖时至少保留约 80px 面板可见,便于继续拖动;工具栏盖在面板上
|
||
const panelYMax = Math.max(panelYMin, h - 80);
|
||
const movableAreaHeight = panelYMax + panelHeight;
|
||
// 问题面板默认盖住半屏:初始 y 为屏幕高度一半
|
||
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({
|
||
taskTitle: decodedTitle || '客观题',
|
||
campId: camp_id,
|
||
paperId: paper_id,
|
||
taskId: task_id,
|
||
courseId: course_id,
|
||
loading: true,
|
||
answers: [],
|
||
questionAnsweredStatus: [],
|
||
startTime: new Date().toISOString(),
|
||
});
|
||
|
||
// 加载题目数据(不包含答案和解析)
|
||
this.fetchQuestionPaper(paper_id, task_id)
|
||
.then(() => {
|
||
setTimeout(() => {
|
||
this.setData({ loading: false });
|
||
}, 300);
|
||
})
|
||
.catch(error => {
|
||
wx.showToast({
|
||
title: '加载试卷失败',
|
||
icon: 'none'
|
||
});
|
||
this.setData({ loading: false });
|
||
});
|
||
},
|
||
|
||
onReady() {
|
||
// 初始化系统信息
|
||
this.systemInfo = wx.getSystemInfoSync();
|
||
// 创建动画实例
|
||
this.animation = wx.createAnimation({
|
||
duration: 0,
|
||
timingFunction: 'linear'
|
||
});
|
||
},
|
||
|
||
/**
|
||
* 根据题目的 material_id 查找对应的材料
|
||
* @param {Object} question - 题目对象
|
||
* @param {Array} materials - 材料数组(可选,默认使用 this.data.materials)
|
||
* @returns {Object|null} 对应的材料对象,如果没有则返回 null
|
||
*/
|
||
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;
|
||
},
|
||
|
||
/**
|
||
* 切换题目时更新当前题目的材料和面板位置
|
||
* @param {Number} questionIndex - 题目索引
|
||
*/
|
||
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,
|
||
currentQuestion: question,
|
||
currentQuestionMaterial,
|
||
panelY: newPanelY
|
||
});
|
||
},
|
||
|
||
fetchQuestionPaper(paperId, taskId) {
|
||
return paperService.fetchQuestionPaper(paperId, taskId).then(paperData => {
|
||
const materials = paperData.materials || [];
|
||
const materialTitle = materials.length <= 1 ? '资料' : '资料一';
|
||
const tabLabels = ['资料一', '资料二', '资料三', '资料四', '资料五', '资料六', '资料七', '资料八', '资料九', '资料十'];
|
||
const materialTabLabels = (materials.length > 1)
|
||
? materials.map((_, i) => tabLabels[i] || ('资料' + (i + 1)))
|
||
: [];
|
||
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)));
|
||
const panelY = materials.length > 0 ? defaultPanelY : 0;
|
||
// 根据第一道题目的 material_id 查找对应材料
|
||
const firstQuestion = paperData.questions[0] || null;
|
||
const currentQuestionMaterial = this.findMaterialForQuestion(firstQuestion, materials);
|
||
// 如果当前题目没有材料,面板从顶部开始
|
||
const actualPanelY = currentQuestionMaterial ? defaultPanelY : 0;
|
||
this.setData({
|
||
paperInfo: paperData.paperInfo,
|
||
questions: paperData.questions,
|
||
answers: paperData.answers,
|
||
questionAnsweredStatus: paperData.questionAnsweredStatus,
|
||
currentQuestionIndex: 0,
|
||
currentQuestion: firstQuestion,
|
||
materials,
|
||
materialTitle,
|
||
materialTabLabels,
|
||
materialScrollHeight,
|
||
currentMaterialIndex: 0,
|
||
currentQuestionMaterial,
|
||
panelHeight,
|
||
panelY: actualPanelY,
|
||
panelYMin,
|
||
panelYMax,
|
||
movableAreaHeight,
|
||
loading: false
|
||
});
|
||
if (!this.data.taskTitle && paperData.paperInfo && paperData.paperInfo.title) {
|
||
wx.setNavigationBarTitle({ title: paperData.paperInfo.title });
|
||
}
|
||
|
||
// 试卷加载完成后启动计时器
|
||
const durationMinutes = paperData.paperInfo ? (paperData.paperInfo.duration_minutes || 0) : 0;
|
||
if (durationMinutes > 0) {
|
||
timerService.startTimer(this, durationMinutes, this.data.startTime, () => {
|
||
wx.showModal({
|
||
title: '时间到',
|
||
content: '答题时间已到,将自动提交',
|
||
showCancel: false,
|
||
success: () => {
|
||
this.submitAnswers();
|
||
}
|
||
});
|
||
});
|
||
} else {
|
||
// 如果没有时长限制,使用简单的计时器(显示已用时间)
|
||
this.startSimpleTimer();
|
||
}
|
||
});
|
||
},
|
||
|
||
|
||
stripHtmlTags(html) {
|
||
if (!html) return '';
|
||
return html.replace(/<\/?[^>]+(>|$)/g, "");
|
||
},
|
||
|
||
onMaterialChange(e) {
|
||
const idx = e.detail.current;
|
||
const labels = (this.data.materialTabLabels || []);
|
||
const materialTitle = labels[idx] || ('资料' + (idx + 1));
|
||
this.setData({
|
||
currentMaterialIndex: idx,
|
||
materialTitle
|
||
});
|
||
},
|
||
|
||
onMaterialTabTap(e) {
|
||
const index = parseInt(e.currentTarget.dataset.index, 10) || 0;
|
||
const labels = (this.data.materialTabLabels || []);
|
||
const materialTitle = labels[index] || ('资料' + (index + 1));
|
||
this.setData({
|
||
currentMaterialIndex: index,
|
||
materialTitle
|
||
});
|
||
},
|
||
|
||
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
|
||
});
|
||
},
|
||
|
||
handleOptionClick(e) {
|
||
// 处理组件触发的事件
|
||
const { optionIndex, questionIndex } = e.detail;
|
||
const currentQuestion = this.data.questions[questionIndex];
|
||
const isSingleChoice = currentQuestion.type === 'single_choice';
|
||
const isLastQuestion = questionIndex === this.data.questions.length - 1;
|
||
|
||
let newAnswers = [...this.data.answers];
|
||
if (!newAnswers[questionIndex]) {
|
||
newAnswers[questionIndex] = new Array(currentQuestion.options.length).fill(false);
|
||
}
|
||
|
||
if (isSingleChoice) {
|
||
newAnswers[questionIndex] = newAnswers[questionIndex].map((_, i) => i === optionIndex);
|
||
} else {
|
||
newAnswers[questionIndex][optionIndex] = !newAnswers[questionIndex][optionIndex];
|
||
}
|
||
|
||
let newQuestionAnsweredStatus = [...this.data.questionAnsweredStatus];
|
||
newQuestionAnsweredStatus[questionIndex] = true;
|
||
|
||
this.setData({
|
||
answers: newAnswers,
|
||
questionAnsweredStatus: newQuestionAnsweredStatus
|
||
}, () => {
|
||
if (isSingleChoice) {
|
||
if (isLastQuestion) {
|
||
setTimeout(() => {
|
||
this.setData({
|
||
showQuestionCard: true
|
||
});
|
||
}, 300);
|
||
} else {
|
||
const nextIndex = questionIndex + 1;
|
||
this.updateQuestionMaterial(nextIndex);
|
||
}
|
||
}
|
||
});
|
||
},
|
||
|
||
getOptionClass(index) {
|
||
const { currentQuestionIndex, answers } = this.data;
|
||
if (!answers || !answers[currentQuestionIndex]) return '';
|
||
return answers[currentQuestionIndex][index] ? 'option-selected' : '';
|
||
},
|
||
|
||
onQuestionTransition() {
|
||
// 保持为空
|
||
},
|
||
|
||
onQuestionAnimationFinish(e) {
|
||
const index = e.detail.current;
|
||
this.setData({
|
||
currentQuestionIndex: index,
|
||
currentMaterialIndex: 0
|
||
});
|
||
},
|
||
|
||
// 简单计时器(显示已用时间,无时长限制)
|
||
startSimpleTimer() {
|
||
if (this.timerInterval) {
|
||
clearInterval(this.timerInterval);
|
||
}
|
||
|
||
this.timerInterval = setInterval(() => {
|
||
if (!this.data.isPaused) {
|
||
const startTime = new Date(this.data.startTime);
|
||
const currentTime = new Date();
|
||
const elapsedTime = Math.floor((currentTime - startTime) / 1000);
|
||
|
||
const hours = Math.floor(elapsedTime / 3600);
|
||
const minutes = Math.floor((elapsedTime % 3600) / 60);
|
||
const seconds = elapsedTime % 60;
|
||
|
||
const formattedTime = [
|
||
hours.toString().padStart(2, '0'),
|
||
minutes.toString().padStart(2, '0'),
|
||
seconds.toString().padStart(2, '0')
|
||
].join(':');
|
||
|
||
this.setData({
|
||
timerText: formattedTime
|
||
});
|
||
}
|
||
}, 1000);
|
||
},
|
||
|
||
togglePause() {
|
||
const newPausedState = !this.data.isPaused;
|
||
|
||
// 更新暂停状态,控制遮罩显示/隐藏
|
||
this.setData({ isPaused: newPausedState });
|
||
|
||
if (newPausedState) {
|
||
// 暂停计时器
|
||
const pausedSeconds = timerService.pauseTimer(this);
|
||
this._pausedSeconds = pausedSeconds;
|
||
} else {
|
||
// 恢复计时器
|
||
if (this._pausedSeconds !== undefined) {
|
||
const durationMinutes = this.data.paperInfo ? (this.data.paperInfo.duration_minutes || 0) : 0;
|
||
if (durationMinutes > 0) {
|
||
timerService.resumeTimer(this, this._pausedSeconds, () => {
|
||
wx.showModal({
|
||
title: '时间到',
|
||
content: '答题时间已到,将自动提交',
|
||
showCancel: false,
|
||
success: () => {
|
||
this.submitAnswers();
|
||
}
|
||
});
|
||
});
|
||
} else {
|
||
// 简单计时器恢复
|
||
this.startSimpleTimer();
|
||
}
|
||
}
|
||
}
|
||
},
|
||
|
||
showAnswerCard: function () {
|
||
// 实现显示答题卡逻辑
|
||
},
|
||
|
||
nextQuestion: function () {
|
||
if (this.data.currentQuestionIndex < this.data.questions.length - 1) {
|
||
this.updateQuestionMaterial(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;
|
||
|
||
return answers[index].some(answer => answer === true);
|
||
},
|
||
|
||
onMaterialResizeEnd(e) {
|
||
this.setData({
|
||
currentMaterialIndex: e.detail.current
|
||
});
|
||
},
|
||
|
||
onRichTextTap(e) {
|
||
const nodes = e.currentTarget.dataset.content;
|
||
if (!nodes || !Array.isArray(nodes)) return;
|
||
|
||
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 '';
|
||
|
||
return content.replace(/<img(.*?)>/g, (match, attrs) => {
|
||
const isInline = match.includes('style="display: inline') ||
|
||
match.includes('vertical-align: middle');
|
||
|
||
if (isInline) {
|
||
return match.replace('<img', '<img class="inline"');
|
||
}
|
||
return match;
|
||
});
|
||
},
|
||
|
||
onImgTap(e) {
|
||
const { src } = e.detail;
|
||
if (src) {
|
||
wx.previewImage({
|
||
current: src,
|
||
urls: [src]
|
||
});
|
||
}
|
||
},
|
||
|
||
onQuestionChange(e) {
|
||
const index = e.detail.current;
|
||
this.updateQuestionMaterial(index);
|
||
},
|
||
|
||
/** 题目区域内横向滑动时由 question-display 触发(因 catch 导致 swiper 收不到触摸),在此切换题目 */
|
||
onQuestionSwipeHorizontal(e) {
|
||
const dir = e.detail && e.detail.direction;
|
||
if (!dir) return;
|
||
const cur = this.data.currentQuestionIndex;
|
||
const total = (this.data.questions || []).length;
|
||
let next = dir === 'left' ? cur + 1 : cur - 1;
|
||
next = Math.max(0, Math.min(total - 1, next));
|
||
if (next !== cur) {
|
||
this.updateQuestionMaterial(next);
|
||
}
|
||
},
|
||
|
||
showQuestionCard() {
|
||
this.setData({
|
||
showQuestionCard: true
|
||
});
|
||
},
|
||
|
||
hideQuestionCard() {
|
||
this.setData({
|
||
showQuestionCard: false
|
||
});
|
||
},
|
||
|
||
jumpToQuestion(e) {
|
||
const index = parseInt(e.currentTarget.dataset.index) || 0;
|
||
this.updateQuestionMaterial(index);
|
||
this.setData({ showQuestionCard: false });
|
||
},
|
||
|
||
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;
|
||
},
|
||
|
||
showAnswerCard: function () {
|
||
// 实现显示答题卡逻辑
|
||
},
|
||
|
||
nextQuestion: function () {
|
||
if (this.data.currentQuestionIndex < this.data.questions.length - 1) {
|
||
this.updateQuestionMaterial(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;
|
||
|
||
return answers[index].some(answer => answer === true);
|
||
},
|
||
|
||
submitAnswers() {
|
||
// 先关闭自定义答题卡弹窗
|
||
this.setData({
|
||
// showQuestionCard: false,
|
||
isPaused: false
|
||
});
|
||
|
||
// 检查是否有未作答的题目
|
||
const unanswered = this.data.answers.some(arr => !arr || arr.every(v => !v));
|
||
|
||
// 只定义 doSubmit,不直接调用
|
||
const doSubmit = () => {
|
||
if (this.data.pendingSubmit) return;
|
||
this.setData({ pendingSubmit: true });
|
||
|
||
// 获取用户ID
|
||
const userId = wx.getStorageSync('wxuserid');
|
||
if (!userId) {
|
||
wx.showToast({
|
||
title: '请先登录',
|
||
icon: 'none'
|
||
});
|
||
this.setData({ pendingSubmit: false });
|
||
return;
|
||
}
|
||
|
||
// 计算开始时间和结束时间(Unix时间戳,秒)
|
||
const startTimeDate = new Date(this.data.startTime);
|
||
const endTimeDate = new Date();
|
||
const startTimeSeconds = Math.floor(startTimeDate.getTime() / 1000); // 开始答题的时间戳(秒)
|
||
const endTimeSeconds = Math.floor(endTimeDate.getTime() / 1000); // 结束答题的时间戳(秒)
|
||
|
||
// 格式化答案数组
|
||
const answers = this.data.questions.map((question, index) => {
|
||
const answerArray = this.data.answers[index] || [];
|
||
const userAnswer = answerArray.reduce((acc, curr, idx) => {
|
||
if (curr) {
|
||
acc.push(this.data.optionMarkers[idx]);
|
||
}
|
||
return acc;
|
||
}, []).join(',');
|
||
|
||
return {
|
||
question_id: String(question.id),
|
||
user_answer: userAnswer || ''
|
||
};
|
||
});
|
||
|
||
const submitData = {
|
||
user_id: String(userId),
|
||
paper_id: String(this.data.paperId),
|
||
task_id: String(this.data.taskId || ''),
|
||
answers: answers,
|
||
start_time: startTimeSeconds,
|
||
end_time: endTimeSeconds
|
||
};
|
||
|
||
if (!submitData.paper_id) {
|
||
wx.showToast({
|
||
title: '参数错误',
|
||
icon: 'none'
|
||
});
|
||
this.setData({ pendingSubmit: false });
|
||
return;
|
||
}
|
||
|
||
// 先提交答题记录
|
||
questionApi.createAnswerRecord(submitData)
|
||
.then(res => {
|
||
if (res.code === 200 || res.success === true) {
|
||
// 客观题完成只看正确率(正确数/试卷总题数),由后端判断是否达标并保留最高正确率
|
||
const totalQuestions = (this.data.questions && this.data.questions.length) || 0;
|
||
const correctCount = (res.correct_count != null) ? Number(res.correct_count) : 0;
|
||
const progressData = {
|
||
user_id: String(userId),
|
||
task_id: String(this.data.taskId),
|
||
is_completed: true,
|
||
completed_at: String(endTimeSeconds),
|
||
objective_correct_count: correctCount,
|
||
objective_total_count: totalQuestions
|
||
};
|
||
|
||
return campApi.updateCampProgress(progressData)
|
||
.then(progressRes => {
|
||
wx.showToast({
|
||
title: '提交成功',
|
||
icon: 'success'
|
||
});
|
||
console.log('提交成功', this.data);
|
||
// 跳转到结果页面
|
||
setTimeout(() => {
|
||
const query = [];
|
||
query.push('paper_id=' + encodeURIComponent(this.data.paperId));
|
||
query.push('task_id=' + encodeURIComponent(this.data.taskId));
|
||
query.push('camp_id=' + encodeURIComponent(this.data.campId || ''));
|
||
query.push('task_title=' + encodeURIComponent(this.data.taskTitle || '客观题'));
|
||
if (this.data.courseId) {
|
||
query.push('course_id=' + encodeURIComponent(this.data.courseId));
|
||
}
|
||
wx.redirectTo({
|
||
url: '/pages/camp_task_objective_questions_result/index?' + query.join('&')
|
||
});
|
||
}, 1500);
|
||
this.setData({ pendingSubmit: false });
|
||
})
|
||
.catch(progressErr => {
|
||
// 答题记录已提交,但进度更新失败,仍然提示成功
|
||
wx.showToast({
|
||
title: '提交成功',
|
||
icon: 'success'
|
||
});
|
||
console.warn('答题记录提交成功,但进度更新失败:', progressErr);
|
||
// 跳转到结果页面
|
||
setTimeout(() => {
|
||
const query = [];
|
||
query.push('paper_id=' + encodeURIComponent(this.data.paperId));
|
||
query.push('task_id=' + encodeURIComponent(this.data.taskId));
|
||
query.push('camp_id=' + encodeURIComponent(this.data.campId || ''));
|
||
query.push('task_title=' + encodeURIComponent(this.data.taskTitle || '客观题'));
|
||
if (this.data.courseId) {
|
||
query.push('course_id=' + encodeURIComponent(this.data.courseId));
|
||
}
|
||
wx.redirectTo({
|
||
url: '/pages/camp_task_objective_questions_result/index?' + query.join('&')
|
||
});
|
||
}, 1500);
|
||
this.setData({ pendingSubmit: false });
|
||
});
|
||
} else {
|
||
wx.showToast({
|
||
title: res.message || res.msg || '提交失败',
|
||
icon: 'none'
|
||
});
|
||
this.setData({ pendingSubmit: false });
|
||
}
|
||
})
|
||
.catch(err => {
|
||
wx.showToast({
|
||
title: '提交失败',
|
||
icon: 'none'
|
||
});
|
||
this.setData({ pendingSubmit: false });
|
||
console.error('提交答案失败:', err);
|
||
});
|
||
};
|
||
|
||
// 保存 doSubmit 到 this,供自定义弹窗确认按钮调用
|
||
this.doSubmit = doSubmit;
|
||
|
||
if (unanswered) {
|
||
setTimeout(() => {
|
||
this.setData({ showCustomModal: true });
|
||
}, 200);
|
||
} else {
|
||
doSubmit();
|
||
}
|
||
},
|
||
|
||
// 自定义弹窗的确认
|
||
onCustomModalConfirm() {
|
||
this.setData({ showCustomModal: false });
|
||
setTimeout(() => {
|
||
if (typeof this.doSubmit === 'function') {
|
||
this.doSubmit();
|
||
}
|
||
}, 200);
|
||
},
|
||
|
||
// 自定义弹窗的取消
|
||
onCustomModalCancel() {
|
||
this.setData({ showCustomModal: false });
|
||
},
|
||
|
||
startResize(e) {
|
||
const touch = e.touches[0];
|
||
this.startY = touch.clientY;
|
||
this.startHeight = this.data.materialHeight;
|
||
this.setData({ isResizing: true });
|
||
},
|
||
|
||
onResize(e) {
|
||
if (!this.data.isResizing) return;
|
||
const touch = e.touches[0];
|
||
const deltaY = touch.clientY - this.startY;
|
||
let newHeight = this.startHeight + deltaY;
|
||
newHeight = Math.max(this.data.minHeight, Math.min(newHeight, this.data.maxHeight));
|
||
this.setData({ materialHeight: newHeight });
|
||
},
|
||
|
||
endResize() {
|
||
if (!this.data.isResizing) return;
|
||
const presetHeights = [200, 300, 400, 500, 600, 700, 800, 900, 1000];
|
||
const currentHeight = this.data.materialHeight;
|
||
const targetHeight = presetHeights.reduce((prev, curr) =>
|
||
Math.abs(curr - currentHeight) < Math.abs(prev - currentHeight) ? curr : prev
|
||
);
|
||
this.setData({
|
||
materialHeight: targetHeight,
|
||
isResizing: false
|
||
});
|
||
},
|
||
|
||
onUnload() {
|
||
timerService.stopTimer(this);
|
||
},
|
||
|
||
|
||
}); |