feat: 综合大题改造(替代 Word 上传)+ MockExam 按组分支
中职/高职组以「综合大题」替代原 Word 文件上传:管理员在后台录入题干 (wangEditor 富文本,含表格),系统按组随机抽题,考生在 3 个小问内作答, 每小题 10 分、大题合计 30 分。 - api/admin.js / api/exam.js / stores/exam.js:大题题库 CRUD、抽题、作答与评分链路 - Competition.vue:Word 上传区块替换为大题作答区(3 小问 + 字数上限 + 富文本题干渲染) - Admin.vue:大题题库管理、考生作答查看与人工评分 - MockExam.vue:按组分支——中职/高职显示综合大题,其余组保持 Word 答题;侧栏与提交提示同步 - RichEditor.vue + utils/richtext.js:题干富文本渲染前做白名单消毒(零依赖,纵深防御) 说明:big_questions / big_question_answers 建表迁移 023/024 随后端仓库提交。
This commit is contained in:
@@ -347,4 +347,92 @@ export function resetTestData() {
|
||||
return request.post('/admin/reset-test-data')
|
||||
}
|
||||
|
||||
// ==================== 综合大题管理(中职/高职) ====================
|
||||
|
||||
/**
|
||||
* 获取综合大题题库(按组别)
|
||||
*
|
||||
* 仅中职/高职启用;本科/教师组调用返回 400(未启用综合大题)。
|
||||
* @param {string} group - 组别标识(zhongzhi / gaozhi)
|
||||
* @returns {Promise} {
|
||||
* list: [{ id, questionUid, group, title, description,
|
||||
* subPrompt1, subPrompt2, subPrompt3,
|
||||
* scorePerSub, totalScore, sortOrder, status, createdAt, updatedAt }],
|
||||
* total, scorePerSub, subCount, maxChars
|
||||
* }
|
||||
*/
|
||||
export function getBigQuestions(group) {
|
||||
return request.get(`/admin/big-questions/${group}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增大题
|
||||
*
|
||||
* 题干 description 为富文本 HTML(支持表格);三小问提示 subPrompt1~3 与题干同存一行。
|
||||
* @param {string} group - 组别标识
|
||||
* @param {object} data - { title, description, subPrompt1, subPrompt2, subPrompt3, scorePerSub, sortOrder, status }
|
||||
* @returns {Promise} { id, questionUid }
|
||||
*/
|
||||
export function createBigQuestion(group, data) {
|
||||
return request.post(`/admin/big-questions/${group}`, data)
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新大题
|
||||
*
|
||||
* 注意:已被考生抽取的题目仍可修改,但考生端展示的是抽题时刻的题干快照,
|
||||
* 不会受此修改影响(防纠纷设计)。
|
||||
* @param {string} group - 组别标识
|
||||
* @param {number|string} id - 大题 ID
|
||||
* @param {object} data - 同 createBigQuestion
|
||||
* @returns {Promise} { id }
|
||||
*/
|
||||
export function updateBigQuestion(group, id, data) {
|
||||
return request.put(`/admin/big-questions/${group}/${id}`, data)
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除大题
|
||||
*
|
||||
* 已被考生抽取的题目服务端拒绝删除(返回 409),避免作答记录悬空。
|
||||
* @param {string} group - 组别标识
|
||||
* @param {number|string} id - 大题 ID
|
||||
* @returns {Promise} { deleted }
|
||||
*/
|
||||
export function deleteBigQuestion(group, id) {
|
||||
return request.delete(`/admin/big-questions/${group}/${id}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取大题作答与查证列表(覆盖该组全部考生,含未抽题者)
|
||||
*
|
||||
* 每行含考生信息、抽题快照、三小问答案、已答数量、三小问得分、大题总分与提交时间,
|
||||
* 供管理员核对考生实际抽到的题目与作答内容,避免考生纠纷。
|
||||
* @param {string} group - 组别标识
|
||||
* @returns {Promise} {
|
||||
* list: [{ contestantId, aggregationUid, name, idNumber, phone, teamId,
|
||||
* drawn, bigQuestionUid, questionSnapshot,
|
||||
* answer1, answer2, answer3, answeredCount,
|
||||
* score1, score2, score3, bigScore, wordScore, submitTime }],
|
||||
* total, drawnCount
|
||||
* }
|
||||
*/
|
||||
export function getBigAnswers(group) {
|
||||
return request.get(`/admin/big-answers/${group}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 录入三小问得分(小题分)
|
||||
*
|
||||
* 服务端逐问校验 0 ~ scorePerSub 区间,累加后经唯一评分写路径
|
||||
* service.admin.updateScores 落 contestants.word_score(中职/高职总分 = exam_score + word_score)。
|
||||
* @param {string} group - 组别标识
|
||||
* @param {number|string} uid - 考生聚合 ID(aggregation_uid)
|
||||
* @param {{score_1: number, score_2: number, score_3: number}} scores
|
||||
* @returns {Promise} { total, perSubScore }
|
||||
*/
|
||||
export function setBigAnswerScores(group, uid, scores) {
|
||||
return request.put(`/admin/big-answers/${group}/${uid}/scores`, scores)
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user