exam11/background/service/fileService.js
2025-09-13 07:24:13 +08:00

801 lines
28 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 转换为CommonJS模块语法 - 确保所有导入语句在文件最前面
const path = require('path');
const fs = require('fs');
const PDFDocument = require('pdfkit');
const { app } = require('electron');
// 不依赖__filename和__dirname直接使用app.getAppPath()获取应用路径
// 这是解决webpack打包导致路径问题的关键
function getAppRoot() {
try {
// 在Electron应用中app.getAppPath()会返回应用的实际路径
return path.dirname(app.getAppPath());
} catch (error) {
console.error('获取应用路径失败:', error);
// 备选方案:使用当前工作目录
return process.cwd();
}
}
// 获取字体路径的函数
function getFontPath() {
const appRoot = getAppRoot();
// 定义多个可能的字体目录路径
const possiblePaths = [
// 开发环境下的字体路径
path.join(appRoot, 'src', 'background', 'font'),
// 打包后的字体路径
path.join(appRoot, 'background', 'font'),
// 高版本工程中的字体路径
path.join(appRoot, 'electron', 'font')
];
// 检查哪个路径存在
for (const fontPath of possiblePaths) {
if (fs.existsSync(fontPath)) {
console.log('找到字体目录:', fontPath);
return fontPath;
}
}
// 如果都不存在,返回默认路径
const defaultPath = path.join(appRoot, 'src', 'background', 'font');
console.warn('未找到字体目录,使用默认路径:', defaultPath);
return defaultPath;
}
// 现在使用getFontPath()函数来获取字体路径
const FONT_PATH = getFontPath();
// 统一使用微软雅黑字体
const msyhFontPath = path.join(FONT_PATH, 'msyh.ttf');
// 输出找到的字体路径供调试
console.log('FONT_PATH:', FONT_PATH);
console.log('msyhFontPath:', msyhFontPath, '存在?', fs.existsSync(msyhFontPath));
/**
* 服务层:获取所有考生列表
* @returns {Promise<Array>} 考生列表
*/
async function createFileService() {
try {
/// TODO 测试用
// return await writeFileAsync('C:/Users/chenqiang/Desktop/1.txt', 'hello world');
} catch (error) {
console.error('服务层: 创建文件失败', error);
throw error;
}
}
/**
* 生成PDF文件并保存到合适的目录
* @param {Object} pdfData - PDF数据
* @param {string} fileName - 文件名
* @returns {Promise<string>} 文件保存路径
*/
async function generatePdfService(pdfData, fileName) {
try {
// 获取合适的保存目录
const appDir = path.join(getAppSaveDir(), '..')
const filePath = path.join(appDir, `${fileName || 'document'}.pdf`);
return new Promise((resolve, reject) => {
// 创建PDF文档
const doc = new PDFDocument();
// 加载中文字体的标志
let chineseFontLoaded = false;
let currentFont = null;
// 专门的字体加载函数
function loadMicrosoftYaHeiFont() {
try {
// 只加载微软雅黑字体
if (fs.existsSync(msyhFontPath)) {
doc.registerFont('MicrosoftYaHei', msyhFontPath);
doc.font('MicrosoftYaHei');
currentFont = 'MicrosoftYaHei';
chineseFontLoaded = true;
console.log('成功加载msyh.ttf字体');
return true;
} else {
console.error('微软雅黑字体文件不存在:', msyhFontPath);
return false;
}
} catch (error) {
console.error('加载微软雅黑字体失败:', error);
return false;
}
}
// 尝试加载字体
if (!loadMicrosoftYaHeiFont()) {
console.warn('无法加载微软雅黑字体,将使用系统字体');
// 在macOS上尝试使用系统字体
if (process.platform === 'darwin') {
try {
doc.font('Arial Unicode MS'); // macOS内置支持多语言的字体
currentFont = 'Arial Unicode MS';
chineseFontLoaded = true;
console.log('成功加载系统Arial Unicode MS字体');
} catch (error) {
console.error('加载系统字体失败:', error);
}
}
}
// 保存到文件
const writeStream = fs.createWriteStream(filePath);
doc.pipe(writeStream);
// 设置文档标题
if (pdfData.title) {
// 保存当前字体
const tempFont = currentFont;
try {
// 微软雅黑没有单独的粗体,使用普通字体但加大字号
doc.fontSize(20).text(pdfData.title, { align: 'center' }).moveDown();
} catch (error) {
console.error('设置标题字体失败:', error);
doc.fontSize(20).text(pdfData.title, { align: 'center' }).moveDown();
}
// 恢复字体
if (currentFont) {
doc.font(currentFont);
}
}
// 添加内容
if (pdfData.content) {
pdfData.content.forEach(item => {
if (item.type === 'text') {
doc.fontSize(item.fontSize || 12).text(item.text, item.options || {}).moveDown();
} else if (item.type === 'heading') {
// 保存当前字体
const tempFont = currentFont;
try {
// 微软雅黑没有单独的粗体,使用普通字体但加大字号
doc.fontSize(item.fontSize || 16).text(item.text, item.options || {}).moveDown();
} catch (error) {
console.error('切换到标题字体失败:', error);
doc.fontSize(item.fontSize || 16).text(item.text, item.options || {}).moveDown();
}
// 恢复之前的字体
if (currentFont) {
doc.font(currentFont);
}
} else if (item.type === 'table') {
// 改进表格实现 - 不使用splitTextToFit方法
const { headers, rows } = item;
const cellWidth = 100;
const baseCellHeight = 25; // 增加基础单元格高度,更好地适应中文
const marginLeft = 50;
let currentY = doc.y;
const fontSize = 12;
// 辅助函数:计算文本在指定宽度内的行数
const calculateLines = (text, width) => {
// 估算每行字符数(假设平均字符宽度为字体大小的一半)
const charsPerLine = Math.floor(width / (fontSize / 2));
const lines = [];
let currentText = text;
while (currentText.length > 0) {
// 找到合适的换行位置
let splitIndex = Math.min(currentText.length, charsPerLine);
// 尝试在空格处换行
if (currentText.length > splitIndex && currentText[splitIndex] !== ' ') {
const lastSpace = currentText.lastIndexOf(' ', splitIndex);
if (lastSpace > 0) {
splitIndex = lastSpace;
}
}
lines.push(currentText.substring(0, splitIndex).trim());
currentText = currentText.substring(splitIndex).trim();
}
return lines;
};
// 绘制表头
headers.forEach((header, i) => {
// 计算单元格实际高度(考虑换行)
const lines = calculateLines(header, cellWidth - 10);
const cellHeight = Math.max(baseCellHeight, lines.length * 15);
doc.rect(marginLeft + i * cellWidth, currentY, cellWidth, cellHeight).stroke();
// 微软雅黑没有单独的粗体,使用普通字体
doc.fontSize(fontSize).text(header, marginLeft + i * cellWidth + 5, currentY + (cellHeight - lines.length * 15) / 2, {
width: cellWidth - 10,
height: cellHeight - 10
});
});
// 移动到下一行,考虑最高的表头单元格高度
const headerLines = headers.map(header => calculateLines(header, cellWidth - 10).length);
const maxHeaderLines = Math.max(...headerLines);
currentY += Math.max(baseCellHeight, maxHeaderLines * 15) + 5; // 添加一些间距
// 绘制行
rows.forEach(row => {
// 计算这一行中最高的单元格
const rowLines = row.map(cell => calculateLines(cell.toString(), cellWidth - 10).length);
const maxRowLines = Math.max(...rowLines);
const rowHeight = Math.max(baseCellHeight, maxRowLines * 15);
row.forEach((cell, i) => {
doc.rect(marginLeft + i * cellWidth, currentY, cellWidth, rowHeight).stroke();
const cellLines = calculateLines(cell.toString(), cellWidth - 10);
doc.fontSize(fontSize).text(cell.toString(), marginLeft + i * cellWidth + 5, currentY + (rowHeight - cellLines.length * 15) / 2, {
width: cellWidth - 10,
height: rowHeight - 10
});
});
// 移动到下一行
currentY += rowHeight + 5; // 添加一些间距
});
// 更新文档的当前Y位置
doc.y = currentY;
}
});
}
// 结束文档
doc.end();
// 监听完成事件
writeStream.on('finish', () => {
resolve(filePath);
});
// 监听错误事件
writeStream.on('error', (error) => {
reject(error);
});
});
} catch (error) {
console.error('服务层: 生成PDF失败', error);
throw error;
}
}
/**
* 初始化文件相关IPC服务
* @param {ipcMain} ipcMain - Electron IPC主进程实例
*/
async function initFileIpc(ipcMain) {
// 测试用接口
ipcMain.handle('file-test', async () => {
try {
// 测试用
return '文件服务测试成功';
} catch (error) {
console.error('服务层: 文件测试失败:', error);
return { success: false, message: error.message };
}
});
// 生成PDF文件接口
ipcMain.handle('file-generate-pdf', async (event, pdfData, fileName) => {
try {
const filePath = await generatePdfService(pdfData, fileName);
return { success: true, filePath };
} catch (error) {
console.error('服务层: 生成PDF失败:', error);
return { success: false, message: error.message };
}
});
// 生成试卷PDF文件接口
ipcMain.handle('file-generate-paper-pdf', async (event, jsonString) => {
try {
const filePath = await generatePaperPdf(jsonString);
return { success: true, filePath };
} catch (error) {
console.error('服务层: 生成试卷PDF失败:', error);
return { success: false, message: error.message };
}
});
}
/**
* 获取应用保存目录,适配不同操作系统和环境
* @returns {string} 保存目录路径
*/
function getAppSaveDir() {
// 判断是否为开发环境
const isDev = process.env.NODE_ENV === 'development' || !app.isPackaged;
if (isDev) {
// 开发环境:使用项目根目录
const outputDir = path.join(process.cwd(), 'output');
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
return outputDir;
} else {
// 检测是否为便携模式
const exePath = app.getPath('exe');
const appDir = path.dirname(exePath);
const portableFlagPath = path.join(appDir, 'portable.txt');
const isPortable = fs.existsSync(portableFlagPath);
// 便携模式:使用应用根目录
if (isPortable) {
return appDir;
} else {
// 非便携模式使用应用同级的output目录
const outputDir = path.join(appDir, 'output');
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
return outputDir;
}
}
}
/**
* 获取用户桌面目录路径
* @returns {string} 用户桌面绝对路径
*/
function getDesktopDir() {
try {
// 使用Electron的app.getPath方法获取桌面路径
return app.getPath('desktop');
} catch (error) {
console.error('获取桌面路径失败:', error);
// 发生错误时,返回当前工作目录作为备选
return process.cwd();
}
}
/**
* 将文件复制到用户桌面
* @param {string} filePath - 要复制的文件的绝对路径
* @returns {Promise<void>}
*/
async function copyToDesk(filePath) {
try {
const desktopDir = getDesktopDir();
const fileName = path.basename(filePath);
const destPath = path.join(desktopDir, fileName);
// 使用fs.promises进行文件复制
await fs.promises.copyFile(filePath, destPath);
console.log(`文件已成功复制到桌面: ${destPath}`);
return destPath;
} catch (error) {
console.error('复制文件到桌面失败:', error);
throw error;
}
}
/**
* 生成试卷PDF文件
* @param {string} jsonString - 包含试卷信息的JSON字符串
* @returns {Promise<string>} - 生成的PDF文件绝对路径
*/
async function generatePaperPdf(jsonString) {
try {
// 解析JSON字符串
const paperData = JSON.parse(jsonString);
// 提取考生信息
const { examinee } = paperData;
const examineeName = examinee.examinee_name;
const idCard = examinee.examinee_id_card;
const admissionTicket = examinee.examinee_admission_ticket;
// 提取考试时间信息
const startTime = paperData.paper_start_time;
const endTime = paperData.paper_end_time;
// 截取考试日期 (假设格式为 'YYYY-MM-DD HH:mm:ss')
const examDate = startTime.split(' ')[0];
// 计算用时(分钟)- 直接使用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题的数量之和
let totalQuestions = 0;
paperData.questions.forEach(question => {
if (question.choices && question.choices.length > 0) {
totalQuestions += question.choices.length;
}
if (question.blanks && question.blanks.length > 0) {
totalQuestions += question.blanks.length;
}
});
const totalScore = paperData.paper_score;
const realScore = paperData.paper_score_real;
// 对questions列表按照question_type和id进行排序
paperData.questions.sort((a, b) => {
// 先按题型排序
if (a.question_type !== b.question_type) {
return a.question_type.localeCompare(b.question_type);
}
// 再按id排序
return a.id - b.id;
});
// 生成文件名
// 格式化paper_end_time移除特殊字符
const endDate = new Date(endTime.replace(/-/g, '/'));
const formattedEndTime = [
endDate.getFullYear(),
String(endDate.getMonth() + 1).padStart(2, '0'),
String(endDate.getDate()).padStart(2, '0'),
String(endDate.getHours()).padStart(2, '0'),
String(endDate.getMinutes()).padStart(2, '0'),
String(endDate.getSeconds()).padStart(2, '0')
].join('');
const fileName = `${examineeName}_${idCard}_${formattedEndTime}.pdf`;
// 获取保存路径
const appDir = getAppSaveDir();
// 确保目录存在
if (!fs.existsSync(appDir)) {
fs.mkdirSync(appDir, { recursive: true });
}
const filePath = path.join(appDir, fileName);
// 创建PDF文档保留默认边距
const doc = new PDFDocument({
size: 'A4',
margin: 50
});
// 加载中文字体 - 直接使用文件开头定义的全局变量
let chineseFontLoaded = false;
let currentFont = null;
// 专门的字体加载函数
function loadMicrosoftYaHeiFont() {
try {
// 只加载微软雅黑字体
if (fs.existsSync(msyhFontPath)) {
doc.registerFont('MicrosoftYaHei', msyhFontPath);
doc.font('MicrosoftYaHei');
currentFont = 'MicrosoftYaHei';
chineseFontLoaded = true;
console.log('成功加载msyh.ttf字体');
return true;
} else {
console.error('微软雅黑字体文件不存在:', msyhFontPath);
return false;
}
} catch (error) {
console.error('加载微软雅黑字体失败:', error);
return false;
}
}
// 添加字体加载状态日志
console.log('尝试加载字体:');
console.log('- 微软雅黑字体:', msyhFontPath, '存在?', fs.existsSync(msyhFontPath));
// 尝试加载字体
if (!loadMicrosoftYaHeiFont()) {
console.warn('无法加载微软雅黑字体,可能导致中文显示异常');
// 在macOS上尝试使用系统字体
if (process.platform === 'darwin') {
try {
doc.font('Arial Unicode MS');
currentFont = 'Arial Unicode MS';
chineseFontLoaded = true;
console.log('使用系统默认字体 Arial Unicode MS');
} catch (error) {
console.error('加载系统字体失败:', error);
}
}
}
// 保存到文件
const writeStream = fs.createWriteStream(filePath);
doc.pipe(writeStream);
// 添加标题
// 微软雅黑没有单独的粗体,使用普通字体但加大字号
doc.fontSize(24).text('机考试卷', { align: 'center' }).moveDown(2);
// 恢复常规字体
if (currentFont) {
doc.font(currentFont);
}
// 辅助函数:绘制表格
function drawTable(headers, rows, cellWidth = 120, baseCellHeight = 25) {
// 从左边距开始
const marginLeft = doc.page.margins.left;
let currentY = doc.y;
const fontSize = 12;
const pageWidth = doc.page.width - doc.page.margins.left - doc.page.margins.right;
// 计算文本在指定宽度内的行数
const calculateLines = (text, width) => {
const charsPerLine = Math.floor(width / (fontSize / 2));
const lines = [];
let currentText = text;
while (currentText.length > 0) {
let splitIndex = Math.min(currentText.length, charsPerLine);
if (currentText.length > splitIndex && currentText[splitIndex] !== ' ') {
const lastSpace = currentText.lastIndexOf(' ', splitIndex);
if (lastSpace > 0) {
splitIndex = lastSpace;
}
}
lines.push(currentText.substring(0, splitIndex).trim());
currentText = currentText.substring(splitIndex).trim();
}
return lines;
};
// 确保表格不会超出页面宽度
const totalTableWidth = headers.length * cellWidth;
const adjustedCellWidth = totalTableWidth > pageWidth ? pageWidth / headers.length : cellWidth;
// 绘制表头
headers.forEach((header, i) => {
const lines = calculateLines(header, adjustedCellWidth - 10);
const cellHeight = Math.max(baseCellHeight, lines.length * 15);
doc.rect(marginLeft + i * adjustedCellWidth, currentY, adjustedCellWidth, cellHeight).stroke();
// 微软雅黑没有单独的粗体,使用普通字体
doc.fontSize(fontSize).text(header, marginLeft + i * adjustedCellWidth + 5, currentY + (cellHeight - lines.length * 15) / 2, {
width: adjustedCellWidth - 10,
height: cellHeight - 10
});
});
// 移动到下一行(无间隙)
const headerLines = headers.map(header => calculateLines(header, adjustedCellWidth - 10).length);
const maxHeaderLines = Math.max(...headerLines);
currentY += Math.max(baseCellHeight, maxHeaderLines * 15);
// 绘制行
rows.forEach(row => {
const rowLines = row.map(cell => calculateLines(cell.toString(), adjustedCellWidth - 10).length);
const maxRowLines = Math.max(...rowLines);
const rowHeight = Math.max(baseCellHeight, maxRowLines * 15);
row.forEach((cell, i) => {
doc.rect(marginLeft + i * adjustedCellWidth, currentY, adjustedCellWidth, rowHeight).stroke();
const cellLines = calculateLines(cell.toString(), adjustedCellWidth - 10);
doc.fontSize(fontSize).text(cell.toString(), marginLeft + i * adjustedCellWidth + 5, currentY + (rowHeight - cellLines.length * 15) / 2, {
width: adjustedCellWidth - 10,
height: rowHeight - 10
});
});
currentY += rowHeight; // 无间隙
});
doc.y = currentY;
}
// 辅助函数添加base64图片
function addBase64Image(base64String, maxWidth = 400, maxHeight = 300) {
try {
// 移除base64前缀
const base64Data = base64String.replace(/^data:image\/\w+;base64,/, '');
// 将base64转换为缓冲区
const imageBuffer = Buffer.from(base64Data, 'base64');
// 获取图片尺寸
const image = doc.openImage(imageBuffer);
// 计算缩放比例
const scale = Math.min(maxWidth / image.width, maxHeight / image.height, 1);
// 添加图片,从左边距开始
doc.image(image, doc.page.margins.left, doc.y, {
width: image.width * scale,
height: image.height * scale
});
// 移动文档指针
doc.y += image.height * scale + 10;
} catch (error) {
console.error('添加图片失败:', error);
doc.fontSize(10).text('图片加载失败', doc.page.margins.left, doc.y).moveDown();
}
}
// 绘制考生信息表格
drawTable(
['姓名', '身份证号', '准考证号'],
[[examineeName, idCard, admissionTicket]]
);
doc.moveDown();
// 绘制考试信息表格
drawTable(
['考试日期', '开始时间', '结束时间', '用时(分钟)'],
[[examDate, startTime.split(' ')[1], endTime.split(' ')[1], durationMinutes.toString()]]
);
doc.moveDown();
// 绘制试卷信息表格
drawTable(
['总题量', '试卷总分', '考试得分'],
[[totalQuestions.toString(), totalScore.toString(), realScore.toString()]]
);
// 留2行间距
doc.moveDown(2);
// 添加题目信息
paperData.questions.forEach((question, index) => {
// 题目类型和描述
// 上面留3行间距
doc.moveDown(3);
// 微软雅黑没有单独的粗体,使用普通字体但加大字号
doc.fontSize(14).text(`${index + 1} 题 (${question.question_type_name})`, doc.page.margins.left).moveDown(1); // 增加题干编号与上方内容的间距
if (currentFont) {
doc.font(currentFont);
}
// 确保题干描述从左边距开始
doc.fontSize(12).text(question.question_description, {
x: doc.page.margins.left,
width: doc.page.width - doc.page.margins.left - doc.page.margins.right
});
// 留1行间距
doc.moveDown(1);
// 添加图片
if (question.images && question.images.length > 0) {
// doc.fontSize(12).text('图片:', doc.page.margins.left).moveDown();
question.images.forEach((image) => {
if (image.image_base64) {
addBase64Image(image.image_base64);
} else {
doc.fontSize(10).text(`图片: ${image.image_name || '无名称'}`, doc.page.margins.left).moveDown();
}
});
}
// 添加数据集
if (question.datasets && question.datasets.length > 0) {
doc.fontSize(12).text('数据集:', doc.page.margins.left).moveDown();
question.datasets.forEach((dataset, dataIndex) => {
doc.fontSize(10).text(`数据集 ${dataIndex + 1} (${dataset.dataset_name || '无名称'})`, doc.page.margins.left).moveDown();
// 尝试解析数据集数据为表格
try {
const datasetData = JSON.parse(dataset.dataset_data);
if (Array.isArray(datasetData) && datasetData.length > 0) {
// 假设第一行是表头
const headers = Object.keys(datasetData[0]);
const rows = datasetData.map(item => headers.map(header => item[header]));
// 缩小单元格宽度以适应页面
drawTable(headers, rows, 80, 20);
}
} catch (error) {
console.error('解析数据集失败:', error);
doc.fontSize(10).text(`数据集内容: ${dataset.dataset_data.substring(0, 100)}...`, doc.page.margins.left).moveDown();
}
});
}
// 留1行间距
// doc.moveDown(1);
// 添加填空题
if (question.blanks && question.blanks.length > 0) {
question.blanks.forEach((blank, blankIndex) => {
// 微软雅黑没有单独的粗体,使用普通字体但加大字号
// 对于第一个问题,减少与题干的间距
// if (blankIndex === 0) {
// doc.moveUp(5); // 向上调整一点,减少与题干的间距
// }
doc.fontSize(12).text(`填空 ${blankIndex + 1}`, doc.page.margins.left).moveDown();
if (currentFont) {
doc.font(currentFont);
}
// 确保问题描述从左边距开始
doc.fontSize(12).text(blank.blank_description, {
x: doc.page.margins.left,
width: doc.page.width - doc.page.margins.left - doc.page.margins.right
}).moveDown();
drawTable(
['正确答案', '考生答案', '分值', '得分'],
[
[
Array.isArray(blank.correct_answers) ? blank.correct_answers.join(', ') : blank.correct_answers,
Array.isArray(blank.examinee_answers) ? blank.examinee_answers.join(', ') : blank.examinee_answers,
blank.score.toString(),
blank.score_real.toString()
]
],
100
);
doc.moveDown(1);
});
}
// 添加选择题
if (question.choices && question.choices.length > 0) {
question.choices.forEach((choice, choiceIndex) => {
// 微软雅黑没有单独的粗体,使用普通字体但加大字号
// 对于第一个问题,减少与题干的间距
// if (choiceIndex === 0) {
// doc.moveUp(5); // 向上调整一点,减少与题干的间距
// }
doc.fontSize(12).text(`选择题 ${choiceIndex + 1}`, doc.page.margins.left).moveDown();
if (currentFont) {
doc.font(currentFont);
}
// 确保问题描述从左边距开始
doc.fontSize(12).text(choice.choice_description, {
x: doc.page.margins.left,
width: doc.page.width - doc.page.margins.left - doc.page.margins.right
}).moveDown();
// 显示选项
if (choice.choice_options && choice.choice_options.length > 0) {
const optionsText = choice.choice_options.map((option, i) => {
const optionLabel = String.fromCharCode(65 + i); // A, B, C, D...
return `${optionLabel}. ${option}`;
}).join(' '); // 使用多个空格作为选项间的间距
doc.fontSize(10).text(`选项: ${optionsText}`, doc.page.margins.left);
}
// 留一行间距
doc.moveDown(1);
drawTable(
['正确答案', '考生答案', '分值', '得分'],
[
[
Array.isArray(choice.correct_answers) ? choice.correct_answers.join(', ') : choice.correct_answers,
Array.isArray(choice.examinee_answers) ? choice.examinee_answers.join(', ') : choice.examinee_answers,
choice.score.toString(),
choice.score_real.toString()
]
],
100
);
doc.moveDown(1);
});
}
// 分页处理
if (doc.y > 700) {
doc.addPage();
}
});
// 结束文档
doc.end();
return new Promise((resolve, reject) => {
writeStream.on('finish', async () => {
try {
// 调用copyToDesk方法将文件复制到桌面获取新的路径
const newFilePath = await copyToDesk(filePath);
resolve(newFilePath);
} catch (error) {
reject(error);
}
});
writeStream.on('error', reject);
});
} catch (error) {
console.error('生成PDF失败:', error);
throw error;
}
}
// 导出使用CommonJS格式
module.exports = {
initFileIpc,
createFileService,
generatePaperPdf,
generatePdfService
};