873 lines
37 KiB
JavaScript
873 lines
37 KiB
JavaScript
import * as taskService from './modules/task-service.js';
|
||
import * as imageHandler from '../camp_task_subjective_question/modules/image-handler.js';
|
||
import { questionApi } from '../../config/question_api.js';
|
||
import { campApi } from '../../config/camp_api.js';
|
||
|
||
// 兼容:formatDateTime 可能不存在,提供安全封装
|
||
function safeFormatDateTime(v) {
|
||
try {
|
||
if (typeof formatDateTime === 'function') return formatDateTime(v);
|
||
} catch (_) {}
|
||
return v || '';
|
||
}
|
||
|
||
// 导入 formatDateTime(在 safeFormatDateTime 之后,以便在函数内部使用)
|
||
import { formatDateTime } from '../../utils/util';
|
||
|
||
// 格式化日期为 YYYY-MM-DD HH:mm:ss 格式
|
||
function formatDateTimeString(date) {
|
||
var d = date || new Date();
|
||
var year = d.getFullYear();
|
||
var month = String(d.getMonth() + 1).padStart(2, '0');
|
||
var day = String(d.getDate()).padStart(2, '0');
|
||
var hours = String(d.getHours()).padStart(2, '0');
|
||
var minutes = String(d.getMinutes()).padStart(2, '0');
|
||
var seconds = String(d.getSeconds()).padStart(2, '0');
|
||
return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds;
|
||
}
|
||
|
||
// 申论按题批复解析(与管理端约定一致)
|
||
var essayReviewParse = require('./modules/essay-review-parse.js');
|
||
var parseEssayReview = essayReviewParse.parseEssayReview;
|
||
var splitReviewImagesByCounts = essayReviewParse.splitReviewImagesByCounts;
|
||
function getReviewTimeDisplay(progress) {
|
||
var t = progress.review_time || progress.reviewTime;
|
||
if (t && String(t).trim() !== '') return safeFormatDateTime(t);
|
||
var completed = progress.completed_at || progress.completedAt;
|
||
if (!completed || String(completed).trim() === '') return '';
|
||
var str = String(completed).trim();
|
||
if (/^\d+$/.test(str)) return formatDateTimeString(new Date(parseInt(str, 10) * 1000));
|
||
return str;
|
||
}
|
||
|
||
Page({
|
||
data: {
|
||
loading: false,
|
||
materials: [],
|
||
problems: [],
|
||
currentProblemIndex: 0,
|
||
currentProblemTeacherFeedback: null, // 当前题目的老师批复(放在提交按钮下方展示)
|
||
submittedImages: [],
|
||
maxImages: 3,
|
||
paperId: '',
|
||
taskId: '',
|
||
title: '',
|
||
needReview: false,
|
||
swiperHeight: 0,
|
||
// 材料与面板相关(申论题:所有题目共享全部材料)
|
||
currentMaterialIndex: 0,
|
||
currentMaterial: null, // 当前展示的材料对象
|
||
materialTabLabels: [], // 多材料时的 tab 标签
|
||
problemTabLabels: [], // 多问题时的 tab 标签
|
||
materialScrollHeight: 400,
|
||
panelY: 0,
|
||
panelHeight: 400,
|
||
panelYMin: 0,
|
||
panelYMax: 400,
|
||
movableAreaHeight: 600,
|
||
},
|
||
|
||
onLoad(options) {
|
||
var that = this;
|
||
|
||
// 计算主内容区高度、问题面板高度、拖动边界(与客观题页面一致)
|
||
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) {
|
||
}
|
||
|
||
var eventChannel = this.getOpenerEventChannel();
|
||
|
||
if (!eventChannel) {
|
||
return;
|
||
}
|
||
|
||
eventChannel.on('sendParams', function(params) {
|
||
|
||
wx.setNavigationBarTitle({
|
||
title: params.taskTitle || '申论题'
|
||
});
|
||
|
||
that.setData({
|
||
campId: String(params.campId),
|
||
courseId: String(params.courseId),
|
||
taskId: String(params.taskId),
|
||
paperId: params.paperId ? String(params.paperId) : ''
|
||
}, function(){
|
||
that.getTaskDetail();
|
||
});
|
||
});
|
||
},
|
||
|
||
|
||
// 获取任务详情
|
||
getTaskDetail: function() {
|
||
var that = this;
|
||
var taskId = this.data.taskId;
|
||
this.setData({ loading: true });
|
||
|
||
taskService.getTaskDetail(taskId).then(function(taskData){
|
||
if (!taskData || !taskData.paperId) {
|
||
that.setData({ loading: false });
|
||
wx.showToast({
|
||
title: '获取任务详情失败',
|
||
icon: 'none'
|
||
});
|
||
return;
|
||
}
|
||
|
||
that.setData({
|
||
paperId: taskData.paperId,
|
||
needReview: taskData.needReview
|
||
});
|
||
|
||
if (taskData.paperId) {
|
||
that.fetchPaperData(taskData.paperId);
|
||
} else {
|
||
wx.showToast({
|
||
title: '试卷ID不存在',
|
||
icon: 'none'
|
||
});
|
||
that.setData({ loading: false });
|
||
}
|
||
}).finally(function(){
|
||
var userId = wx.getStorageSync('wxuserid');
|
||
if (userId && taskId) {
|
||
taskService.fetchProgressSilent(userId, taskId)
|
||
.then(function(progressRes){
|
||
if (progressRes && progressRes.success === true && progressRes.progress) {
|
||
if (!that.data.problems || that.data.problems.length === 0) {
|
||
that._pendingProgress = progressRes.progress;
|
||
} else {
|
||
that.handleUserProgress(progressRes.progress);
|
||
}
|
||
}
|
||
})
|
||
.catch(function(err){
|
||
});
|
||
}
|
||
});
|
||
},
|
||
|
||
// 获取试卷内容
|
||
fetchPaperData: function(paperId) {
|
||
var that = this;
|
||
taskService.fetchPaperData(paperId).then(function(paperData){
|
||
var problems = paperData.problems.map(function(p) {
|
||
return Object.assign({}, p, {
|
||
auditText: that.data.needReview ? '需要审核' : '不需要审核',
|
||
auditTextStyle: that.data.needReview ? 'need' : 'no-need'
|
||
});
|
||
});
|
||
|
||
if (problems.length === 0) {
|
||
wx.showToast({
|
||
title: '暂无申论题目',
|
||
icon: 'none'
|
||
});
|
||
}
|
||
|
||
// 处理 paper 级别的材料(申论题:所有题目共享全部材料)
|
||
var materials = paperData.materials || [];
|
||
var h = that.data.swiperHeight || 400;
|
||
var w = 375;
|
||
try { w = wx.getSystemInfoSync().windowWidth; } catch (e) {}
|
||
var titleBarPx = materials.length > 1
|
||
? Math.round(140 * (w / 750))
|
||
: Math.round(100 * (w / 750));
|
||
var materialScrollHeight = materials.length > 0 ? Math.max(0, h - titleBarPx) : h;
|
||
var panelYMin = that.data.panelYMin || Math.round(160 * (375 / 750));
|
||
var panelHeight = Math.max(200, h - panelYMin);
|
||
var panelYMax = Math.max(panelYMin, h - 80);
|
||
var movableAreaHeight = panelYMax + panelHeight;
|
||
var defaultPanelY = Math.max(panelYMin, Math.min(panelYMax, Math.round(h / 2)));
|
||
// 申论题:有材料时面板默认半屏,无材料时顶部
|
||
var hasMaterials = materials.length > 0;
|
||
var actualPanelY = hasMaterials ? defaultPanelY : 0;
|
||
// 多材料时生成 tab 标签
|
||
var tabNames = ['资料一', '资料二', '资料三', '资料四', '资料五', '资料六', '资料七', '资料八', '资料九', '资料十'];
|
||
var materialTabLabels = materials.length > 1
|
||
? materials.map(function(_, i) { return tabNames[i] || ('资料' + (i + 1)); })
|
||
: [];
|
||
// 多问题时生成问题 tab 标签
|
||
var problemTabLabels = problems.length > 1
|
||
? problems.map(function(_, i) { return '问题' + (i + 1); })
|
||
: [];
|
||
|
||
that.setData({
|
||
problems: problems,
|
||
materials: materials,
|
||
currentMaterialIndex: 0,
|
||
currentMaterial: hasMaterials ? materials[0] : null,
|
||
currentProblemTeacherFeedback: (problems[0] && problems[0].teacherFeedback) || null,
|
||
materialTabLabels: materialTabLabels,
|
||
problemTabLabels: problemTabLabels,
|
||
materialScrollHeight: materialScrollHeight,
|
||
panelHeight: panelHeight,
|
||
panelY: actualPanelY,
|
||
panelYMin: panelYMin,
|
||
panelYMax: panelYMax,
|
||
movableAreaHeight: movableAreaHeight,
|
||
title: paperData.title,
|
||
loading: false
|
||
}, function(){
|
||
if (that._pendingProgress) {
|
||
that.handleUserProgress(that._pendingProgress);
|
||
that._pendingProgress = null;
|
||
}
|
||
});
|
||
}).catch(function(err){
|
||
wx.showToast({
|
||
title: '获取题目失败',
|
||
icon: 'none'
|
||
});
|
||
that.setData({ loading: false });
|
||
});
|
||
},
|
||
|
||
// 处理用户进度(参考主观题页面)
|
||
handleUserProgress: function(progress) {
|
||
var that = this;
|
||
try {
|
||
var problems = that.data.problems || [];
|
||
|
||
// 如果 problems 为空,说明试卷数据还没加载完,延迟处理
|
||
if (problems.length === 0) {
|
||
// 延迟 500ms 后重试
|
||
setTimeout(function() {
|
||
that.handleUserProgress(progress);
|
||
}, 500);
|
||
return;
|
||
}
|
||
|
||
// 解析申论题按题目维度的答案(优先)或旧版整份 answer_images
|
||
var essayAnswerImages = null;
|
||
if (Array.isArray(progress.essay_answer_images) && progress.essay_answer_images.length > 0) {
|
||
essayAnswerImages = progress.essay_answer_images;
|
||
}
|
||
var legacySubmittedImages = [];
|
||
if (Array.isArray(progress.answer_images)) {
|
||
legacySubmittedImages = progress.answer_images;
|
||
} else if (typeof progress.answer_images === 'string') {
|
||
try { legacySubmittedImages = JSON.parse(progress.answer_images) || []; } catch(_) { legacySubmittedImages = []; }
|
||
}
|
||
|
||
// 解析批复图片:可能是数组、JSON 字符串或空对象
|
||
var reviewImages = [];
|
||
if (Array.isArray(progress.review_images)) {
|
||
reviewImages = progress.review_images;
|
||
} else if (typeof progress.review_images === 'string') {
|
||
try {
|
||
reviewImages = JSON.parse(progress.review_images) || [];
|
||
} catch(_) {
|
||
reviewImages = [];
|
||
}
|
||
} else if (progress.review_images && typeof progress.review_images === 'object') {
|
||
try {
|
||
reviewImages = Object.values(progress.review_images).filter(function(item) {
|
||
return typeof item === 'string' && item.length > 0;
|
||
});
|
||
} catch(_) {
|
||
reviewImages = [];
|
||
}
|
||
}
|
||
|
||
var reviewTime = getReviewTimeDisplay(progress);
|
||
var currentNeedReview = that.data.needReview;
|
||
if (progress.need_review !== undefined) {
|
||
currentNeedReview = !!progress.need_review;
|
||
}
|
||
// 申论按题批复:解析 review_comment 与 review_images,得到每题评语与每题图片
|
||
var perQuestionComments = [];
|
||
var perQuestionImages = [];
|
||
var parsed = parseEssayReview(progress);
|
||
if (parsed.comments && parsed.comments.length > 0) {
|
||
perQuestionComments = parsed.comments;
|
||
}
|
||
if (parsed.imageCounts && parsed.imageCounts.length > 0 && reviewImages.length > 0) {
|
||
perQuestionImages = splitReviewImagesByCounts(reviewImages, parsed.imageCounts);
|
||
}
|
||
|
||
var hasSubmitted = (essayAnswerImages && essayAnswerImages.length > 0) || legacySubmittedImages.length > 0 || (progress.is_completed === true || progress.is_completed === 1);
|
||
|
||
// 按题目应用答案:有 essay_answer_images 则每题用对应下标;否则沿用旧逻辑(整份答案应用到所有题)
|
||
var problemsData = problems.map(function(problem, idx) {
|
||
var submittedImagesArray = [];
|
||
if (essayAnswerImages && Array.isArray(essayAnswerImages[idx])) {
|
||
submittedImagesArray = essayAnswerImages[idx].map(function(url, i) {
|
||
return { id: Date.now() + idx * 1000 + i, path: url };
|
||
});
|
||
} else if (legacySubmittedImages.length > 0) {
|
||
submittedImagesArray = legacySubmittedImages.map(function(url, i) {
|
||
return { id: Date.now() + i, path: url };
|
||
});
|
||
}
|
||
var finalTempImages = hasSubmitted ? [] : (problem.tempImages || []);
|
||
var feedbackComment = (perQuestionComments[idx] !== undefined ? perQuestionComments[idx] : '') || (perQuestionComments.length === 0 ? (progress.review_comment || '') : '');
|
||
var feedbackImages = (perQuestionImages[idx] !== undefined ? perQuestionImages[idx] : []) || (perQuestionImages.length === 0 ? reviewImages : []);
|
||
return {
|
||
...problem,
|
||
submittedImages: submittedImagesArray,
|
||
tempImages: finalTempImages,
|
||
totalImageCount: submittedImagesArray.length + finalTempImages.length,
|
||
teacherFeedback: {
|
||
comment: feedbackComment,
|
||
images: feedbackImages,
|
||
review_time: reviewTime
|
||
},
|
||
allowResubmit: hasSubmitted ? false : true,
|
||
auditText: that.getAuditText(progress.review_status, currentNeedReview),
|
||
auditTextStyle: that.getAuditTextStyle(progress.review_status, currentNeedReview)
|
||
};
|
||
});
|
||
|
||
// 更新 needReview 数据
|
||
var idx = that.data.currentProblemIndex || 0;
|
||
var updateData = {
|
||
problems: problemsData,
|
||
currentProblemTeacherFeedback: (problemsData[idx] && problemsData[idx].teacherFeedback) || null
|
||
};
|
||
if (progress.need_review !== undefined) {
|
||
updateData.needReview = !!progress.need_review;
|
||
}
|
||
|
||
that.setData(updateData, function() {
|
||
});
|
||
} catch (error) {
|
||
}
|
||
},
|
||
|
||
// 获取审核文本
|
||
getAuditText: function(reviewStatus, needReview) {
|
||
if (!needReview) {
|
||
return '不需要审核';
|
||
}
|
||
// 规范化审核状态(支持多种格式)
|
||
var normalizedStatus = String(reviewStatus || '').toLowerCase();
|
||
if (normalizedStatus === 'approved' || normalizedStatus === 'review_status_approved') {
|
||
return '审核通过';
|
||
} else if (normalizedStatus === 'rejected' || normalizedStatus === 'review_status_rejected') {
|
||
return '已驳回';
|
||
} else if (normalizedStatus === 'pending' || normalizedStatus === 'review_status_pending') {
|
||
return '等待审核';
|
||
}
|
||
return '需要审核';
|
||
},
|
||
|
||
// 获取审核文本样式
|
||
getAuditTextStyle: function(reviewStatus, needReview) {
|
||
if (!needReview) {
|
||
return 'no-need';
|
||
}
|
||
// 规范化审核状态(支持多种格式)
|
||
var normalizedStatus = String(reviewStatus || '').toLowerCase();
|
||
if (normalizedStatus === 'approved' || normalizedStatus === 'review_status_approved') {
|
||
return 'no-need';
|
||
}
|
||
return 'need';
|
||
},
|
||
|
||
|
||
/**
|
||
* 切换题目时更新面板位置(申论题:材料不随题目变化,所有题目共享全部材料)
|
||
*/
|
||
updateQuestionMaterial(questionIndex) {
|
||
var hasMaterials = this.data.materials && this.data.materials.length > 0;
|
||
var panelYMin = this.data.panelYMin;
|
||
var panelYMax = this.data.panelYMax;
|
||
var swiperHeight = this.data.swiperHeight;
|
||
var defaultPanelY = Math.max(panelYMin, Math.min(panelYMax, Math.round(swiperHeight / 2)));
|
||
var newPanelY = hasMaterials ? this.data.panelY : 0;
|
||
var problems = this.data.problems || [];
|
||
var feedback = (problems[questionIndex] && problems[questionIndex].teacherFeedback) || null;
|
||
this.setData({
|
||
currentProblemIndex: questionIndex,
|
||
currentProblemTeacherFeedback: feedback,
|
||
panelY: newPanelY
|
||
});
|
||
},
|
||
|
||
/**
|
||
* 切换材料 tab(申论题支持多材料)
|
||
*/
|
||
onMaterialTabTap(e) {
|
||
var index = e.currentTarget.dataset.index;
|
||
var materials = this.data.materials || [];
|
||
if (index >= 0 && index < materials.length) {
|
||
this.setData({
|
||
currentMaterialIndex: index,
|
||
currentMaterial: materials[index]
|
||
});
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 点击问题 tab 切换问题
|
||
*/
|
||
onProblemTabTap(e) {
|
||
var index = e.currentTarget.dataset.index;
|
||
var problems = this.data.problems || [];
|
||
if (index >= 0 && index < problems.length) {
|
||
var feedback = (problems[index] && problems[index].teacherFeedback) || null;
|
||
this.setData({
|
||
currentProblemIndex: index,
|
||
currentProblemTeacherFeedback: feedback
|
||
});
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 材料 swiper 左右滑动切换
|
||
*/
|
||
onMaterialSwiperChange(e) {
|
||
var source = e.detail.source;
|
||
// 只处理用户手势触发的切换,避免 setData 导致的循环
|
||
if (source !== 'touch' && source !== 'autoplay') return;
|
||
var index = e.detail.current;
|
||
var materials = this.data.materials || [];
|
||
if (index >= 0 && index < materials.length) {
|
||
this.setData({
|
||
currentMaterialIndex: index,
|
||
currentMaterial: materials[index]
|
||
});
|
||
}
|
||
},
|
||
|
||
// 面板拖拽相关方法(与客观题页面一致)
|
||
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;
|
||
var panelYMin = this.data.panelYMin;
|
||
var panelYMax = this.data.panelYMax;
|
||
var deltaY = e.touches[0].clientY - this._panelDragStartY;
|
||
var newY = this._panelDragStartPanelY + deltaY;
|
||
newY = Math.max(panelYMin, Math.min(panelYMax, newY));
|
||
this.setData({ panelY: newY });
|
||
},
|
||
|
||
onPanelDragEnd(e) {
|
||
this._panelDragStartY = null;
|
||
this._panelDragStartPanelY = null;
|
||
},
|
||
|
||
// 预览图片(参考主观题页面)
|
||
previewImage(e) {
|
||
const { url, problemIndex } = e.currentTarget.dataset;
|
||
const currentProblem = this.data.problems[problemIndex];
|
||
if (!currentProblem) {
|
||
return;
|
||
}
|
||
// 优先使用临时图片,如果没有则使用已提交的图片
|
||
const images = currentProblem.tempImages && currentProblem.tempImages.length > 0 ?
|
||
currentProblem.tempImages :
|
||
(currentProblem.submittedImages || []).map(img => img.path || img);
|
||
|
||
wx.previewImage({
|
||
current: url,
|
||
urls: images
|
||
});
|
||
},
|
||
|
||
// 删除图片(参考主观题页面)
|
||
deleteImage(e) {
|
||
const { index, problemIndex } = e.currentTarget.dataset;
|
||
const problems = [...this.data.problems];
|
||
const currentProblem = problems[problemIndex];
|
||
|
||
if (!currentProblem) {
|
||
return;
|
||
}
|
||
|
||
// 优先删除临时图片
|
||
if (currentProblem.tempImages && currentProblem.tempImages.length > 0) {
|
||
const tempImages = currentProblem.tempImages.slice();
|
||
tempImages.splice(index, 1);
|
||
currentProblem.tempImages = tempImages;
|
||
// 更新图片总数
|
||
currentProblem.totalImageCount = tempImages.length + (currentProblem.submittedImages || []).length;
|
||
} else if (currentProblem.submittedImages && currentProblem.submittedImages.length > 0) {
|
||
// 如果已提交,则不允许删除(或者需要重新提交)
|
||
wx.showToast({
|
||
title: '已提交的图片不能删除',
|
||
icon: 'none'
|
||
});
|
||
return;
|
||
}
|
||
|
||
this.setData({
|
||
problems
|
||
});
|
||
},
|
||
|
||
// 选择图片(参考主观题页面)
|
||
chooseImage(e) {
|
||
const { problemIndex } = e.currentTarget.dataset;
|
||
const problems = [...this.data.problems];
|
||
const currentProblem = problems[problemIndex];
|
||
|
||
if (!currentProblem) {
|
||
return;
|
||
}
|
||
|
||
// 计算当前图片总数(临时图片 + 已提交图片)
|
||
const tempCount = (currentProblem.tempImages || []).length;
|
||
const submittedCount = (currentProblem.submittedImages || []).length;
|
||
const currentCount = tempCount + submittedCount;
|
||
const remainCount = 6 - currentCount;
|
||
|
||
if (remainCount <= 0) {
|
||
wx.showToast({
|
||
title: '最多只能上传6张图片',
|
||
icon: 'none'
|
||
});
|
||
return;
|
||
}
|
||
|
||
wx.chooseMedia({
|
||
count: remainCount,
|
||
mediaType: ['image'],
|
||
sizeType: ['compressed'],
|
||
sourceType: ['album', 'camera'],
|
||
success: (res) => {
|
||
const newImages = res.tempFiles.map(file => file.tempFilePath);
|
||
const tempImages = (currentProblem.tempImages || []).concat(newImages);
|
||
currentProblem.tempImages = tempImages;
|
||
currentProblem.allowResubmit = true;
|
||
// 更新图片总数
|
||
currentProblem.totalImageCount = tempImages.length + (currentProblem.submittedImages || []).length;
|
||
this.setData({ problems });
|
||
}
|
||
});
|
||
},
|
||
|
||
// 统一提交全部题目答案(已废弃:改为每题单独提交)
|
||
submitAllAnswers() {
|
||
wx.showToast({ title: '请使用每题下方的「提交」按钮', icon: 'none' });
|
||
},
|
||
|
||
// 单题提交 / 前往下一题:暂存当前题并可选切到下一题
|
||
submitOneQuestion(e) {
|
||
var that = this;
|
||
var problemIndex = parseInt(e.currentTarget.dataset.problemIndex, 10);
|
||
var isLast = e.currentTarget.dataset.isLast === 'true' || e.currentTarget.dataset.isLast === true;
|
||
var problems = this.data.problems || [];
|
||
if (problemIndex < 0 || problemIndex >= problems.length) return;
|
||
var currentProblem = problems[problemIndex];
|
||
var submitted = (currentProblem.submittedImages || []).map(function(img) { return img.path || img; });
|
||
var temp = currentProblem.tempImages || [];
|
||
if (submitted.length === 0 && temp.length === 0) {
|
||
wx.showToast({ title: '请先上传图片', icon: 'none' });
|
||
return;
|
||
}
|
||
|
||
// 构建每题答案:当前题用待上传的 temp 或已提交;其他题用已提交(可为空,后端会与已有进度合并)
|
||
var perQuestionUrls = [];
|
||
var perQuestionTempPaths = [];
|
||
for (var i = 0; i < problems.length; i++) {
|
||
var p = problems[i];
|
||
var sub = (p.submittedImages || []).map(function(img) { return img.path || img; });
|
||
var t = p.tempImages || [];
|
||
if (i === problemIndex) {
|
||
if (temp.length > 0) {
|
||
perQuestionUrls[i] = [];
|
||
perQuestionTempPaths[i] = temp;
|
||
} else {
|
||
perQuestionUrls[i] = submitted;
|
||
perQuestionTempPaths[i] = [];
|
||
}
|
||
} else {
|
||
perQuestionUrls[i] = sub;
|
||
perQuestionTempPaths[i] = [];
|
||
}
|
||
}
|
||
|
||
wx.showLoading({ title: '提交中...', mask: true });
|
||
|
||
var uploadPromises = [];
|
||
perQuestionTempPaths.forEach(function(paths, qIdx) {
|
||
if (paths.length === 0) return;
|
||
uploadPromises.push(
|
||
Promise.all(paths.map(function(tempPath) { return that.uploadImage(tempPath); }))
|
||
.then(function(urls) {
|
||
perQuestionUrls[qIdx] = urls;
|
||
})
|
||
);
|
||
});
|
||
|
||
(uploadPromises.length > 0 ? Promise.all(uploadPromises) : Promise.resolve())
|
||
.then(function() {
|
||
var essayAnswerImages = perQuestionUrls.map(function(urls) { return urls || []; });
|
||
var userId = String(wx.getStorageSync('wxuserid') || '');
|
||
var taskIdStr = String(that.data.taskId);
|
||
return taskService.submitEssayAnswer(userId, taskIdStr, essayAnswerImages);
|
||
})
|
||
.then(function(result) {
|
||
if ((result && result.code === 200) || (result && result.success === true)) {
|
||
var problemsData = problems.map(function(p, idx) {
|
||
var urls = perQuestionUrls[idx] || [];
|
||
var newTemp = idx === problemIndex ? [] : (p.tempImages || []);
|
||
return {
|
||
...p,
|
||
submittedImages: urls.map(function(url, i) { return { id: Date.now() + idx * 1000 + i, path: url }; }),
|
||
tempImages: newTemp,
|
||
totalImageCount: urls.length + newTemp.length,
|
||
allowResubmit: false,
|
||
auditText: that.data.needReview ? '等待审核' : that.getAuditText('', that.data.needReview),
|
||
auditTextStyle: that.data.needReview ? 'need' : 'no-need'
|
||
};
|
||
});
|
||
that.setData({
|
||
problems: problemsData,
|
||
currentProblemTeacherFeedback: (problemsData[that.data.currentProblemIndex] && problemsData[that.data.currentProblemIndex].teacherFeedback) || null
|
||
}, function() {
|
||
if (!isLast) {
|
||
that.updateQuestionMaterial(problemIndex + 1);
|
||
wx.showToast({ title: '已提交,请继续作答下一题', icon: 'none', duration: 2000 });
|
||
} else {
|
||
wx.showToast({ title: '提交成功', icon: 'success' });
|
||
}
|
||
});
|
||
} else {
|
||
throw new Error((result && (result.message || result.msg)) || '提交失败');
|
||
}
|
||
})
|
||
.catch(function(error) {
|
||
wx.showToast({
|
||
title: error.message || '提交失败,请重试',
|
||
icon: 'none'
|
||
});
|
||
})
|
||
.finally(function() {
|
||
wx.hideLoading();
|
||
});
|
||
},
|
||
|
||
// 上传单张图片
|
||
uploadImage(tempFilePath) {
|
||
return imageHandler.uploadImage(tempFilePath, this.data.taskId, 'essay');
|
||
},
|
||
|
||
// 处理答案输入
|
||
handleInput(e) {
|
||
const { index } = e.currentTarget.dataset;
|
||
const { value } = e.detail;
|
||
const answers = [...this.data.answers];
|
||
answers[index] = value;
|
||
this.setData({ answers });
|
||
},
|
||
|
||
// 上一题
|
||
prevProblem() {
|
||
if (this.data.currentProblemIndex > 0) {
|
||
this.setData({
|
||
currentProblemIndex: this.data.currentProblemIndex - 1,
|
||
currentMaterialIndex: 0
|
||
});
|
||
}
|
||
},
|
||
|
||
// 下一题
|
||
nextProblem() {
|
||
if (this.data.currentProblemIndex < this.data.problems.length - 1) {
|
||
this.setData({
|
||
currentProblemIndex: this.data.currentProblemIndex + 1,
|
||
currentMaterialIndex: 0
|
||
});
|
||
}
|
||
},
|
||
|
||
// 题目切换
|
||
onQuestionChange(e) {
|
||
const index = e.detail.current;
|
||
this.updateQuestionMaterial(index);
|
||
},
|
||
|
||
// 题目滑动过渡
|
||
onQuestionTransition() {
|
||
// 保持为空
|
||
},
|
||
|
||
// 题目滑动动画完成
|
||
onQuestionAnimationFinish(e) {
|
||
const index = e.detail.current;
|
||
this.updateQuestionMaterial(index);
|
||
},
|
||
|
||
|
||
startTimer() {
|
||
this.setData({
|
||
startTime: Date.now()
|
||
});
|
||
this.updateTimer();
|
||
},
|
||
|
||
updateTimer() {
|
||
const now = Date.now();
|
||
const diff = Math.floor((now - this.data.startTime) / 1000);
|
||
|
||
const hours = Math.floor(diff / 3600);
|
||
const minutes = Math.floor((diff % 3600) / 60);
|
||
const seconds = diff % 60;
|
||
|
||
const formattedTime = `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
|
||
|
||
this.setData({ formattedTime });
|
||
|
||
setTimeout(() => {
|
||
this.updateTimer();
|
||
}, 1000);
|
||
},
|
||
|
||
showAnswerCard() {
|
||
this.setData({ showAnswerCard: true });
|
||
},
|
||
|
||
hideAnswerCard() {
|
||
this.setData({ showAnswerCard: false });
|
||
},
|
||
|
||
jumpToQuestion(e) {
|
||
const index = e.currentTarget.dataset.index;
|
||
this.setData({ showAnswerCard: false }, () => {
|
||
this.updateQuestionMaterial(index);
|
||
});
|
||
},
|
||
|
||
// 处理提交答案按钮点击
|
||
handleSubmitAnswer() {
|
||
this.setData({
|
||
showUploadOptions: true
|
||
});
|
||
},
|
||
|
||
// 隐藏上传选项
|
||
hideUploadOptions() {
|
||
this.setData({
|
||
showUploadOptions: false
|
||
});
|
||
},
|
||
|
||
// 处理拍照
|
||
handleTakePhoto() {
|
||
wx.chooseMedia({
|
||
count: 1,
|
||
mediaType: ['image'],
|
||
sourceType: ['camera'],
|
||
camera: 'back',
|
||
success: (res) => {
|
||
this.handleImageUpload(res.tempFiles[0].tempFilePath);
|
||
},
|
||
fail: (err) => {
|
||
wx.showToast({
|
||
title: '拍照失败',
|
||
icon: 'none'
|
||
});
|
||
}
|
||
});
|
||
},
|
||
|
||
// 处理从相册选择
|
||
handleChooseImage() {
|
||
wx.chooseMedia({
|
||
count: 1,
|
||
mediaType: ['image'],
|
||
sourceType: ['album'],
|
||
success: (res) => {
|
||
this.handleImageUpload(res.tempFiles[0].tempFilePath);
|
||
},
|
||
fail: (err) => {
|
||
wx.showToast({
|
||
title: '选择图片失败',
|
||
icon: 'none'
|
||
});
|
||
}
|
||
});
|
||
},
|
||
|
||
// 处理图片上传
|
||
handleImageUpload(tempFilePath) {
|
||
wx.showLoading({
|
||
title: '正在上传...',
|
||
mask: true
|
||
});
|
||
|
||
// 这里添加实际的上传逻辑
|
||
// 示例:将图片保存到本地数据中
|
||
const answers = [...this.data.currentProblemAnswers];
|
||
answers[this.data.currentProblemIndex] = tempFilePath;
|
||
|
||
this.setData({
|
||
currentProblemAnswers: answers,
|
||
showUploadOptions: false
|
||
});
|
||
|
||
wx.hideLoading();
|
||
wx.showToast({
|
||
title: '提交成功',
|
||
icon: 'success'
|
||
});
|
||
|
||
},
|
||
|
||
|
||
// 重新提交(参考主观题页面)
|
||
handleResubmit(e) {
|
||
const { problemIndex } = e.currentTarget.dataset;
|
||
const problems = [...this.data.problems];
|
||
const currentProblem = problems[problemIndex];
|
||
|
||
if (!currentProblem) {
|
||
return;
|
||
}
|
||
|
||
// 清空已提交图片,允许重新上传
|
||
currentProblem.submittedImages = [];
|
||
currentProblem.tempImages = [];
|
||
currentProblem.totalImageCount = 0;
|
||
currentProblem.allowResubmit = true;
|
||
this.setData({ problems });
|
||
},
|
||
|
||
/**
|
||
* 用户点击右上角分享
|
||
*/
|
||
onShareAppMessage: function () {
|
||
|
||
},
|
||
|
||
// 老师批复区域图片预览
|
||
previewTeacherImage(e) {
|
||
const { url } = e.currentTarget.dataset;
|
||
const feedback = this.data.currentProblemTeacherFeedback || (this.data.problems[this.data.currentProblemIndex] && this.data.problems[this.data.currentProblemIndex].teacherFeedback);
|
||
const teacherImages = (feedback && feedback.images) || [];
|
||
if (teacherImages.length === 0) return;
|
||
wx.previewImage({
|
||
urls: teacherImages,
|
||
current: url,
|
||
success: () => {},
|
||
fail: () => {}
|
||
});
|
||
},
|
||
|
||
}); |