继续考试功能已实现
This commit is contained in:
parent
681e3fe273
commit
08bf170216
@ -772,9 +772,10 @@ async function startPaper(paperId) {
|
||||
/**
|
||||
* 提交考试
|
||||
* @param {number} paperId - 试卷ID
|
||||
* @param {number} duration_seconds - 答题时长(秒)
|
||||
* @returns {Promise<Object>} - 包含操作结果和试卷数据的对象
|
||||
*/
|
||||
async function submitPaper(paperId) {
|
||||
async function submitPaper(paperId, duration_seconds = 0) {
|
||||
try {
|
||||
const userDb = await getDbConnection(getUserDbPath());
|
||||
const currentTime = formatDateTime(new Date());
|
||||
@ -783,9 +784,10 @@ async function submitPaper(paperId) {
|
||||
`UPDATE examinee_papers
|
||||
SET paper_status = 2,
|
||||
paper_submit_time = ?,
|
||||
paper_end_time = ?
|
||||
paper_end_time = ?,
|
||||
paper_duration_seconds = ?
|
||||
WHERE id = ?`,
|
||||
[currentTime, currentTime, paperId]
|
||||
[currentTime, currentTime, duration_seconds, paperId]
|
||||
);
|
||||
|
||||
// 查询更新后的试卷数据
|
||||
@ -819,12 +821,23 @@ async function endPaper(paperId) {
|
||||
const userDb = await getDbConnection(getUserDbPath());
|
||||
const currentTime = formatDateTime(new Date());
|
||||
|
||||
// 先查询当前试卷的paper_minutes
|
||||
const existingPaper = await userDb.getAsync(
|
||||
`SELECT paper_minutes FROM examinee_papers WHERE id = ?`,
|
||||
[paperId]
|
||||
);
|
||||
|
||||
// 计算paper_duration_seconds (将分钟转换为秒)
|
||||
const paperMinutes = existingPaper ? parseInt(existingPaper.paper_minutes) : 0;
|
||||
const paperDurationSeconds = paperMinutes > 0 ? paperMinutes * 60 : 0;
|
||||
|
||||
await userDb.runAsync(
|
||||
`UPDATE examinee_papers
|
||||
SET paper_status = 2,
|
||||
paper_end_time = ?
|
||||
paper_end_time = ?,
|
||||
paper_duration_seconds = ?
|
||||
WHERE id = ?`,
|
||||
[currentTime, paperId]
|
||||
[currentTime, paperDurationSeconds, paperId]
|
||||
);
|
||||
|
||||
// 查询更新后的试卷数据
|
||||
@ -851,18 +864,20 @@ async function endPaper(paperId) {
|
||||
/**
|
||||
* 更新试卷最后操作时间
|
||||
* @param {number} paperId - 试卷ID
|
||||
* @param {number} duration_seconds - 答题时长(秒)
|
||||
* @returns {Promise<Object>} - 包含操作结果和试卷数据的对象
|
||||
*/
|
||||
async function processPaper(paperId) {
|
||||
async function processPaper(paperId, duration_seconds) {
|
||||
try {
|
||||
const userDb = await getDbConnection(getUserDbPath());
|
||||
const currentTime = formatDateTime(new Date());
|
||||
|
||||
await userDb.runAsync(
|
||||
`UPDATE examinee_papers
|
||||
SET paper_last_time = ?
|
||||
SET paper_last_time = ?,
|
||||
paper_duration_seconds = ?
|
||||
WHERE id = ?`,
|
||||
[currentTime, paperId]
|
||||
[currentTime, duration_seconds, paperId]
|
||||
);
|
||||
|
||||
// 查询更新后的试卷数据
|
||||
@ -1196,8 +1211,195 @@ async function getPaper(paperId) {
|
||||
}
|
||||
}
|
||||
|
||||
// 在文件末尾添加所有导出
|
||||
module.exports = {
|
||||
/**
|
||||
* 根据考生ID获取历史考试记录
|
||||
* @param {number} examineeId - 考生ID
|
||||
* @returns {Promise<Object>} - 包含查询结果的对象
|
||||
*/
|
||||
async function getExamineePaper(examineeId) {
|
||||
try {
|
||||
// 1. 获取数据库连接
|
||||
const systemDb = await getDbConnection(getSystemDbPath());
|
||||
const userDb = await getDbConnection(getUserDbPath());
|
||||
|
||||
// 2. 查询考生信息
|
||||
const examinee = await userDb.getAsync(
|
||||
`SELECT * FROM examinee WHERE id = ?`,
|
||||
[examineeId]
|
||||
);
|
||||
|
||||
if (!examinee) {
|
||||
console.log(`未找到ID为${examineeId}的考生`);
|
||||
return {
|
||||
success: false,
|
||||
message: '未找到历史考试',
|
||||
data: null
|
||||
};
|
||||
}
|
||||
|
||||
// 3. 查询考生的所有试卷记录
|
||||
const examineePapers = await userDb.allAsync(
|
||||
`SELECT * FROM examinee_papers WHERE examinee_id = ? ORDER BY created_at DESC`,
|
||||
[examineeId]
|
||||
);
|
||||
|
||||
if (!examineePapers || examineePapers.length === 0) {
|
||||
console.log(`未找到ID为${examineeId}的考生的试卷记录`);
|
||||
return {
|
||||
success: false,
|
||||
message: '未找到历史考试',
|
||||
data: null
|
||||
};
|
||||
}
|
||||
|
||||
// 4. 为每个试卷获取详细信息
|
||||
const papersWithDetails = [];
|
||||
for (const paper of examineePapers) {
|
||||
const paperId = paper.id;
|
||||
|
||||
// 4.1 查询试卷关联的题目
|
||||
const paperQuestions = await userDb.allAsync(
|
||||
`SELECT * FROM paper_questions WHERE paper_id = ?`,
|
||||
[paperId]
|
||||
);
|
||||
|
||||
// 4.2 为每个题目获取详细信息
|
||||
const questionsWithDetails = [];
|
||||
for (const question of paperQuestions) {
|
||||
const questionId = question.id;
|
||||
const questionType = question.question_type;
|
||||
|
||||
// 查询题型名称
|
||||
const dictItem = await systemDb.getAsync(
|
||||
`SELECT item_name FROM dict_items WHERE type_code = 'question_type' AND item_code = ?`,
|
||||
[questionType]
|
||||
);
|
||||
|
||||
const questionWithDetails = {
|
||||
...question,
|
||||
question_type_name: dictItem && dictItem.item_name ? dictItem.item_name : '',
|
||||
images: [],
|
||||
datasets: [],
|
||||
choices: [],
|
||||
blanks: []
|
||||
};
|
||||
|
||||
// 查询题目图片
|
||||
const images = await userDb.allAsync(
|
||||
`SELECT * FROM question_images WHERE question_id = ?`,
|
||||
[questionId]
|
||||
);
|
||||
questionWithDetails.images = images;
|
||||
|
||||
// 查询题目数据集
|
||||
const datasets = await userDb.allAsync(
|
||||
`SELECT * FROM question_datasets WHERE question_id = ?`,
|
||||
[questionId]
|
||||
);
|
||||
// 解析dataset_data为数组
|
||||
questionWithDetails.datasets = datasets.map(dataset => ({
|
||||
...dataset,
|
||||
dataset_data: JSON.parse(dataset.dataset_data || '[]')
|
||||
}));
|
||||
|
||||
// 根据题型查询对应的答案表
|
||||
if (questionType === 'choice') {
|
||||
// 查询选择题
|
||||
const choices = await userDb.allAsync(
|
||||
`SELECT * FROM question_choices WHERE question_id = ?`,
|
||||
[questionId]
|
||||
);
|
||||
// 解析数组列
|
||||
questionWithDetails.choices = choices.map(choice => ({
|
||||
...choice,
|
||||
choice_options: JSON.parse(choice.choice_options || '[]'),
|
||||
correct_answers: JSON.parse(choice.correct_answers || '[]'),
|
||||
examinee_answers: JSON.parse(choice.examinee_answers || '[]')
|
||||
}));
|
||||
} else if (questionType === 'fill_blank') {
|
||||
// 查询填空题
|
||||
const blanks = await userDb.allAsync(
|
||||
`SELECT * FROM question_fill_blanks WHERE question_id = ?`,
|
||||
[questionId]
|
||||
);
|
||||
// 解析数组列
|
||||
questionWithDetails.blanks = blanks.map(blank => ({
|
||||
...blank,
|
||||
correct_answers: JSON.parse(blank.correct_answers || '[]'),
|
||||
examinee_answers: JSON.parse(blank.examinee_answers || '[]')
|
||||
}));
|
||||
}
|
||||
|
||||
questionsWithDetails.push(questionWithDetails);
|
||||
}
|
||||
|
||||
// 4.3 构建完整的试卷对象
|
||||
const fullPaper = {
|
||||
...paper,
|
||||
examinee: examinee,
|
||||
questions: questionsWithDetails
|
||||
};
|
||||
|
||||
papersWithDetails.push(fullPaper);
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: '找到历史考试',
|
||||
data: papersWithDetails
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('获取考生历史试卷过程中发生错误:', error);
|
||||
return {
|
||||
success: false,
|
||||
message: `获取历史考试失败: ${error.message}`,
|
||||
data: null
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 更新导出语句,添加getExamineePaper方法
|
||||
/**
|
||||
* 继续考试
|
||||
* @param {number} paperId - 试卷ID
|
||||
* @param {number} usedMinutes - 已用时间(分钟)
|
||||
* @returns {Promise<Object>} - 包含操作结果和试卷数据的对象
|
||||
*/
|
||||
async function continueExam(paperId) {
|
||||
try {
|
||||
const userDb = await getDbConnection(getUserDbPath());
|
||||
const currentTime = new Date();
|
||||
const formattedCurrentTime = formatDateTime(currentTime);
|
||||
|
||||
await userDb.runAsync(
|
||||
`UPDATE examinee_papers
|
||||
SET paper_status = 1,
|
||||
paper_last_time = ?
|
||||
WHERE id = ?`,
|
||||
[formattedCurrentTime, paperId]
|
||||
);
|
||||
|
||||
// 查询更新后的试卷数据
|
||||
const paper = await userDb.getAsync(
|
||||
`SELECT * FROM examinee_papers WHERE id = ?`,
|
||||
[paperId]
|
||||
);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: '考试已成功继续',
|
||||
data: paper
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('继续考试失败:', error);
|
||||
return {
|
||||
success: false,
|
||||
message: `继续考试失败: ${error.message}`
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
exports = module.exports = {
|
||||
formatDateTime,
|
||||
batchInsert,
|
||||
generateExamineePaper,
|
||||
@ -1209,5 +1411,7 @@ module.exports = {
|
||||
endPaper,
|
||||
processPaper,
|
||||
checkPaperAnswers,
|
||||
getPaper
|
||||
getPaper,
|
||||
getExamineePaper,
|
||||
continueExam
|
||||
};
|
@ -1,6 +1,7 @@
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const { app } = require('electron');
|
||||
// 添加缺少的path模块导入
|
||||
const path = require('path');
|
||||
|
||||
// 判断是否为开发环境
|
||||
const isDev = process.env.NODE_ENV === 'development' || !app.isPackaged;
|
||||
|
@ -208,6 +208,7 @@ userSchema = {
|
||||
paper_last_time TEXT,
|
||||
paper_submit_time TEXT,
|
||||
paper_end_time TEXT,
|
||||
paper_duration_seconds INTEGER DEFAULT 0,
|
||||
paper_status INTEGER NOT NULL DEFAULT 0,
|
||||
paper_score_real REAL DEFAULT 0,
|
||||
paper_score REAL DEFAULT 0,
|
||||
|
@ -1,5 +1,6 @@
|
||||
const sqlite3 = require('sqlite3');
|
||||
const { promisify } = require('util');
|
||||
// 添加缺少的sqlite3模块导入
|
||||
const sqlite3 = require('sqlite3');
|
||||
|
||||
sqlite3.verbose();
|
||||
|
||||
|
@ -110,8 +110,10 @@ contextBridge.exposeInMainWorld("electronAPI", {
|
||||
ipcRenderer.invoke("examinee-update", { id, examineeData }),
|
||||
examineeDelete: (id) => ipcRenderer.invoke("examinee-delete", id),
|
||||
|
||||
// 在考生考试服务相关接口部分添加examingCheckPaperAnswers
|
||||
// 考生考试服务相关接口
|
||||
// 在"考生考试服务相关接口"部分添加getPaper接口
|
||||
examingGetPaper: ({ paperId }) =>
|
||||
ipcRenderer.invoke("examing-get-paper", { paperId }),
|
||||
// 在考生考试服务相关接口
|
||||
examingGeneratePaper: ({ examineeId, examId }) =>
|
||||
ipcRenderer.invoke("examing-generate-paper", { examineeId, examId }),
|
||||
examingGetPaperStatus: ({ examineeId, examId }) =>
|
||||
@ -125,21 +127,26 @@ contextBridge.exposeInMainWorld("electronAPI", {
|
||||
tableName,
|
||||
relatedId,
|
||||
}),
|
||||
// 修复:examingUpdateAnswer接口的参数结构
|
||||
examingUpdateAnswer: ({ tableName, id, answers }) =>
|
||||
ipcRenderer.invoke("examing-update-answer", { tableName, id, answers }),
|
||||
// 在考生考试服务相关接口部分确保examingStartPaper正确暴露
|
||||
examingStartPaper: ({ paperId }) =>
|
||||
ipcRenderer.invoke("examing-start-paper", { paperId }),
|
||||
examingSubmitPaper: ({ paperId }) =>
|
||||
ipcRenderer.invoke("examing-submit-paper", { paperId }),
|
||||
// 确保examingGetExamResult API正确暴露
|
||||
examingSubmitPaper: ({ paperId, duration_seconds = 0 }) =>
|
||||
ipcRenderer.invoke("examing-submit-paper", { paperId, duration_seconds }),
|
||||
examingGetExamResult: ({ paperId }) =>
|
||||
ipcRenderer.invoke("examing-get-exam-result", { paperId }),
|
||||
examingCheckPaperAnswers: ({ paperId }) =>
|
||||
ipcRenderer.invoke("examing-check-paper-answers", { paperId }),
|
||||
examingProcessPaper: ({ paperId }) =>
|
||||
ipcRenderer.invoke("examing-process-paper", { paperId }),
|
||||
examingProcessPaper: ({ paperId, duration_seconds = 0 }) =>
|
||||
ipcRenderer.invoke("examing-process-paper", { paperId, duration_seconds }),
|
||||
examingGetExamineePaper: ({ examineeId }) =>
|
||||
ipcRenderer.invoke("examing-get-examinee-paper", { examineeId }),
|
||||
// 继续考试接口
|
||||
examingContinuePaper: ({ paperId }) =>
|
||||
ipcRenderer.invoke("examing-continue-paper", { paperId }),
|
||||
// 新增:结束考试接口
|
||||
examingEndPaper: ({ paperId }) =>
|
||||
ipcRenderer.invoke("examing-end-paper", { paperId }),
|
||||
|
||||
// 文件服务相关接口
|
||||
// 保留一个fileGeneratePaperPdf定义,移除重复的
|
||||
|
@ -1,4 +1,4 @@
|
||||
// 保留第一个导入
|
||||
// 首先在文件顶部的require语句中添加getExamineePaper
|
||||
const {
|
||||
generateExamineePaper,
|
||||
loadPaperSerial,
|
||||
@ -10,13 +10,12 @@ const {
|
||||
processPaper,
|
||||
checkPaperAnswers,
|
||||
getPaper,
|
||||
getExamineePaper,
|
||||
continueExam, // 添加continueExam到导入列表中
|
||||
} = require("../db/examing.js");
|
||||
const { getDbConnection, closeAllConnections } = require("../db/index.js");
|
||||
const { getUserDbPath } = require("../db/path.js");
|
||||
|
||||
// 删除下面这行重复的导入
|
||||
// const { generateExamineePaper, loadPaperSerial, getQuestionByRelatedId, updateExamineeAnswer, startPaper, submitPaper, endPaper, processPaper, checkPaperAnswers, getPaper } = require('../db/examing.js');
|
||||
|
||||
/**
|
||||
* 服务层:生成考生试卷
|
||||
* @param {Object} examineeData - 考生数据
|
||||
@ -244,19 +243,41 @@ async function startPaperService(paperId) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务层:获取试卷详细信息
|
||||
* @param {number} paperId - 试卷ID
|
||||
* @returns {Promise<Object>} - 包含试卷详细信息的对象
|
||||
*/
|
||||
async function getPaperService(paperId) {
|
||||
try {
|
||||
if (!paperId || paperId <= 0) {
|
||||
throw new Error("试卷ID必须为正数");
|
||||
}
|
||||
|
||||
const result = await getPaper(paperId);
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error("服务层: 获取试卷详细信息失败", error);
|
||||
return {
|
||||
success: false,
|
||||
message: `获取试卷详细信息失败: ${error.message}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务层:提交考试
|
||||
* @param {number} paperId - 试卷ID
|
||||
* @returns {Promise<Object>} - 包含操作结果的对象
|
||||
*/
|
||||
async function submitPaperService(paperId) {
|
||||
async function submitPaperService(paperId, duration_seconds) {
|
||||
try {
|
||||
if (!paperId || paperId <= 0) {
|
||||
throw new Error("试卷ID必须为正数");
|
||||
}
|
||||
|
||||
// 1. 提交试卷
|
||||
const submitResult = await submitPaper(paperId);
|
||||
const submitResult = await submitPaper(paperId, duration_seconds);
|
||||
|
||||
if (!submitResult.success) {
|
||||
throw new Error(submitResult.message);
|
||||
@ -338,16 +359,20 @@ async function endPaperService(paperId) {
|
||||
/**
|
||||
* 服务层:处理试卷
|
||||
* @param {number} paperId - 试卷ID
|
||||
* @param {number} duration_seconds - 答题时长(秒)
|
||||
* @returns {Promise<Object>} - 包含操作结果的对象
|
||||
*/
|
||||
async function processPaperService(paperId) {
|
||||
async function processPaperService(paperId, duration_seconds) {
|
||||
try {
|
||||
if (!paperId || paperId <= 0) {
|
||||
throw new Error("试卷ID必须为正数");
|
||||
}
|
||||
|
||||
const result = await processPaper(paperId);
|
||||
return result;
|
||||
// 这样在继续考试时,就不会覆盖数据库中已有的paper_duration_seconds值
|
||||
if (duration_seconds === undefined || duration_seconds === null) {
|
||||
return await processPaper(paperId);
|
||||
}
|
||||
return await processPaper(paperId, duration_seconds);
|
||||
} catch (error) {
|
||||
console.error("服务层: 处理试卷失败", error);
|
||||
return {
|
||||
@ -380,10 +405,50 @@ async function checkPaperAnswersService(paperId) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化考试相关的IPC处理程序
|
||||
* @param {import('electron').IpcMain} ipcMain - IPC主进程实例
|
||||
* 服务层:根据考生ID获取试卷数据
|
||||
* @param {number} examineeId - 考生ID
|
||||
* @returns {Promise<Object>} - 包含试卷数据的对象
|
||||
*/
|
||||
// 在initExamingIpc函数中添加examing-get-exam-result处理器
|
||||
async function getExamineePaperService(examineeId) {
|
||||
try {
|
||||
if (!examineeId || examineeId <= 0) {
|
||||
throw new Error("考生ID必须为正数");
|
||||
}
|
||||
|
||||
const result = await getExamineePaper(examineeId);
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error("服务层: 获取考生试卷数据失败", error);
|
||||
return {
|
||||
success: false,
|
||||
message: `获取考生试卷数据失败: ${error.message}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务层:继续未完成的考试
|
||||
* @param {number} paperId - 试卷ID
|
||||
* @returns {Promise<Object>} - 包含操作结果的对象
|
||||
*/
|
||||
async function continueExamService(paperId) {
|
||||
try {
|
||||
if (!paperId || paperId <= 0) {
|
||||
throw new Error("试卷ID必须为正数");
|
||||
}
|
||||
|
||||
const result = await continueExam(paperId);
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error("服务层: 继续考试失败", error);
|
||||
return {
|
||||
success: false,
|
||||
message: `继续考试失败: ${error.message}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 在initExamingIpc函数中添加continueExam的IPC处理程序(在其他处理器之后添加)
|
||||
function initExamingIpc(ipcMain) {
|
||||
// 生成考生试卷
|
||||
ipcMain.handle(
|
||||
@ -490,17 +555,21 @@ function initExamingIpc(ipcMain) {
|
||||
});
|
||||
|
||||
// 提交考试
|
||||
ipcMain.handle("examing-submit-paper", async (event, { paperId }) => {
|
||||
try {
|
||||
return await submitPaperService(paperId);
|
||||
} catch (error) {
|
||||
console.error("提交考试失败:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: `提交考试失败: ${error.message}`,
|
||||
};
|
||||
// 提交考试
|
||||
ipcMain.handle(
|
||||
"examing-submit-paper",
|
||||
async (event, { paperId, duration_seconds }) => {
|
||||
try {
|
||||
return await submitPaperService(paperId, duration_seconds);
|
||||
} catch (error) {
|
||||
console.error("提交考试失败:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: `提交考试失败: ${error.message}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
// 结束考试
|
||||
ipcMain.handle("examing-end-paper", async (event, { paperId }) => {
|
||||
@ -516,17 +585,20 @@ function initExamingIpc(ipcMain) {
|
||||
});
|
||||
|
||||
// 处理试卷
|
||||
ipcMain.handle("examing-process-paper", async (event, { paperId }) => {
|
||||
try {
|
||||
return await processPaperService(paperId);
|
||||
} catch (error) {
|
||||
console.error("处理试卷失败:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: `处理试卷失败: ${error.message}`,
|
||||
};
|
||||
ipcMain.handle(
|
||||
"examing-process-paper",
|
||||
async (event, { paperId, duration_seconds }) => {
|
||||
try {
|
||||
return await processPaperService(paperId, duration_seconds);
|
||||
} catch (error) {
|
||||
console.error("处理试卷失败:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: `处理试卷失败: ${error.message}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
// 检查试卷答案并计算得分
|
||||
ipcMain.handle("examing-check-paper-answers", async (event, { paperId }) => {
|
||||
@ -553,6 +625,48 @@ function initExamingIpc(ipcMain) {
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// 添加通过考生ID获取试卷数据的IPC处理器
|
||||
ipcMain.handle(
|
||||
"examing-get-examinee-paper",
|
||||
async (event, { examineeId }) => {
|
||||
try {
|
||||
return await getExamineePaperService(examineeId);
|
||||
} catch (error) {
|
||||
console.error("获取考生试卷数据失败:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: `获取考生试卷数据失败: ${error.message}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// 添加继续考试的IPC处理器
|
||||
ipcMain.handle("examing-continue-paper", async (event, { paperId }) => {
|
||||
try {
|
||||
return await continueExamService(paperId);
|
||||
} catch (error) {
|
||||
console.error("继续考试失败:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: `继续考试失败: ${error.message}`,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// 在initExamingIpc函数中添加getPaper的IPC处理器
|
||||
ipcMain.handle("examing-get-paper", async (event, { paperId }) => {
|
||||
try {
|
||||
return await getPaperService(paperId);
|
||||
} catch (error) {
|
||||
console.error("获取试卷详细信息失败:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: `获取试卷详细信息失败: ${error.message}`,
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 导出使用CommonJS格式
|
||||
@ -568,5 +682,9 @@ module.exports = {
|
||||
endPaperService,
|
||||
processPaperService,
|
||||
checkPaperAnswersService,
|
||||
getExamResultService,
|
||||
getExamineePaperService,
|
||||
continueExamService,
|
||||
getPaperService,
|
||||
initExamingIpc,
|
||||
};
|
||||
|
@ -395,10 +395,17 @@ async function generatePaperPdf(jsonString) {
|
||||
const endTime = paperData.paper_end_time;
|
||||
// 截取考试日期 (假设格式为 'YYYY-MM-DD HH:mm:ss')
|
||||
const examDate = startTime.split(' ')[0];
|
||||
// 计算用时(分钟)
|
||||
const start = new Date(startTime.replace(/-/g, '/'));
|
||||
const end = new Date(endTime.replace(/-/g, '/'));
|
||||
const durationMinutes = Math.round((end - start) / (1000 * 60));
|
||||
// 计算用时(分钟)- 直接使用paper_duration_seconds字段
|
||||
let durationMinutes = 0;
|
||||
if (paperData.paper_duration_seconds && paperData.paper_duration_seconds > 0) {
|
||||
// 直接从paper_duration_seconds字段获取并转换为分钟
|
||||
durationMinutes = Math.round(paperData.paper_duration_seconds / 60);
|
||||
} else {
|
||||
// 备选方案:如果paper_duration_seconds不存在或无效,使用原来的计算方式
|
||||
const start = new Date(startTime.replace(/-/g, '/'));
|
||||
const end = new Date(endTime.replace(/-/g, '/'));
|
||||
durationMinutes = Math.round((end - start) / (1000 * 60));
|
||||
}
|
||||
|
||||
// 提取试卷信息
|
||||
// 计算总题量(每个question下的choice题和fill_blank题的数量之和)
|
||||
|
@ -106,7 +106,8 @@ export default {
|
||||
return {
|
||||
size: 'default',
|
||||
pdfPath: '',
|
||||
isChecking: false
|
||||
isChecking: false,
|
||||
paperData: null // 新增:在组件内保存paper数据
|
||||
}
|
||||
},
|
||||
// 在script部分确保正确的计算属性定义
|
||||
@ -115,22 +116,9 @@ export default {
|
||||
examinee () {
|
||||
return this.$store.state.examinee || {}
|
||||
},
|
||||
// 从store中获取试卷信息
|
||||
// 修改:不再从store中获取试卷信息,改为使用组件内数据
|
||||
paper () {
|
||||
// 如果store中没有数据,尝试从localStorage获取备用数据
|
||||
if (!this.$store.state.paper || Object.keys(this.$store.state.paper).length === 0) {
|
||||
const savedPaper = localStorage.getItem('lastExamPaper');
|
||||
if (savedPaper) {
|
||||
try {
|
||||
// 解析并返回localStorage中的数据,但不直接修改store
|
||||
return JSON.parse(savedPaper);
|
||||
} catch (e) {
|
||||
console.error('解析localStorage试卷数据失败:', e);
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
return this.$store.state.paper || {};
|
||||
return this.paperData || {}
|
||||
},
|
||||
// 图标样式计算属性
|
||||
iconStyle () {
|
||||
@ -164,112 +152,101 @@ export default {
|
||||
|
||||
// 检查试卷数据
|
||||
checkPaperData () {
|
||||
// 1. 首先检查store中是否有完整数据
|
||||
if (this.paper && this.paper.id) {
|
||||
console.log('store中已有完整试卷数据');
|
||||
// 检查是否需要判卷
|
||||
if (!this.paper.is_checked) {
|
||||
this.checkAnswers(this.paper.id);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 从路由参数获取paperId
|
||||
const paperIdFromRoute = this.$route.params.paperId;
|
||||
// 1. 从路由参数获取paperId
|
||||
const paperIdFromRoute = this.$route.params.paperId
|
||||
|
||||
if (!paperIdFromRoute) {
|
||||
console.error('路由参数中没有找到paperId');
|
||||
this.$message.error('无法获取试卷信息,请重新提交试卷');
|
||||
return;
|
||||
console.error('路由参数中没有找到paperId')
|
||||
this.$message.error('无法获取试卷信息,请重新提交试卷')
|
||||
return
|
||||
}
|
||||
|
||||
// 3. 尝试从localStorage恢复数据
|
||||
const savedPaperData = localStorage.getItem('lastExamPaper');
|
||||
// 2. 尝试从localStorage恢复数据
|
||||
const savedPaperData = localStorage.getItem('lastExamPaper')
|
||||
if (savedPaperData) {
|
||||
try {
|
||||
const paperData = JSON.parse(savedPaperData);
|
||||
const paperData = JSON.parse(savedPaperData)
|
||||
// 验证恢复的数据是否匹配当前paperId
|
||||
if (paperData.id && paperData.id.toString() === paperIdFromRoute.toString()) {
|
||||
console.log('从localStorage恢复到store的试卷数据:', paperData);
|
||||
// 保存到store,确保所有地方都能通过this.paper访问
|
||||
this.$store.commit('setPaper', paperData);
|
||||
console.log('从localStorage恢复的试卷数据:', paperData)
|
||||
// 修改:保存到组件本地而不是store
|
||||
this.paperData = paperData
|
||||
|
||||
// 检查是否需要判卷
|
||||
if (!paperData.is_checked) {
|
||||
this.checkAnswers(paperData.id);
|
||||
this.checkAnswers(paperData.id)
|
||||
}
|
||||
return;
|
||||
return
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('解析localStorage中的试卷数据失败:', e);
|
||||
console.error('解析localStorage中的试卷数据失败:', e)
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 如果以上都失败,调用API加载数据
|
||||
console.log('调用API加载试卷数据,paperId:', paperIdFromRoute);
|
||||
this.loadExamResult(paperIdFromRoute);
|
||||
// 3. 调用API加载数据
|
||||
console.log('调用API加载试卷数据,paperId:', paperIdFromRoute)
|
||||
this.loadExamResult(paperIdFromRoute)
|
||||
},
|
||||
|
||||
// 加载考试结果 - 确保数据正确保存到store
|
||||
async loadExamResult (paperIdFromParams) {
|
||||
try {
|
||||
const paperId = paperIdFromParams || this.$route.params.paperId;
|
||||
const paperId = paperIdFromParams || this.$route.params.paperId
|
||||
|
||||
if (!paperId) {
|
||||
console.error('没有找到试卷ID');
|
||||
this.$message.error('无法获取试卷信息,请重新提交试卷');
|
||||
return;
|
||||
console.error('没有找到试卷ID')
|
||||
this.$message.error('无法获取试卷信息,请重新提交试卷')
|
||||
return
|
||||
}
|
||||
|
||||
// 检查API是否存在
|
||||
if (!window.electronAPI || typeof window.electronAPI.examingGetExamResult !== 'function') {
|
||||
console.error('examingGetExamResult API不存在');
|
||||
this.$message.warning('获取考试结果功能暂不可用');
|
||||
return;
|
||||
console.error('examingGetExamResult API不存在')
|
||||
this.$message.warning('获取考试结果功能暂不可用')
|
||||
return
|
||||
}
|
||||
|
||||
const result = await window.electronAPI.examingGetExamResult({ paperId });
|
||||
const result = await window.electronAPI.examingGetExamResult({ paperId })
|
||||
|
||||
if (result && result.success && result.data) {
|
||||
// 数据已经是对象,直接保存到store
|
||||
this.$store.commit('setPaper', result.data);
|
||||
// 修改:保存到组件本地而不是store
|
||||
this.paperData = result.data
|
||||
// 同时保存到localStorage作为备份
|
||||
localStorage.setItem('lastExamPaper', JSON.stringify(result.data));
|
||||
localStorage.setItem('lastExamPaper', JSON.stringify(result.data))
|
||||
|
||||
console.log('API获取并保存到store的试卷数据:', this.$store.state.paper);
|
||||
console.log('API获取的试卷数据:', this.paperData)
|
||||
|
||||
// 检查是否需要判卷
|
||||
if (!result.data.is_checked) {
|
||||
this.checkAnswers(result.data.id || paperId);
|
||||
this.checkAnswers(result.data.id || paperId)
|
||||
}
|
||||
} else {
|
||||
console.error('获取考试结果失败:', result?.message || '未知错误');
|
||||
this.$message.error('获取考试结果失败');
|
||||
console.error('获取考试结果失败:', result?.message || '未知错误')
|
||||
this.$message.error('获取考试结果失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载考试结果失败:', error);
|
||||
this.$message.error('加载考试结果失败');
|
||||
console.error('加载考试结果失败:', error)
|
||||
this.$message.error('加载考试结果失败')
|
||||
}
|
||||
},
|
||||
// 判卷方法
|
||||
// 判卷方法
|
||||
async checkAnswers () {
|
||||
try {
|
||||
if (!this.paper || !this.paper.id) {
|
||||
console.error('没有试卷ID,无法判卷');
|
||||
this.$message.warning('缺少试卷信息,无法判卷');
|
||||
return;
|
||||
if (!this.paperData || !this.paperData.id) {
|
||||
console.error('没有试卷ID,无法判卷')
|
||||
this.$message.warning('缺少试卷信息,无法判卷')
|
||||
return
|
||||
}
|
||||
|
||||
this.isChecking = true
|
||||
const paperId = this.paper.id
|
||||
const paperId = this.paperData.id
|
||||
|
||||
// 检查API是否存在
|
||||
if (!window.electronAPI || typeof window.electronAPI.examingCheckPaperAnswers !== 'function') {
|
||||
console.error('examingCheckPaperAnswers API不存在');
|
||||
this.$message.warning('判卷功能暂不可用');
|
||||
this.isChecking = false;
|
||||
return;
|
||||
console.error('examingCheckPaperAnswers API不存在')
|
||||
this.$message.warning('判卷功能暂不可用')
|
||||
this.isChecking = false
|
||||
return
|
||||
}
|
||||
|
||||
const result = await window.electronAPI.examingCheckPaperAnswers({ paperId })
|
||||
@ -280,26 +257,26 @@ export default {
|
||||
// 新增:在判卷成功后生成PDF
|
||||
try {
|
||||
// 获取最新的试卷数据(包含判卷结果)
|
||||
const paperDataStr = JSON.stringify(this.paper);
|
||||
const paperDataStr = JSON.stringify(this.paperData)
|
||||
// 生成PDF
|
||||
const pdfResult = await window.electronAPI.fileGeneratePaperPdf(paperDataStr);
|
||||
const pdfResult = await window.electronAPI.fileGeneratePaperPdf(paperDataStr)
|
||||
if (pdfResult && pdfResult.filePath) {
|
||||
this.pdfPath = pdfResult;
|
||||
console.log('PDF生成成功,保存路径:', pdfResult.filePath);
|
||||
this.pdfPath = pdfResult
|
||||
console.log('PDF生成成功,保存路径:', pdfResult.filePath)
|
||||
// this.$message.success('考试报告PDF已生成');
|
||||
} else {
|
||||
console.error('PDF生成失败:', pdfResult);
|
||||
console.error('PDF生成失败:', pdfResult)
|
||||
}
|
||||
} catch (pdfError) {
|
||||
console.error('PDF生成过程异常:', pdfError);
|
||||
console.error('PDF生成过程异常:', pdfError)
|
||||
}
|
||||
} else {
|
||||
console.error('判卷失败:', result?.message || '未知错误');
|
||||
this.$message.error('判卷失败,请稍后重试');
|
||||
console.error('判卷失败:', result?.message || '未知错误')
|
||||
this.$message.error('判卷失败,请稍后重试')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('判卷过程异常:', error);
|
||||
this.$message.error('判卷过程中发生异常');
|
||||
console.error('判卷过程异常:', error)
|
||||
this.$message.error('判卷过程中发生异常')
|
||||
} finally {
|
||||
this.isChecking = false
|
||||
}
|
||||
@ -328,36 +305,54 @@ export default {
|
||||
|
||||
// 获取格式化的开始时间
|
||||
getFormattedStartTime () {
|
||||
if (!this.paper || !this.paper.paper_start_time) return '未知';
|
||||
return this.formatDateTime(this.paper.paper_start_time);
|
||||
if (!this.paper || !this.paper.paper_start_time) return '未知'
|
||||
return this.formatDateTime(this.paper.paper_start_time)
|
||||
},
|
||||
|
||||
// 获取格式化的结束时间
|
||||
getFormattedEndTime () {
|
||||
if (!this.paper || !this.paper.paper_end_time) return '未知';
|
||||
return this.formatDateTime(this.paper.paper_end_time);
|
||||
if (!this.paper || !this.paper.paper_end_time) return '未知'
|
||||
return this.formatDateTime(this.paper.paper_end_time)
|
||||
},
|
||||
|
||||
// 计算考试总时长
|
||||
getDuration () {
|
||||
if (!this.paper || !this.paper.paper_start_time || !this.paper.paper_end_time) {
|
||||
return '未知';
|
||||
// 优先检查paper_duration_seconds是否存在且有效
|
||||
if (this.paper && this.paper.paper_duration_seconds !== undefined && this.paper.paper_duration_seconds !== null) {
|
||||
const totalSeconds = this.paper.paper_duration_seconds
|
||||
|
||||
const hours = Math.floor(totalSeconds / 3600)
|
||||
const minutes = Math.floor((totalSeconds % 3600) / 60)
|
||||
const seconds = totalSeconds % 60
|
||||
|
||||
if (hours > 0) {
|
||||
return `${hours}小时${minutes}分${seconds}秒`
|
||||
} else if (minutes > 0) {
|
||||
return `${minutes}分${seconds}秒`
|
||||
} else {
|
||||
return `${seconds}秒`
|
||||
}
|
||||
}
|
||||
|
||||
const startTime = new Date(this.paper.paper_start_time);
|
||||
const endTime = new Date(this.paper.paper_end_time);
|
||||
const durationMs = endTime - startTime;
|
||||
// 如果paper_duration_seconds不存在或无效,则使用原来的时间差值计算作为备选
|
||||
if (!this.paper || !this.paper.paper_start_time || !this.paper.paper_end_time) {
|
||||
return '未知'
|
||||
}
|
||||
|
||||
const hours = Math.floor(durationMs / (1000 * 60 * 60));
|
||||
const minutes = Math.floor((durationMs % (1000 * 60 * 60)) / (1000 * 60));
|
||||
const seconds = Math.floor((durationMs % (1000 * 60)) / 1000);
|
||||
const startTime = new Date(this.paper.paper_start_time)
|
||||
const endTime = new Date(this.paper.paper_end_time)
|
||||
const durationMs = endTime - startTime
|
||||
|
||||
const hours = Math.floor(durationMs / (1000 * 60 * 60))
|
||||
const minutes = Math.floor((durationMs % (1000 * 60 * 60)) / (1000 * 60))
|
||||
const seconds = Math.floor((durationMs % (1000 * 60)) / 1000)
|
||||
|
||||
if (hours > 0) {
|
||||
return `${hours}小时${minutes}分${seconds}秒`;
|
||||
return `${hours}小时${minutes}分${seconds}秒`
|
||||
} else if (minutes > 0) {
|
||||
return `${minutes}分${seconds}秒`;
|
||||
return `${minutes}分${seconds}秒`
|
||||
} else {
|
||||
return `${seconds}秒`;
|
||||
return `${seconds}秒`
|
||||
}
|
||||
},
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -31,7 +31,7 @@
|
||||
<div class="exam-bottom-bar rounded shadow-sm mt-2">
|
||||
<el-button type="primary" size="small" @click="prevQuestion" :disabled="currentQuestion === 1">上一题</el-button>
|
||||
<el-button type="primary" size="small" @click="nextQuestion" :disabled="currentQuestion === questionList.length">下一题</el-button>
|
||||
<el-button type="danger" size="small" @click="showExamPreview" :disabled="!canSubmit">交卷预览</el-button>
|
||||
<el-button type="danger" size="small" @click="showExamPreview" :disabled="!canSubmitComputed">交卷预览</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -70,6 +70,8 @@ export default {
|
||||
timer: null,
|
||||
processTimer: null,
|
||||
paperId: '',
|
||||
action: '', // 新增:存储action参数
|
||||
paperData: null, // 新增:在组件内保存paper数据
|
||||
blankAnswerTip: '数字请保留2位小数,如"100.00"',
|
||||
showPreviewModal: false, // 控制交卷预览模态框显示
|
||||
previewQuestionData: [] // 存储预览用的所有题目数据
|
||||
@ -80,9 +82,9 @@ export default {
|
||||
examinee() {
|
||||
return this.$store.state.examinee
|
||||
},
|
||||
// 从store中获取试卷信息
|
||||
// 修改:不再从store中获取试卷信息,改为使用本地数据
|
||||
paper() {
|
||||
return this.$store.state.paper
|
||||
return this.paperData
|
||||
},
|
||||
// 当前题目数据
|
||||
currentQuestionData() {
|
||||
@ -91,6 +93,34 @@ export default {
|
||||
// 添加:已作答题目数量计算
|
||||
answeredCount() {
|
||||
return this.questionList.filter(item => item.answered === 1).length;
|
||||
},
|
||||
// 添加:使用computed计算交卷按钮可用性
|
||||
canSubmitComputed() {
|
||||
// 如果没有paper信息,默认不可用
|
||||
if (!this.paper) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 如果没有设置最小时长,直接可用
|
||||
if (!this.paper.paper_minutes_min) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 将countdown字符串转换为剩余秒数
|
||||
const timeParts = this.countdown.split(':');
|
||||
if (timeParts.length !== 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const hours = parseInt(timeParts[0], 10);
|
||||
const minutes = parseInt(timeParts[1], 10);
|
||||
const seconds = parseInt(timeParts[2], 10);
|
||||
const remainingSeconds = hours * 3600 + minutes * 60 + seconds;
|
||||
const paperMinutesSeconds = this.paper.paper_minutes * 60;
|
||||
const paperMinutesMinSeconds = this.paper.paper_minutes_min * 60;
|
||||
|
||||
// 根据新规则:倒计时的剩余总秒数 + paper_minutes_min分钟转为的秒数 <= paper_minutes分钟数转为的秒数
|
||||
return remainingSeconds + paperMinutesMinSeconds <= paperMinutesSeconds;
|
||||
}
|
||||
},
|
||||
created() {
|
||||
@ -98,6 +128,10 @@ export default {
|
||||
if (this.$route.params && this.$route.params.paperId) {
|
||||
this.paperId = this.$route.params.paperId
|
||||
}
|
||||
// 获取action参数
|
||||
if (this.$route.params && this.$route.params.action) {
|
||||
this.action = this.$route.params.action
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 检查是否已登录
|
||||
@ -106,10 +140,13 @@ export default {
|
||||
return
|
||||
}
|
||||
|
||||
// 优先从路由参数获取paperId
|
||||
// 优先从路由参数获取paperId和action
|
||||
if (!this.paperId && this.$route.params && this.$route.params.paperId) {
|
||||
this.paperId = this.$route.params.paperId
|
||||
}
|
||||
if (!this.action && this.$route.params && this.$route.params.action) {
|
||||
this.action = this.$route.params.action
|
||||
}
|
||||
|
||||
// 如果路由参数中没有paperId,尝试从localStorage获取
|
||||
if (!this.paperId) {
|
||||
@ -138,37 +175,70 @@ export default {
|
||||
clearInterval(this.processTimer)
|
||||
this.processTimer = null
|
||||
}
|
||||
// 清除试卷信息
|
||||
this.$store.commit('clearPaper')
|
||||
// 移除:不再清除试卷信息
|
||||
// this.$store.commit('clearPaper')
|
||||
},
|
||||
methods: {
|
||||
// 初始化考试
|
||||
async initializeExam() {
|
||||
try {
|
||||
console.log('初始化考试,paperId:', this.paperId)
|
||||
console.log('初始化考试,paperId:', this.paperId, 'action:', this.action)
|
||||
|
||||
// 清除可能存在的旧试卷信息
|
||||
this.$store.commit('clearPaper')
|
||||
|
||||
// 1. 从接口获取试卷信息
|
||||
await this.loadPaperInfo(this.paperId)
|
||||
|
||||
// 2. 加载试题列表
|
||||
await this.loadQuestionList(this.paperId)
|
||||
|
||||
// 3. 初始化计时器
|
||||
if (this.paper && this.paper.paper_minutes) {
|
||||
this.initializeTimer(this.paper.paper_minutes * 60)
|
||||
// 根据action参数决定调用哪个接口
|
||||
if (this.action === 'start') {
|
||||
// 开始新考试
|
||||
const startResult = await window.electronAPI.examingStartPaper({ paperId: this.paperId })
|
||||
console.log('开始考试结果:', startResult);
|
||||
if (startResult && startResult.success && startResult.data) {
|
||||
this.paperData = startResult.data
|
||||
} else {
|
||||
throw new Error('开始考试失败')
|
||||
}
|
||||
} else if (this.action === 'continue') {
|
||||
// 继续考试
|
||||
const continueResult = await window.electronAPI.examingContinuePaper({ paperId: this.paperId })
|
||||
console.log('继续考试结果:', continueResult);
|
||||
if (continueResult && continueResult.success && continueResult.data) {
|
||||
this.paperData = continueResult.data
|
||||
} else {
|
||||
throw new Error('继续考试失败')
|
||||
}
|
||||
} else {
|
||||
this.initializeTimer(120 * 60) // 默认120分钟
|
||||
// 默认情况,直接获取试卷信息
|
||||
await this.loadPaperInfo(this.paperId)
|
||||
}
|
||||
|
||||
// 4. 启动自动保存定时器
|
||||
// 确保已获取到paperData
|
||||
if (!this.paperData) {
|
||||
throw new Error('未能获取到试卷数据,请重试')
|
||||
}
|
||||
|
||||
// 加载试题列表
|
||||
await this.loadQuestionList(this.paperId)
|
||||
|
||||
// 初始化计时器 - 严格基于数据库中的paper_duration_seconds
|
||||
let totalSeconds = 0
|
||||
|
||||
if (this.paperData && this.paperData.paper_minutes) {
|
||||
// 将试卷总时长转换为秒
|
||||
const paperTotalSeconds = this.paperData.paper_minutes * 60
|
||||
|
||||
// 必须使用数据库中的paper_duration_seconds计算剩余时间
|
||||
// 如果数据库中没有该值或为0,则使用完整时长
|
||||
const dbDurationSeconds = this.paperData.paper_duration_seconds || 0
|
||||
totalSeconds = Math.max(0, paperTotalSeconds - dbDurationSeconds)
|
||||
console.log(`使用数据库中的paper_duration_seconds计算剩余时间 - 总时长: ${paperTotalSeconds}秒, 已用时: ${dbDurationSeconds}秒, 剩余时间: ${totalSeconds}秒`)
|
||||
}
|
||||
|
||||
this.initializeTimer(totalSeconds)
|
||||
|
||||
// 启动自动保存定时器 - 确保不会将paper_duration_seconds改小
|
||||
this.startProcessTimer(this.paperId)
|
||||
|
||||
this.isReady = true
|
||||
this.canSubmit = this.paper && this.paper.paper_minutes_min ?
|
||||
false : true // 根据最小时长决定初始提交状态
|
||||
} catch (error) {
|
||||
console.error('初始化考试失败:', error)
|
||||
this.$message.error(`初始化考试失败: ${error.message || '未知错误'}`)
|
||||
@ -197,11 +267,12 @@ export default {
|
||||
this.$message.warning('考试时间剩余10分钟')
|
||||
}
|
||||
|
||||
// 移除定时器中的canSubmit设置
|
||||
// 检查是否达到最小交卷时间
|
||||
if (this.paper && this.paper.paper_minutes_min &&
|
||||
remainingSeconds <= totalSeconds - (this.paper.paper_minutes_min * 60)) {
|
||||
this.canSubmit = true
|
||||
}
|
||||
// if (this.paper && this.paper.paper_minutes_min &&
|
||||
// remainingSeconds + (this.paper.paper_minutes_min * 60) <= totalSeconds) {
|
||||
// this.canSubmit = true
|
||||
// }
|
||||
}
|
||||
}, 1000)
|
||||
},
|
||||
@ -222,75 +293,80 @@ export default {
|
||||
this.processTimer = null
|
||||
}
|
||||
|
||||
// 每15秒调用一次processPaper接口
|
||||
// 定期调用processPaper接口
|
||||
this.processTimer = setInterval(async () => {
|
||||
try {
|
||||
// 正确传递对象参数 { paperId: paperId }
|
||||
const result = await window.electronAPI.examingProcessPaper({ paperId: paperId })
|
||||
// 将返回的paper对象同步到store中
|
||||
// 计算当前已用时间(秒)
|
||||
let duration_seconds = 0
|
||||
let shouldUpdateDuration = false
|
||||
|
||||
if (this.paperData && this.paperData.paper_minutes && this.countdown) {
|
||||
// 将countdown字符串转换为剩余秒数
|
||||
const timeParts = this.countdown.split(':')
|
||||
if (timeParts.length === 3) {
|
||||
const hours = parseInt(timeParts[0], 10)
|
||||
const minutes = parseInt(timeParts[1], 10)
|
||||
const seconds = parseInt(timeParts[2], 10)
|
||||
const remainingSeconds = hours * 3600 + minutes * 60 + seconds
|
||||
|
||||
// 计算已用时:总时长秒数减去剩余秒数
|
||||
duration_seconds = this.paperData.paper_minutes * 60 - remainingSeconds
|
||||
duration_seconds = Math.max(0, duration_seconds)
|
||||
|
||||
// 关键逻辑:只有当计算出的duration_seconds大于数据库中的值时才更新
|
||||
const dbDurationSeconds = this.paperData.paper_duration_seconds || 0
|
||||
shouldUpdateDuration = duration_seconds > dbDurationSeconds
|
||||
|
||||
if (!shouldUpdateDuration) {
|
||||
console.log(`跳过更新paper_duration_seconds: 计算值(${duration_seconds})不大于数据库值(${dbDurationSeconds})`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 构建参数 - 只有当需要更新duration_seconds时才传递该参数
|
||||
const params = { paperId: paperId }
|
||||
if (shouldUpdateDuration) {
|
||||
params.duration_seconds = duration_seconds
|
||||
}
|
||||
|
||||
const result = await window.electronAPI.examingProcessPaper(params)
|
||||
// 修改:不再保存到store,而是保存到组件本地
|
||||
if (result.success && result.data) {
|
||||
this.$store.commit('setPaper', result.data)
|
||||
// 更新本地数据时,确保保留较大的paper_duration_seconds值
|
||||
const newData = result.data
|
||||
const currentDuration = this.paperData.paper_duration_seconds || 0
|
||||
const newDuration = newData.paper_duration_seconds || 0
|
||||
|
||||
// 如果新数据中的duration_seconds小于当前值,则保留当前值
|
||||
if (newDuration < currentDuration) {
|
||||
newData.paper_duration_seconds = currentDuration
|
||||
}
|
||||
|
||||
this.paperData = newData
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('自动更新试卷状态失败:', error)
|
||||
// 错误不中断定时器,继续尝试下一次
|
||||
}
|
||||
}, 15000) // 15秒 = 15000毫秒
|
||||
}, 5000) // 5秒 = 5000毫秒
|
||||
},
|
||||
|
||||
// 加载试卷信息的方法
|
||||
// 加载试卷信息的方法 - 简化版,只获取数据不修改
|
||||
async loadPaperInfo(paperId) {
|
||||
try {
|
||||
console.log('开始加载试卷信息,paperId:', paperId)
|
||||
|
||||
// 清除可能存在的旧试卷信息
|
||||
this.$store.commit('clearPaper')
|
||||
|
||||
// 1. 先获取试卷的当前状态
|
||||
// 只获取试卷信息
|
||||
const result = await window.electronAPI.examingProcessPaper({ paperId: paperId })
|
||||
console.log('loadPaperInfo结果:', result)
|
||||
|
||||
if (result && result.success && result.data) {
|
||||
// 2. 检查试卷状态,如果是未开始(0),则调用startPaper开始考试
|
||||
if (result.data.paper_status === 0) {
|
||||
console.log('试卷状态为未开始,调用startPaper开始考试')
|
||||
const startResult = await window.electronAPI.examingStartPaper({ paperId: paperId })
|
||||
console.log('开始考试结果:', startResult);
|
||||
if (startResult && startResult.success && startResult.data) {
|
||||
// 使用开始考试后的数据
|
||||
this.$store.commit('setPaper', startResult.data)
|
||||
this.canSubmit = startResult.data.paper_minutes_min ? false : true
|
||||
} else {
|
||||
console.error('开始考试失败')
|
||||
this.$store.commit('setPaper', result.data)
|
||||
this.canSubmit = result.data.paper_minutes_min ? false : true
|
||||
}
|
||||
} else {
|
||||
// 如果不是未开始状态,直接使用返回的数据
|
||||
this.$store.commit('setPaper', result.data)
|
||||
this.canSubmit = result.data.paper_minutes_min ? false : true
|
||||
}
|
||||
} else {
|
||||
console.error('加载试卷信息失败: 返回数据无效')
|
||||
// 如果无法获取试卷信息,创建一个默认的试卷对象以避免页面白屏
|
||||
this.$store.commit('setPaper', {
|
||||
id: paperId,
|
||||
paper_score: 0,
|
||||
paper_minutes: 0,
|
||||
paper_minutes_min: 0
|
||||
})
|
||||
this.canSubmit = false
|
||||
// 保存到组件本地
|
||||
this.paperData = result.data
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载试卷信息失败:', error)
|
||||
// 错误情况下也创建一个默认的试卷对象
|
||||
this.$store.commit('setPaper', {
|
||||
id: paperId,
|
||||
paper_score: 0,
|
||||
paper_minutes: 0,
|
||||
paper_minutes_min: 0
|
||||
})
|
||||
this.canSubmit = false
|
||||
this.$message.error(`加载试卷信息失败: ${error.message || '未知错误'}`)
|
||||
}
|
||||
},
|
||||
|
||||
@ -624,10 +700,10 @@ export default {
|
||||
// 交卷方法 - 确保数据完全保存到store后再跳转
|
||||
async submitExam() {
|
||||
try {
|
||||
if (!this.canSubmit) {
|
||||
this.$message.warning('您的答案尚未保存,请等待保存完成后再提交');
|
||||
return;
|
||||
}
|
||||
if (!this.canSubmitComputed) {
|
||||
this.$message.warning('您的答案尚未保存,请等待保存完成后再提交');
|
||||
return;
|
||||
}
|
||||
|
||||
// 显示确认对话框
|
||||
const confirmResult = await this.$confirm('确定要提交试卷吗?提交后将无法继续答题。', '确认提交', {
|
||||
@ -639,8 +715,29 @@ export default {
|
||||
// 保存当前答案
|
||||
await this.saveCurrentAnswer();
|
||||
|
||||
// 调用提交试卷API
|
||||
const result = await window.electronAPI.examingSubmitPaper({ paperId: this.paper.id });
|
||||
// 修改:不再使用start_time和currentTime计算duration_seconds
|
||||
// 改为使用paper_minutes与倒计时的差值计算
|
||||
let durationSeconds = 0
|
||||
if (this.paperData && this.paperData.paper_minutes && this.countdown) {
|
||||
// 将countdown字符串转换为剩余秒数
|
||||
const timeParts = this.countdown.split(':')
|
||||
if (timeParts.length === 3) {
|
||||
const hours = parseInt(timeParts[0], 10)
|
||||
const minutes = parseInt(timeParts[1], 10)
|
||||
const seconds = parseInt(timeParts[2], 10)
|
||||
const remainingSeconds = hours * 3600 + minutes * 60 + seconds
|
||||
|
||||
// 计算已用时:总时长秒数减去剩余秒数
|
||||
durationSeconds = this.paperData.paper_minutes * 60 - remainingSeconds
|
||||
durationSeconds = Math.max(0, durationSeconds)
|
||||
}
|
||||
}
|
||||
|
||||
// 调用提交试卷API,传递答题时长
|
||||
const result = await window.electronAPI.examingSubmitPaper({
|
||||
paperId: this.paperData.id,
|
||||
duration_seconds: durationSeconds
|
||||
});
|
||||
console.log('提交试卷API返回:', result);
|
||||
|
||||
if (result && result.success) {
|
||||
@ -649,12 +746,12 @@ export default {
|
||||
|
||||
try {
|
||||
// 尝试解析数据
|
||||
const paperData = JSON.parse(paperDataStr);
|
||||
// 1. 将试卷数据保存到store
|
||||
this.$store.commit('setPaper', paperData);
|
||||
// const paperData = JSON.parse(paperDataStr);
|
||||
// 1. 移除:不再保存到store
|
||||
// this.$store.commit('setPaper', paperData);
|
||||
|
||||
// 2. 同时保存到localStorage作为备用
|
||||
localStorage.setItem('lastExamPaper', paperDataStr);
|
||||
// 2. 同时保存到localStorage作为备用
|
||||
localStorage.setItem('lastExamPaper', paperDataStr);
|
||||
|
||||
// 移除:PDF生成代码已移至EndView.vue
|
||||
|
||||
@ -664,7 +761,7 @@ export default {
|
||||
setTimeout(() => {
|
||||
this.$router.push({
|
||||
name: 'End',
|
||||
params: { paperId: this.paper.id }
|
||||
params: { paperId: this.paperData.id }
|
||||
});
|
||||
}, 300); // 增加延迟时间到300ms
|
||||
} catch (jsonError) {
|
||||
@ -672,13 +769,14 @@ export default {
|
||||
this.$message.error('处理试卷数据失败');
|
||||
|
||||
// 即使解析失败,也尝试保存数据
|
||||
this.$store.commit('setPaper', { id: this.paper.id, rawData: result.data });
|
||||
localStorage.setItem('lastExamPaper', paperDataStr);
|
||||
// 移除:不再保存到store
|
||||
// this.$store.commit('setPaper', { id: this.paperData.id, rawData: result.data });
|
||||
localStorage.setItem('lastExamPaper', paperDataStr);
|
||||
|
||||
setTimeout(() => {
|
||||
this.$router.push({
|
||||
name: 'End',
|
||||
params: { paperId: this.paper.id }
|
||||
params: { paperId: this.paperData.id }
|
||||
});
|
||||
}, 300);
|
||||
}
|
||||
@ -702,13 +800,13 @@ export default {
|
||||
await this.saveCurrentAnswer()
|
||||
|
||||
// 修复:使用与high_version一致的接口调用方式
|
||||
const result = await window.electronAPI.endPaper(this.paper.id)
|
||||
const result = await window.electronAPI.examingEndPaper(this.paper.id)
|
||||
|
||||
if (result && result.success) {
|
||||
// 将返回的paper对象同步到store中
|
||||
if (result.data) {
|
||||
this.$store.commit('setPaper', result.data)
|
||||
}
|
||||
// if (result.data) {
|
||||
// this.$store.commit('setPaper', result.data)
|
||||
// }
|
||||
// 清理定时器
|
||||
if (this.processTimer) {
|
||||
clearInterval(this.processTimer)
|
||||
|
Loading…
Reference in New Issue
Block a user