完成了考试全流程,包括答题、交卷、抢卷、判分、生成pdf
This commit is contained in:
parent
1da167638a
commit
e2b5844fec
2
.gitignore
vendored
2
.gitignore
vendored
@ -29,3 +29,5 @@ dist-electron
|
|||||||
*.sw?
|
*.sw?
|
||||||
|
|
||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
*.pdf
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -203,7 +203,7 @@ export const userSchema = {
|
|||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
examinee_id INTEGER NOT NULL,
|
examinee_id INTEGER NOT NULL,
|
||||||
paper_minutes INTEGER NOT NULL DEFAULT 0,
|
paper_minutes INTEGER NOT NULL DEFAULT 0,
|
||||||
paper_minuts_min INTEGER NOT NULL DEFAULT 0,
|
paper_minutes_min INTEGER NOT NULL DEFAULT 0,
|
||||||
paper_start_time TEXT,
|
paper_start_time TEXT,
|
||||||
paper_last_time TEXT,
|
paper_last_time TEXT,
|
||||||
paper_submit_time TEXT,
|
paper_submit_time TEXT,
|
||||||
|
BIN
electron/font/SourceHanSansSC-Bold.otf
Normal file
BIN
electron/font/SourceHanSansSC-Bold.otf
Normal file
Binary file not shown.
BIN
electron/font/SourceHanSansSC-ExtraLight.otf
Normal file
BIN
electron/font/SourceHanSansSC-ExtraLight.otf
Normal file
Binary file not shown.
BIN
electron/font/SourceHanSansSC-Heavy.otf
Normal file
BIN
electron/font/SourceHanSansSC-Heavy.otf
Normal file
Binary file not shown.
BIN
electron/font/SourceHanSansSC-Light.otf
Normal file
BIN
electron/font/SourceHanSansSC-Light.otf
Normal file
Binary file not shown.
BIN
electron/font/SourceHanSansSC-Medium.otf
Normal file
BIN
electron/font/SourceHanSansSC-Medium.otf
Normal file
Binary file not shown.
BIN
electron/font/SourceHanSansSC-Normal.otf
Normal file
BIN
electron/font/SourceHanSansSC-Normal.otf
Normal file
Binary file not shown.
BIN
electron/font/SourceHanSansSC-Regular.otf
Normal file
BIN
electron/font/SourceHanSansSC-Regular.otf
Normal file
Binary file not shown.
BIN
electron/font/simsun.ttc
Normal file
BIN
electron/font/simsun.ttc
Normal file
Binary file not shown.
BIN
electron/font/simsun.ttf
Normal file
BIN
electron/font/simsun.ttf
Normal file
Binary file not shown.
@ -31,6 +31,10 @@ import {
|
|||||||
initExamingIpc
|
initExamingIpc
|
||||||
} from "./service/examingService.js";
|
} from "./service/examingService.js";
|
||||||
|
|
||||||
|
import {
|
||||||
|
initFileIpc
|
||||||
|
} from "./service/fileService.js";
|
||||||
|
|
||||||
// 定义 __dirname 和 __filename
|
// 定义 __dirname 和 __filename
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
const __dirname = path.dirname(__filename);
|
const __dirname = path.dirname(__filename);
|
||||||
@ -163,6 +167,8 @@ function setupIpcMain() {
|
|||||||
initExamineeIpc(ipcMain);
|
initExamineeIpc(ipcMain);
|
||||||
|
|
||||||
initExamingIpc(ipcMain);
|
initExamingIpc(ipcMain);
|
||||||
|
|
||||||
|
initFileIpc(ipcMain);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 确保在 app.whenReady() 中调用 setupIpcMain()
|
// 确保在 app.whenReady() 中调用 setupIpcMain()
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
const { contextBridge, ipcRenderer } = require('electron');
|
const { contextBridge, ipcRenderer } = require('electron');
|
||||||
|
|
||||||
// 暴露API给渲染进程
|
// 暴露API给渲染进程
|
||||||
|
// 在contextBridge.exposeInMainWorld中添加以下方法
|
||||||
contextBridge.exposeInMainWorld('electronAPI', {
|
contextBridge.exposeInMainWorld('electronAPI', {
|
||||||
// 数据库相关
|
// 数据库相关
|
||||||
checkDatabaseInitialized: () => ipcRenderer.invoke('check-database-initialized'),
|
checkDatabaseInitialized: () => ipcRenderer.invoke('check-database-initialized'),
|
||||||
@ -64,9 +65,20 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
|||||||
updateExaminee: (id, examineeData) => ipcRenderer.invoke('examinee-update', { id, examineeData }),
|
updateExaminee: (id, examineeData) => ipcRenderer.invoke('examinee-update', { id, examineeData }),
|
||||||
deleteExaminee: (id) => ipcRenderer.invoke('examinee-delete', id),
|
deleteExaminee: (id) => ipcRenderer.invoke('examinee-delete', id),
|
||||||
userLogin: (idCard, admissionTicket) => ipcRenderer.invoke('user-login', { idCard, admissionTicket }),
|
userLogin: (idCard, admissionTicket) => ipcRenderer.invoke('user-login', { idCard, admissionTicket }),
|
||||||
|
|
||||||
// 考试进行相关API
|
// 考试进行相关API
|
||||||
generateExamineePaper: (examineeData, examData) => ipcRenderer.invoke('examing-generate-paper', { examineeData, examData }),
|
generateExamineePaper: (examineeData, examData) => ipcRenderer.invoke('examing-generate-paper', { examineeData, examData }),
|
||||||
getExamineePaperStatus: (examineeId) => ipcRenderer.invoke('examing-get-paper-status', examineeId),
|
getExamineePaperStatus: (examineeId) => ipcRenderer.invoke('examing-get-paper-status', examineeId),
|
||||||
updatePaperStatus: (paperId, statusData) => ipcRenderer.invoke('examing-update-paper-status', { paperId, statusData }),
|
updatePaperStatus: (paperId, statusData) => ipcRenderer.invoke('examing-update-paper-status', { paperId, statusData }),
|
||||||
|
loadPaperSerial: (paperId) => ipcRenderer.invoke('examing-load-paper-serial', paperId),
|
||||||
|
getQuestionByRelatedId: (tableName, id) => ipcRenderer.invoke('examing-get-question-by-related-id', { tableName, id }),
|
||||||
|
updateExamineeAnswer: (tableName, id, answers) => ipcRenderer.invoke('examing-update-answer', { tableName, id, answers }),
|
||||||
|
startPaper: (paperId) => ipcRenderer.invoke('examing-start-paper', paperId),
|
||||||
|
submitPaper: (paperId) => ipcRenderer.invoke('examing-submit-paper', paperId),
|
||||||
|
endPaper: (paperId) => ipcRenderer.invoke('examing-end-paper', paperId),
|
||||||
|
processPaper: (paperId) => ipcRenderer.invoke('examing-process-paper', paperId),
|
||||||
|
checkPaperAnswers: (paperId) => ipcRenderer.invoke('examing-check-paper-answers', paperId), // 添加暴露接口
|
||||||
|
// 文件相关API
|
||||||
|
generatePdf: (pdfData, fileName) => ipcRenderer.invoke('file-generate-pdf', pdfData, fileName),
|
||||||
|
fileTest: () => ipcRenderer.invoke('file-test'),
|
||||||
|
generatePaperPdf: (jsonString) => ipcRenderer.invoke('file-generate-paper-pdf', jsonString),
|
||||||
});
|
});
|
@ -1,7 +1,17 @@
|
|||||||
// 导入数据库操作函数
|
// 导入数据库操作函数
|
||||||
import { generateExamineePaper } from '../db/examing.js';
|
import {
|
||||||
import { getDbConnection, closeAllConnections } from '../db/index.js';
|
generateExamineePaper,
|
||||||
import { getUserDbPath } from '../db/path.js';
|
loadPaperSerial,
|
||||||
|
getQuestionByRelatedId,
|
||||||
|
updateExamineeAnswer,
|
||||||
|
startPaper,
|
||||||
|
submitPaper,
|
||||||
|
endPaper,
|
||||||
|
processPaper,
|
||||||
|
checkPaperAnswers, // 添加导入
|
||||||
|
} from "../db/examing.js";
|
||||||
|
import { getDbConnection, closeAllConnections } from "../db/index.js";
|
||||||
|
import { getUserDbPath } from "../db/path.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 服务层:生成考生试卷
|
* 服务层:生成考生试卷
|
||||||
@ -14,24 +24,27 @@ export async function generateExamineePaperService(examineeData, examData) {
|
|||||||
try {
|
try {
|
||||||
// 数据验证
|
// 数据验证
|
||||||
if (!examineeData || !examineeData.id || !examineeData.examinee_name) {
|
if (!examineeData || !examineeData.id || !examineeData.examinee_name) {
|
||||||
throw new Error('考生数据不完整,必须包含ID和姓名');
|
throw new Error("考生数据不完整,必须包含ID和姓名");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!examData || !examData.exam_minutes || examData.exam_minutes <= 0) {
|
if (!examData || !examData.exam_minutes || examData.exam_minutes <= 0) {
|
||||||
throw new Error('考试时长必须为正数');
|
throw new Error("考试时长必须为正数");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (examData.exam_minutes_min === undefined || examData.exam_minutes_min < 0) {
|
if (
|
||||||
throw new Error('最短考试时长必须为非负数');
|
examData.exam_minutes_min === undefined ||
|
||||||
|
examData.exam_minutes_min < 0
|
||||||
|
) {
|
||||||
|
throw new Error("最短考试时长必须为非负数");
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await generateExamineePaper(examineeData, examData);
|
const result = await generateExamineePaper(examineeData, examData);
|
||||||
return result;
|
return result;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('服务层: 生成考生试卷失败', error);
|
console.error("服务层: 生成考生试卷失败", error);
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
message: `生成试卷失败: ${error.message}`
|
message: `生成试卷失败: ${error.message}`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -44,18 +57,18 @@ export async function generateExamineePaperService(examineeData, examData) {
|
|||||||
export async function getExamineePaperStatusService(examineeId) {
|
export async function getExamineePaperStatusService(examineeId) {
|
||||||
try {
|
try {
|
||||||
if (!examineeId || examineeId <= 0) {
|
if (!examineeId || examineeId <= 0) {
|
||||||
throw new Error('考生ID必须为正数');
|
throw new Error("考生ID必须为正数");
|
||||||
}
|
}
|
||||||
|
|
||||||
const userDb = await getDbConnection(getUserDbPath());
|
const userDb = await getDbConnection(getUserDbPath());
|
||||||
const paperStatus = await userDb.getAsync(
|
const paperStatus = await userDb.getAsync(
|
||||||
'SELECT * FROM examinee_papers WHERE examinee_id = ?',
|
"SELECT * FROM examinee_papers WHERE examinee_id = ?",
|
||||||
[examineeId]
|
[examineeId]
|
||||||
);
|
);
|
||||||
|
|
||||||
return paperStatus;
|
return paperStatus;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('服务层: 获取考生试卷状态失败', error);
|
console.error("服务层: 获取考生试卷状态失败", error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -69,7 +82,7 @@ export async function getExamineePaperStatusService(examineeId) {
|
|||||||
export async function updatePaperStatusService(paperId, statusData) {
|
export async function updatePaperStatusService(paperId, statusData) {
|
||||||
try {
|
try {
|
||||||
if (!paperId || paperId <= 0) {
|
if (!paperId || paperId <= 0) {
|
||||||
throw new Error('试卷ID必须为正数');
|
throw new Error("试卷ID必须为正数");
|
||||||
}
|
}
|
||||||
|
|
||||||
const userDb = await getDbConnection(getUserDbPath());
|
const userDb = await getDbConnection(getUserDbPath());
|
||||||
@ -79,32 +92,32 @@ export async function updatePaperStatusService(paperId, statusData) {
|
|||||||
const values = [];
|
const values = [];
|
||||||
|
|
||||||
if (statusData.paper_start_time !== undefined) {
|
if (statusData.paper_start_time !== undefined) {
|
||||||
fields.push('paper_start_time = ?');
|
fields.push("paper_start_time = ?");
|
||||||
values.push(statusData.paper_start_time);
|
values.push(statusData.paper_start_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (statusData.paper_last_time !== undefined) {
|
if (statusData.paper_last_time !== undefined) {
|
||||||
fields.push('paper_last_time = ?');
|
fields.push("paper_last_time = ?");
|
||||||
values.push(statusData.paper_last_time);
|
values.push(statusData.paper_last_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (statusData.paper_submit_time !== undefined) {
|
if (statusData.paper_submit_time !== undefined) {
|
||||||
fields.push('paper_submit_time = ?');
|
fields.push("paper_submit_time = ?");
|
||||||
values.push(statusData.paper_submit_time);
|
values.push(statusData.paper_submit_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (statusData.paper_end_time !== undefined) {
|
if (statusData.paper_end_time !== undefined) {
|
||||||
fields.push('paper_end_time = ?');
|
fields.push("paper_end_time = ?");
|
||||||
values.push(statusData.paper_end_time);
|
values.push(statusData.paper_end_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (statusData.paper_status !== undefined) {
|
if (statusData.paper_status !== undefined) {
|
||||||
fields.push('paper_status = ?');
|
fields.push("paper_status = ?");
|
||||||
values.push(statusData.paper_status);
|
values.push(statusData.paper_status);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (statusData.paper_score_real !== undefined) {
|
if (statusData.paper_score_real !== undefined) {
|
||||||
fields.push('paper_score_real = ?');
|
fields.push("paper_score_real = ?");
|
||||||
values.push(statusData.paper_score_real);
|
values.push(statusData.paper_score_real);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,51 +128,365 @@ export async function updatePaperStatusService(paperId, statusData) {
|
|||||||
// 添加WHERE条件的值
|
// 添加WHERE条件的值
|
||||||
values.push(paperId);
|
values.push(paperId);
|
||||||
|
|
||||||
const sql = `UPDATE examinee_papers SET ${fields.join(', ')} WHERE id = ?`;
|
const sql = `UPDATE examinee_papers SET ${fields.join(", ")} WHERE id = ?`;
|
||||||
await userDb.runAsync(sql, values);
|
await userDb.runAsync(sql, values);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('服务层: 更新试卷状态失败', error);
|
console.error("服务层: 更新试卷状态失败", error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务层:加载试卷试题序列
|
||||||
|
* @param {number} paperId - 试卷ID
|
||||||
|
* @returns {Promise<Array>} - 包含试题序列的数组
|
||||||
|
*/
|
||||||
|
export async function loadPaperSerialService(paperId) {
|
||||||
|
try {
|
||||||
|
if (!paperId || paperId <= 0) {
|
||||||
|
throw new Error("试卷ID必须为正数");
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await loadPaperSerial(paperId);
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
data: result,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error("服务层: 加载试卷试题序列失败", error);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: `加载试卷试题序列失败: ${error.message}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务层:根据表名和ID获取完整的试题数据
|
||||||
|
* @param {string} tableName - 表名 (question_choices 或 question_fill_blanks)
|
||||||
|
* @param {number} id - 记录ID
|
||||||
|
* @returns {Promise<Object>} - 包含试题数据的对象
|
||||||
|
*/
|
||||||
|
export async function getQuestionByRelatedIdService(tableName, id) {
|
||||||
|
try {
|
||||||
|
// if (!tableName || !['question_choices', 'question_fill_blanks'].includes(tableName)) {
|
||||||
|
// throw new Error('无效的表名,只能是 question_choices 或 question_fill_blanks');
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (!id || id <= 0) {
|
||||||
|
// throw new Error('记录ID必须为正数');
|
||||||
|
// }
|
||||||
|
|
||||||
|
const result = await getQuestionByRelatedId(tableName, id);
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
data: result,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error("服务层: 获取试题数据失败", error);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: `获取试题数据失败: ${error.message}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务层:更新考生答案
|
||||||
|
* @param {string} tableName - 表名 (question_choices 或 question_fill_blanks)
|
||||||
|
* @param {number} id - 记录ID
|
||||||
|
* @param {Array|string} answers - 考生答案
|
||||||
|
* @returns {Promise<Object>} - 包含更新结果的对象
|
||||||
|
*/
|
||||||
|
export async function updateExamineeAnswerService(tableName, id, answers) {
|
||||||
|
try {
|
||||||
|
if (!["question_choices", "question_fill_blanks"].includes(tableName)) {
|
||||||
|
throw new Error(
|
||||||
|
"无效的表名,只能是 question_choices 或 question_fill_blanks"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!id || id <= 0) {
|
||||||
|
throw new Error("记录ID必须为正数");
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await updateExamineeAnswer(tableName, id, answers);
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: "答案更新成功",
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error("服务层: 更新考生答案失败", error);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: `更新答案失败: ${error.message}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务层:开始考试
|
||||||
|
* @param {number} paperId - 试卷ID
|
||||||
|
* @returns {Promise<Object>} - 包含操作结果的对象
|
||||||
|
*/
|
||||||
|
export async function startPaperService(paperId) {
|
||||||
|
try {
|
||||||
|
if (!paperId || paperId <= 0) {
|
||||||
|
throw new Error("试卷ID必须为正数");
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await startPaper(paperId);
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("服务层: 开始考试失败", error);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: `开始考试失败: ${error.message}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务层:提交考试
|
||||||
|
* @param {number} paperId - 试卷ID
|
||||||
|
* @returns {Promise<Object>} - 包含操作结果的对象
|
||||||
|
*/
|
||||||
|
export async function submitPaperService(paperId) {
|
||||||
|
try {
|
||||||
|
if (!paperId || paperId <= 0) {
|
||||||
|
throw new Error("试卷ID必须为正数");
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await submitPaper(paperId);
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("服务层: 提交考试失败", error);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: `提交考试失败: ${error.message}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务层:结束考试
|
||||||
|
* @param {number} paperId - 试卷ID
|
||||||
|
* @returns {Promise<Object>} - 包含操作结果的对象
|
||||||
|
*/
|
||||||
|
export async function endPaperService(paperId) {
|
||||||
|
try {
|
||||||
|
if (!paperId || paperId <= 0) {
|
||||||
|
throw new Error("试卷ID必须为正数");
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await endPaper(paperId);
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("服务层: 结束考试失败", error);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: `结束考试失败: ${error.message}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务层:处理试卷
|
||||||
|
* @param {number} paperId - 试卷ID
|
||||||
|
* @returns {Promise<Object>} - 包含操作结果的对象
|
||||||
|
*/
|
||||||
|
export async function processPaperService(paperId) {
|
||||||
|
try {
|
||||||
|
if (!paperId || paperId <= 0) {
|
||||||
|
throw new Error("试卷ID必须为正数");
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await processPaper(paperId);
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("服务层: 处理试卷失败", error);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: `处理试卷失败: ${error.message}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务层:检查试卷答案并计算得分
|
||||||
|
* @param {number} paperId - 试卷ID
|
||||||
|
* @returns {Promise<Object>} - 包含操作结果和试卷数据的对象
|
||||||
|
*/
|
||||||
|
export async function checkPaperAnswersService(paperId) {
|
||||||
|
try {
|
||||||
|
if (!paperId || paperId <= 0) {
|
||||||
|
throw new Error("试卷ID必须为正数");
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await checkPaperAnswers(paperId);
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("服务层: 检查试卷答案失败", error);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: `检查试卷答案失败: ${error.message}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化考试相关的IPC处理程序
|
* 初始化考试相关的IPC处理程序
|
||||||
* @param {import('electron').IpcMain} ipcMain - IPC主进程实例
|
* @param {import('electron').IpcMain} ipcMain - IPC主进程实例
|
||||||
*/
|
*/
|
||||||
export function initExamingIpc(ipcMain) {
|
export function initExamingIpc(ipcMain) {
|
||||||
// 生成考生试卷
|
// 生成考生试卷
|
||||||
ipcMain.handle('examing-generate-paper', async (event, { examineeData, examData }) => {
|
ipcMain.handle(
|
||||||
try {
|
"examing-generate-paper",
|
||||||
return await generateExamineePaperService(examineeData, examData);
|
async (event, { examineeData, examData }) => {
|
||||||
} catch (error) {
|
try {
|
||||||
console.error('生成考生试卷失败:', error);
|
return await generateExamineePaperService(examineeData, examData);
|
||||||
return {
|
} catch (error) {
|
||||||
success: false,
|
console.error("生成考生试卷失败:", error);
|
||||||
message: `生成试卷失败: ${error.message}`
|
return {
|
||||||
};
|
success: false,
|
||||||
|
message: `生成试卷失败: ${error.message}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
|
|
||||||
// 获取考生试卷状态
|
// 获取考生试卷状态
|
||||||
ipcMain.handle('examing-get-paper-status', async (event, examineeId) => {
|
ipcMain.handle("examing-get-paper-status", async (event, examineeId) => {
|
||||||
try {
|
try {
|
||||||
return await getExamineePaperStatusService(examineeId);
|
return await getExamineePaperStatusService(examineeId);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取考生试卷状态失败:', error);
|
console.error("获取考生试卷状态失败:", error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 更新试卷状态
|
// 更新试卷状态
|
||||||
ipcMain.handle('examing-update-paper-status', async (event, { paperId, statusData }) => {
|
ipcMain.handle(
|
||||||
|
"examing-update-paper-status",
|
||||||
|
async (event, { paperId, statusData }) => {
|
||||||
|
try {
|
||||||
|
return await updatePaperStatusService(paperId, statusData);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("更新试卷状态失败:", error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// 加载试卷试题序列
|
||||||
|
ipcMain.handle("examing-load-paper-serial", async (event, paperId) => {
|
||||||
try {
|
try {
|
||||||
return await updatePaperStatusService(paperId, statusData);
|
return await loadPaperSerialService(paperId);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('更新试卷状态失败:', error);
|
console.error("加载试卷试题序列失败:", error);
|
||||||
return false;
|
return {
|
||||||
|
success: false,
|
||||||
|
message: `加载试卷试题序列失败: ${error.message}`,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
// 根据表名和ID获取完整的试题数据
|
||||||
|
ipcMain.handle(
|
||||||
|
"examing-get-question-by-related-id",
|
||||||
|
async (event, { tableName, id }) => {
|
||||||
|
try {
|
||||||
|
return await getQuestionByRelatedIdService(tableName, id);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("获取试题数据失败:", error);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: `获取试题数据失败: ${error.message}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// 更新考生答案
|
||||||
|
ipcMain.handle(
|
||||||
|
"examing-update-answer",
|
||||||
|
async (event, { tableName, id, answers }) => {
|
||||||
|
try {
|
||||||
|
return await updateExamineeAnswerService(tableName, id, answers);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("更新考生答案失败:", error);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: `更新答案失败: ${error.message}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// 更新导入语句
|
||||||
|
ipcMain.handle("examing-start-paper", async (event, paperId) => {
|
||||||
|
try {
|
||||||
|
return await startPaperService(paperId);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("开始考试失败:", error);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: `开始考试失败: ${error.message}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 提交考试
|
||||||
|
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-end-paper", async (event, paperId) => {
|
||||||
|
try {
|
||||||
|
return await endPaperService(paperId);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("结束考试失败:", error);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: `结束考试失败: ${error.message}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 处理试卷
|
||||||
|
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-check-paper-answers", async (event, paperId) => {
|
||||||
|
try {
|
||||||
|
return await checkPaperAnswersService(paperId);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("检查试卷答案失败:", error);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: `检查试卷答案失败: ${error.message}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
1381
electron/service/fileService.js
Normal file
1381
electron/service/fileService.js
Normal file
File diff suppressed because one or more lines are too long
2
output/.gitignore
vendored
Normal file
2
output/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
*
|
||||||
|
!.gitignore
|
145
package-lock.json
generated
145
package-lock.json
generated
@ -19,6 +19,7 @@
|
|||||||
"bootstrap": "^5.3.7",
|
"bootstrap": "^5.3.7",
|
||||||
"element-plus": "^2.10.5",
|
"element-plus": "^2.10.5",
|
||||||
"fs": "^0.0.1-security",
|
"fs": "^0.0.1-security",
|
||||||
|
"pdfkit": "^0.17.1",
|
||||||
"popper.js": "^1.16.1",
|
"popper.js": "^1.16.1",
|
||||||
"sqlite": "^5.1.1",
|
"sqlite": "^5.1.1",
|
||||||
"sqlite3": "^5.1.7",
|
"sqlite3": "^5.1.7",
|
||||||
@ -2464,6 +2465,15 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@swc/helpers": {
|
||||||
|
"version": "0.5.17",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@swc/helpers/-/helpers-0.5.17.tgz",
|
||||||
|
"integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@szmarczak/http-timer": {
|
"node_modules/@szmarczak/http-timer": {
|
||||||
"version": "4.0.6",
|
"version": "4.0.6",
|
||||||
"resolved": "https://registry.npmmirror.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz",
|
"resolved": "https://registry.npmmirror.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz",
|
||||||
@ -3420,6 +3430,15 @@
|
|||||||
"concat-map": "0.0.1"
|
"concat-map": "0.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/brotli": {
|
||||||
|
"version": "1.3.3",
|
||||||
|
"resolved": "https://registry.npmmirror.com/brotli/-/brotli-1.3.3.tgz",
|
||||||
|
"integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"base64-js": "^1.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/browserslist": {
|
"node_modules/browserslist": {
|
||||||
"version": "4.25.1",
|
"version": "4.25.1",
|
||||||
"resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.25.1.tgz",
|
"resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.25.1.tgz",
|
||||||
@ -4152,6 +4171,12 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/crypto-js": {
|
||||||
|
"version": "4.2.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/crypto-js/-/crypto-js-4.2.0.tgz",
|
||||||
|
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/csstype": {
|
"node_modules/csstype": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz",
|
"resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz",
|
||||||
@ -4344,6 +4369,12 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
"node_modules/dfa": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/dfa/-/dfa-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/dir-compare": {
|
"node_modules/dir-compare": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmmirror.com/dir-compare/-/dir-compare-4.2.0.tgz",
|
"resolved": "https://registry.npmmirror.com/dir-compare/-/dir-compare-4.2.0.tgz",
|
||||||
@ -5022,7 +5053,6 @@
|
|||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
"resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/fast-json-stable-stringify": {
|
"node_modules/fast-json-stable-stringify": {
|
||||||
@ -5112,6 +5142,32 @@
|
|||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/fontkit": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmmirror.com/fontkit/-/fontkit-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@swc/helpers": "^0.5.12",
|
||||||
|
"brotli": "^1.3.2",
|
||||||
|
"clone": "^2.1.2",
|
||||||
|
"dfa": "^1.2.0",
|
||||||
|
"fast-deep-equal": "^3.1.3",
|
||||||
|
"restructure": "^3.0.0",
|
||||||
|
"tiny-inflate": "^1.0.3",
|
||||||
|
"unicode-properties": "^1.4.0",
|
||||||
|
"unicode-trie": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fontkit/node_modules/clone": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/clone/-/clone-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/foreground-child": {
|
"node_modules/foreground-child": {
|
||||||
"version": "3.3.1",
|
"version": "3.3.1",
|
||||||
"resolved": "https://registry.npmmirror.com/foreground-child/-/foreground-child-3.3.1.tgz",
|
"resolved": "https://registry.npmmirror.com/foreground-child/-/foreground-child-3.3.1.tgz",
|
||||||
@ -5960,6 +6016,12 @@
|
|||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/jpeg-exif": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmmirror.com/jpeg-exif/-/jpeg-exif-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-a+bKEcCjtuW5WTdgeXFzswSrdqi0jk4XlEtZlx5A94wCoBpFjfFTbo/Tra5SpNCl/YFZPvcV1dJc+TAYeg6ROQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/js-tokens": {
|
"node_modules/js-tokens": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz",
|
"resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||||
@ -6069,6 +6131,25 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/linebreak": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/linebreak/-/linebreak-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-MHp03UImeVhB7XZtjd0E4n6+3xr5Dq/9xI/5FptGk5FrbDR3zagPa2DS6U8ks/3HjbKWG9Q1M2ufOzxV2qLYSQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"base64-js": "0.0.8",
|
||||||
|
"unicode-trie": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/linebreak/node_modules/base64-js": {
|
||||||
|
"version": "0.0.8",
|
||||||
|
"resolved": "https://registry.npmmirror.com/base64-js/-/base64-js-0.0.8.tgz",
|
||||||
|
"integrity": "sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/lodash": {
|
"node_modules/lodash": {
|
||||||
"version": "4.17.21",
|
"version": "4.17.21",
|
||||||
"resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz",
|
"resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz",
|
||||||
@ -6831,6 +6912,12 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BlueOak-1.0.0"
|
"license": "BlueOak-1.0.0"
|
||||||
},
|
},
|
||||||
|
"node_modules/pako": {
|
||||||
|
"version": "0.2.9",
|
||||||
|
"resolved": "https://registry.npmmirror.com/pako/-/pako-0.2.9.tgz",
|
||||||
|
"integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/parse-ms": {
|
"node_modules/parse-ms": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmmirror.com/parse-ms/-/parse-ms-4.0.0.tgz",
|
"resolved": "https://registry.npmmirror.com/parse-ms/-/parse-ms-4.0.0.tgz",
|
||||||
@ -6905,6 +6992,19 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/pdfkit": {
|
||||||
|
"version": "0.17.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/pdfkit/-/pdfkit-0.17.1.tgz",
|
||||||
|
"integrity": "sha512-Kkf1I9no14O/uo593DYph5u3QwiMfby7JsBSErN1WqeyTgCBNJE3K4pXBn3TgkdKUIVu+buSl4bYUNC+8Up4xg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"crypto-js": "^4.2.0",
|
||||||
|
"fontkit": "^2.0.4",
|
||||||
|
"jpeg-exif": "^1.1.4",
|
||||||
|
"linebreak": "^1.1.0",
|
||||||
|
"png-js": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/pe-library": {
|
"node_modules/pe-library": {
|
||||||
"version": "0.4.1",
|
"version": "0.4.1",
|
||||||
"resolved": "https://registry.npmmirror.com/pe-library/-/pe-library-0.4.1.tgz",
|
"resolved": "https://registry.npmmirror.com/pe-library/-/pe-library-0.4.1.tgz",
|
||||||
@ -6968,6 +7068,11 @@
|
|||||||
"node": ">=10.4.0"
|
"node": ">=10.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/png-js": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/png-js/-/png-js-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-k+YsbhpA9e+EFfKjTCH3VW6aoKlyNYI6NYdTfDL4CIvFnvsuO84ttonmZE7rc+v23SLTH8XX+5w/Ak9v0xGY4g=="
|
||||||
|
},
|
||||||
"node_modules/popper.js": {
|
"node_modules/popper.js": {
|
||||||
"version": "1.16.1",
|
"version": "1.16.1",
|
||||||
"resolved": "https://registry.npmmirror.com/popper.js/-/popper.js-1.16.1.tgz",
|
"resolved": "https://registry.npmmirror.com/popper.js/-/popper.js-1.16.1.tgz",
|
||||||
@ -7264,6 +7369,12 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/restructure": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/restructure/-/restructure-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/retry": {
|
"node_modules/retry": {
|
||||||
"version": "0.12.0",
|
"version": "0.12.0",
|
||||||
"resolved": "https://registry.npmmirror.com/retry/-/retry-0.12.0.tgz",
|
"resolved": "https://registry.npmmirror.com/retry/-/retry-0.12.0.tgz",
|
||||||
@ -8054,6 +8165,12 @@
|
|||||||
"semver": "bin/semver"
|
"semver": "bin/semver"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tiny-inflate": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmmirror.com/tiny-inflate/-/tiny-inflate-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/tinyglobby": {
|
"node_modules/tinyglobby": {
|
||||||
"version": "0.2.14",
|
"version": "0.2.14",
|
||||||
"resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.14.tgz",
|
"resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.14.tgz",
|
||||||
@ -8111,6 +8228,12 @@
|
|||||||
"utf8-byte-length": "^1.0.1"
|
"utf8-byte-length": "^1.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tslib": {
|
||||||
|
"version": "2.8.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz",
|
||||||
|
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||||
|
"license": "0BSD"
|
||||||
|
},
|
||||||
"node_modules/tunnel-agent": {
|
"node_modules/tunnel-agent": {
|
||||||
"version": "0.6.0",
|
"version": "0.6.0",
|
||||||
"resolved": "https://registry.npmmirror.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
|
"resolved": "https://registry.npmmirror.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
|
||||||
@ -8158,6 +8281,26 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/unicode-properties": {
|
||||||
|
"version": "1.4.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/unicode-properties/-/unicode-properties-1.4.1.tgz",
|
||||||
|
"integrity": "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"base64-js": "^1.3.0",
|
||||||
|
"unicode-trie": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/unicode-trie": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/unicode-trie/-/unicode-trie-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"pako": "^0.2.5",
|
||||||
|
"tiny-inflate": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/unicorn-magic": {
|
"node_modules/unicorn-magic": {
|
||||||
"version": "0.3.0",
|
"version": "0.3.0",
|
||||||
"resolved": "https://registry.npmmirror.com/unicorn-magic/-/unicorn-magic-0.3.0.tgz",
|
"resolved": "https://registry.npmmirror.com/unicorn-magic/-/unicorn-magic-0.3.0.tgz",
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
"bootstrap": "^5.3.7",
|
"bootstrap": "^5.3.7",
|
||||||
"element-plus": "^2.10.5",
|
"element-plus": "^2.10.5",
|
||||||
"fs": "^0.0.1-security",
|
"fs": "^0.0.1-security",
|
||||||
|
"pdfkit": "^0.17.1",
|
||||||
"popper.js": "^1.16.1",
|
"popper.js": "^1.16.1",
|
||||||
"sqlite": "^5.1.1",
|
"sqlite": "^5.1.1",
|
||||||
"sqlite3": "^5.1.7",
|
"sqlite3": "^5.1.7",
|
||||||
@ -56,7 +57,9 @@
|
|||||||
"target": [
|
"target": [
|
||||||
{
|
{
|
||||||
"target": "nsis",
|
"target": "nsis",
|
||||||
"arch": ["ia32"]
|
"arch": [
|
||||||
|
"ia32"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "public/favicon.ico"
|
"icon": "public/favicon.ico"
|
||||||
|
BIN
src/assets/bg.jpeg
Normal file
BIN
src/assets/bg.jpeg
Normal file
Binary file not shown.
After Width: | Height: | Size: 19 KiB |
BIN
src/assets/logo.png
Normal file
BIN
src/assets/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
@ -4,8 +4,9 @@
|
|||||||
<div class="row h-100 align-items-center justify-content-center">
|
<div class="row h-100 align-items-center justify-content-center">
|
||||||
<div class="col-auto">
|
<div class="col-auto">
|
||||||
<h2 class="display-6 m-0 d-flex align-items-center">
|
<h2 class="display-6 m-0 d-flex align-items-center">
|
||||||
<font-awesome-icon icon="graduation-cap" class="me-3" />
|
<!-- <font-awesome-icon icon="graduation-cap" class="me-3" /> -->
|
||||||
{{thisYear}}年抚顺市统计行业职工技能大赛考试系统
|
<img src="../../assets/logo.png" alt="logo" style="width:50px;height:50px;margin-right:1rem;">
|
||||||
|
{{thisYear}}<strong>年抚顺市统计行业职工技能大赛考试系统</strong>
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -16,7 +17,7 @@
|
|||||||
<style scoped>
|
<style scoped>
|
||||||
.header-container {
|
.header-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background-color: #1e40af; /* 深蓝色背景 */
|
background-color: #4f74ed; /* 深蓝色背景 */
|
||||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
height: 80px; /* 设置固定高度 */
|
height: 80px; /* 设置固定高度 */
|
||||||
padding: 0 2rem;
|
padding: 0 2rem;
|
||||||
|
@ -15,6 +15,10 @@ import ExamManagementView from '@/views/admin/ExamManagementView.vue'
|
|||||||
// 导入ExamineeManagementView
|
// 导入ExamineeManagementView
|
||||||
import ExamineeManagementView from '@/views/admin/ExamineeManagementView.vue'
|
import ExamineeManagementView from '@/views/admin/ExamineeManagementView.vue'
|
||||||
|
|
||||||
|
// 首先在文件顶部导入EndView组件
|
||||||
|
import EndView from '@/views/user/EndView.vue'
|
||||||
|
|
||||||
|
// 然后在routes数组中添加新路由
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(import.meta.env.BASE_URL),
|
history: createWebHistory(import.meta.env.BASE_URL),
|
||||||
routes: [
|
routes: [
|
||||||
@ -30,10 +34,16 @@ const router = createRouter({
|
|||||||
},
|
},
|
||||||
// Modify the examing route to use lazy loading
|
// Modify the examing route to use lazy loading
|
||||||
{
|
{
|
||||||
path: '/examinee/examing',
|
path: '/examinee/examing/:paperId',
|
||||||
name: 'examinee-examing',
|
name: 'examinee-examing',
|
||||||
component: ExamingView,
|
component: ExamingView,
|
||||||
},
|
},
|
||||||
|
// 添加EndView路由
|
||||||
|
{
|
||||||
|
path: '/examinee/end',
|
||||||
|
name: 'examinee-end',
|
||||||
|
component: EndView,
|
||||||
|
},
|
||||||
// admin/AdminHomeView路由
|
// admin/AdminHomeView路由
|
||||||
{
|
{
|
||||||
path: '/admin/home',
|
path: '/admin/home',
|
||||||
|
@ -3,7 +3,9 @@ import { reactive, provide, inject } from 'vue'
|
|||||||
// 创建用户状态
|
// 创建用户状态
|
||||||
const userState = reactive({
|
const userState = reactive({
|
||||||
examinee: null,
|
examinee: null,
|
||||||
isLoggedIn: false
|
isLoggedIn: false,
|
||||||
|
exam: null, // 考试信息属性
|
||||||
|
paper: null // 新增:试卷信息属性
|
||||||
})
|
})
|
||||||
|
|
||||||
// 创建状态管理工具
|
// 创建状态管理工具
|
||||||
@ -20,6 +22,16 @@ const userStore = {
|
|||||||
clearExaminee() {
|
clearExaminee() {
|
||||||
this.state.examinee = null
|
this.state.examinee = null
|
||||||
this.state.isLoggedIn = false
|
this.state.isLoggedIn = false
|
||||||
|
},
|
||||||
|
|
||||||
|
// 添加设置考试数据的方法
|
||||||
|
setExam(exam) {
|
||||||
|
this.state.exam = exam
|
||||||
|
},
|
||||||
|
|
||||||
|
// 新增:设置试卷数据的方法
|
||||||
|
setPaper(paper) {
|
||||||
|
this.state.paper = paper
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="welcome-container">
|
<div class="welcome-container" style="background-image: url('/src/assets/bg.jpeg'); background-size: cover; background-position: center;">
|
||||||
<el-container>
|
<el-container>
|
||||||
<Header />
|
<Header />
|
||||||
|
|
||||||
|
269
src/views/user/EndView.vue
Normal file
269
src/views/user/EndView.vue
Normal file
@ -0,0 +1,269 @@
|
|||||||
|
<template>
|
||||||
|
<div class="end-container">
|
||||||
|
<el-container>
|
||||||
|
<Header />
|
||||||
|
<!-- 主要内容区域 -->
|
||||||
|
<el-main>
|
||||||
|
<div style="width: 100%; height: 100%;">
|
||||||
|
<div class="d-flex align-items-center justify-content-center p-4" style="width:100%">
|
||||||
|
<!-- 交卷结果卡片 -->
|
||||||
|
<div class="bg-white rounded-4 shadow-lg p-4 w-100 max-w-2xl border-2 border-primary/20">
|
||||||
|
<el-result icon="success" title="考试已完成" sub-title="您已成功提交试卷,感谢您的参与!">
|
||||||
|
<template #extra>
|
||||||
|
<el-button type="primary" @click="goHome">返回首页</el-button>
|
||||||
|
</template>
|
||||||
|
</el-result>
|
||||||
|
|
||||||
|
<el-divider />
|
||||||
|
<div class="flex justify-between text-center mt-6 mb-4">
|
||||||
|
<h2 class="text-2xl font-bold text-primary">考生信息</h2>
|
||||||
|
</div>
|
||||||
|
<!-- 考生信息部分 -->
|
||||||
|
<el-descriptions class="margin-top" :column="3" :size="size" border>
|
||||||
|
<el-descriptions-item>
|
||||||
|
<template #label>
|
||||||
|
<div class="cell-item">
|
||||||
|
<el-icon :style="iconStyle">
|
||||||
|
<User />
|
||||||
|
</el-icon>
|
||||||
|
姓名
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
{{ examinee?.examinee_name }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item>
|
||||||
|
<template #label>
|
||||||
|
<div class="cell-item">
|
||||||
|
<el-icon :style="iconStyle">
|
||||||
|
<Postcard />
|
||||||
|
</el-icon>
|
||||||
|
身份证号
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
{{ formatIdCard(examinee?.examinee_id_card) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item>
|
||||||
|
<template #label>
|
||||||
|
<div class="cell-item">
|
||||||
|
<el-icon :style="iconStyle">
|
||||||
|
<Ticket />
|
||||||
|
</el-icon>
|
||||||
|
准考证号
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
{{ examinee?.examinee_admission_ticket }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
|
||||||
|
<el-divider />
|
||||||
|
<div class="flex justify-between text-center mt-6 mb-4">
|
||||||
|
<h2 class="text-2xl font-bold text-primary">考试信息</h2>
|
||||||
|
</div>
|
||||||
|
<!-- 试卷信息部分 -->
|
||||||
|
<el-descriptions class="margin-top" :column="2" :size="size" border>
|
||||||
|
<el-descriptions-item>
|
||||||
|
<template #label>
|
||||||
|
<div class="cell-item">
|
||||||
|
<el-icon :style="iconStyle">
|
||||||
|
<Clock />
|
||||||
|
</el-icon>
|
||||||
|
开始时间
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
{{ formatDateTime(paper?.paper_start_time) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item>
|
||||||
|
<template #label>
|
||||||
|
<div class="cell-item">
|
||||||
|
<el-icon :style="iconStyle">
|
||||||
|
<Clock />
|
||||||
|
</el-icon>
|
||||||
|
结束时间
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
{{ formatDateTime(paper?.paper_end_time) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item>
|
||||||
|
<template #label>
|
||||||
|
<div class="cell-item">
|
||||||
|
<el-icon :style="iconStyle">
|
||||||
|
<Timer />
|
||||||
|
</el-icon>
|
||||||
|
总使用时间
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
{{ formatDuration(paper?.paper_start_time, paper?.paper_end_time) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-main>
|
||||||
|
|
||||||
|
<Footer />
|
||||||
|
</el-container>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
// 导入组件
|
||||||
|
import Header from '@/components/common/Header.vue'
|
||||||
|
import Footer from '@/components/common/Footer.vue'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
import { ref, computed, onMounted } from 'vue'
|
||||||
|
import { ElMessage, ElDescriptions, ElDescriptionsItem, ElIcon, ElResult } from 'element-plus'
|
||||||
|
import { User, Postcard, Ticket, Clock, Timer } from '@element-plus/icons-vue'
|
||||||
|
|
||||||
|
// 导入用户状态管理
|
||||||
|
import { useUserStore } from '@/store/userStore'
|
||||||
|
|
||||||
|
// 获取用户状态管理
|
||||||
|
const userStore = useUserStore()
|
||||||
|
// 响应式引用考生数据
|
||||||
|
const examinee = computed(() => userStore.state.examinee)
|
||||||
|
// 响应式引用试卷数据
|
||||||
|
const paper = computed(() => userStore.state.paper)
|
||||||
|
// 添加判卷状态变量
|
||||||
|
const isChecking = ref(false)
|
||||||
|
|
||||||
|
// 检查是否已登录
|
||||||
|
if (!userStore.state.isLoggedIn) {
|
||||||
|
// 如果未登录,重定向到欢迎页
|
||||||
|
router.push('/')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 响应式数据
|
||||||
|
const router = useRouter()
|
||||||
|
const size = ref('default')
|
||||||
|
|
||||||
|
// 图标样式计算属性
|
||||||
|
const iconStyle = computed(() => {
|
||||||
|
const marginMap = {
|
||||||
|
large: '8px',
|
||||||
|
default: '6px',
|
||||||
|
small: '4px',
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
marginRight: marginMap[size.value] || marginMap.default,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 格式化身份证号(第11-15位替换为*)
|
||||||
|
const formatIdCard = (idCard) => {
|
||||||
|
if (!idCard || idCard.length !== 18) return idCard
|
||||||
|
return idCard.substring(0, 9) + '*****' + idCard.substring(14)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式化日期时间
|
||||||
|
const formatDateTime = (dateTime) => {
|
||||||
|
if (!dateTime) return '未知'
|
||||||
|
const date = new Date(dateTime)
|
||||||
|
return date.toLocaleString()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算考试持续时间
|
||||||
|
const formatDuration = (startTime, endTime) => {
|
||||||
|
if (!startTime || !endTime) return '未知'
|
||||||
|
const start = new Date(startTime)
|
||||||
|
const end = new Date(endTime)
|
||||||
|
const diff = end - start
|
||||||
|
const minutes = Math.floor(diff / (1000 * 60))
|
||||||
|
const hours = Math.floor(minutes / 60)
|
||||||
|
const mins = minutes % 60
|
||||||
|
return `${hours}小时${mins}分钟`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回首页
|
||||||
|
const goHome = () => {
|
||||||
|
router.push({ name: 'examinee-home' })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 组件挂载时
|
||||||
|
onMounted(() => {
|
||||||
|
console.log('EndView初始化')
|
||||||
|
// 检查是否有试卷数据
|
||||||
|
if (!paper.value) {
|
||||||
|
ElMessage.warning('未找到试卷信息')
|
||||||
|
console.log('未找到试卷信息,准备跳转到首页')
|
||||||
|
// 延迟重定向到首页
|
||||||
|
setTimeout(() => {
|
||||||
|
goHome()
|
||||||
|
}, 2000)
|
||||||
|
} else {
|
||||||
|
// 有试卷数据,执行判卷
|
||||||
|
checkAnswers()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 判卷函数
|
||||||
|
const checkAnswers = async () => {
|
||||||
|
try {
|
||||||
|
isChecking.value = true
|
||||||
|
console.log('开始判卷,试卷ID:', paper.value.id)
|
||||||
|
|
||||||
|
// 调用检查答案接口
|
||||||
|
const result = await window.electronAPI.checkPaperAnswers(paper.value.id)
|
||||||
|
console.log('判卷接口返回结果:', result)
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
// 调用 generatePaperPdf 接口并传入 result.data
|
||||||
|
const pdfPath = await window.electronAPI.generatePaperPdf(result.data)
|
||||||
|
console.log('生成的试卷PDF文件路径:', pdfPath)
|
||||||
|
// 更新store中的试卷数据
|
||||||
|
userStore.setPaper(JSON.stringify(result.data))
|
||||||
|
console.log('试卷数据已更新到store,得分:', result.data.paper_score_real)
|
||||||
|
} else {
|
||||||
|
console.error('判卷失败:', result.message)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('判卷过程发生错误:', error)
|
||||||
|
} finally {
|
||||||
|
isChecking.value = false
|
||||||
|
console.log('判卷完成')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* 自定义样式 */
|
||||||
|
.bg-primary {
|
||||||
|
background-color: #1E88E5 !important;
|
||||||
|
/* 蓝色主题,与ExamineeHomeView保持一致 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-primary {
|
||||||
|
color: #1E88E5 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 确保容器占满高度 */
|
||||||
|
.end-container,
|
||||||
|
.el-container {
|
||||||
|
height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 让主内容区自动扩展并居中 */
|
||||||
|
.el-main {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 适配移动设备 */
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.max-w-2xl {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.margin-top {
|
||||||
|
margin-top: 20px !important;
|
||||||
|
}
|
||||||
|
</style>
|
@ -221,8 +221,10 @@ const fetchLastExam = async () => {
|
|||||||
const examData = await window.electronAPI.fetchLastExam()
|
const examData = await window.electronAPI.fetchLastExam()
|
||||||
// console.log('获取考试信息成功:', examData)
|
// console.log('获取考试信息成功:', examData)
|
||||||
lastExam.value = examData
|
lastExam.value = examData
|
||||||
|
// 将考试信息保存到store中
|
||||||
|
userStore.setExam(examData)
|
||||||
console.log('信息:', lastExam.value)
|
console.log('信息:', lastExam.value)
|
||||||
// 解析考试须知数组 - 注意:根据控制台日志,exam_notice已经是数组,不需要再JSON.parse
|
// 解析考试须知数组
|
||||||
if (lastExam.value && lastExam.value.exam_notice) {
|
if (lastExam.value && lastExam.value.exam_notice) {
|
||||||
examNotices.value = lastExam.value.exam_notice
|
examNotices.value = lastExam.value.exam_notice
|
||||||
} else {
|
} else {
|
||||||
@ -298,6 +300,8 @@ const startExam = async () => {
|
|||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
console.log('生成试卷成功:', result)
|
console.log('生成试卷成功:', result)
|
||||||
|
// 保存试卷数据到store
|
||||||
|
userStore.setPaper(result.data)
|
||||||
ElMessageBox.alert(
|
ElMessageBox.alert(
|
||||||
'已完成组卷,点击"进入考试"开始答题',
|
'已完成组卷,点击"进入考试"开始答题',
|
||||||
'组卷完成',
|
'组卷完成',
|
||||||
@ -306,7 +310,11 @@ const startExam = async () => {
|
|||||||
type: 'success'
|
type: 'success'
|
||||||
}
|
}
|
||||||
).then(() => {
|
).then(() => {
|
||||||
router.push('/examinee/examing')
|
// 使用命名路由传递参数
|
||||||
|
router.push({
|
||||||
|
name: 'examinee-examing',
|
||||||
|
params: { paperId: result.data.id }
|
||||||
|
})
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
console.error('生成试卷失败:', result)
|
console.error('生成试卷失败:', result)
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user