food
This commit is contained in:
+527
-92
@@ -7,6 +7,7 @@ const axios = require('axios');
|
||||
const { logger } = require('../../backend/src/config/logger');
|
||||
const config = require('../src/config.json')
|
||||
const { print } = require('pdf-to-printer');
|
||||
const { generatePayslipPDF } = require('./pdfGenerator');
|
||||
|
||||
protocol.registerSchemesAsPrivileged([
|
||||
{
|
||||
@@ -51,6 +52,532 @@ try {
|
||||
|
||||
global.allowClose = false;
|
||||
|
||||
// ============================================
|
||||
// 📄 IPC ОБРАБОТЧИКИ
|
||||
// ============================================
|
||||
|
||||
// ✅ 1. Печать PDF (принимает URL или base64) - СКРЫТАЯ ПЕЧАТЬ
|
||||
ipcMain.handle('print-pdf', async (event, data = {}) => {
|
||||
let tempFile = null;
|
||||
|
||||
try {
|
||||
if (!data.pdfUrl && !data.pdfBase64) {
|
||||
throw new Error('URL или base64 PDF не передан');
|
||||
}
|
||||
|
||||
let pdfBuffer;
|
||||
|
||||
// ✅ Если передан base64 — используем его
|
||||
if (data.pdfBase64) {
|
||||
logger.info('[PRINT] Печать PDF из base64');
|
||||
pdfBuffer = Buffer.from(data.pdfBase64, 'base64');
|
||||
logger.info('[PRINT] Размер PDF из base64:', pdfBuffer.length, 'байт');
|
||||
}
|
||||
// ✅ Если передан URL — скачиваем
|
||||
else if (data.pdfUrl) {
|
||||
const cleanUrl = data.pdfUrl.split('#')[0];
|
||||
logger.info('[PRINT] Загрузка PDF из URL:', cleanUrl);
|
||||
|
||||
// Если это blob URL — получаем через fetch
|
||||
if (cleanUrl.startsWith('blob:')) {
|
||||
const response = await fetch(cleanUrl);
|
||||
const blob = await response.blob();
|
||||
pdfBuffer = Buffer.from(await blob.arrayBuffer());
|
||||
} else {
|
||||
const response = await axios.get(cleanUrl, {
|
||||
responseType: 'arraybuffer',
|
||||
timeout: 30000
|
||||
});
|
||||
pdfBuffer = Buffer.from(response.data);
|
||||
}
|
||||
logger.info('[PRINT] Размер PDF из URL:', pdfBuffer.length, 'байт');
|
||||
}
|
||||
|
||||
if (!pdfBuffer || pdfBuffer.length < 100) {
|
||||
throw new Error('PDF слишком маленький или пустой');
|
||||
}
|
||||
|
||||
// ✅ ВРЕМЕННО СОХРАНЯЕМ ДЛЯ ПЕЧАТИ (pdf-to-printer требует файл)
|
||||
const tempDir = app.getPath('temp');
|
||||
tempFile = path.join(tempDir, `print_${Date.now()}.pdf`);
|
||||
fs.writeFileSync(tempFile, pdfBuffer);
|
||||
logger.info('[PRINT] Временный файл создан:', tempFile);
|
||||
|
||||
const printerName = data.deviceName || 'GP-80250 Series';
|
||||
logger.info('[PRINT] СКРЫТАЯ печать через pdf-to-printer:', printerName);
|
||||
|
||||
// ✅ КЛЮЧЕВОЕ: silent: true — печать без диалога
|
||||
await print(tempFile, {
|
||||
printer: printerName,
|
||||
silent: true,
|
||||
printDialog: false
|
||||
});
|
||||
|
||||
logger.info('[PRINT] ✅ Печать успешна');
|
||||
|
||||
// ✅ УДАЛЯЕМ ВРЕМЕННЫЙ ФАЙЛ
|
||||
// try {
|
||||
// if (tempFile && fs.existsSync(tempFile)) {
|
||||
// fs.unlinkSync(tempFile);
|
||||
// logger.info('[PRINT] Временный файл удален');
|
||||
// }
|
||||
// } catch (e) {
|
||||
// logger.warn('[PRINT] Не удалось удалить временный файл:', e.message);
|
||||
// }
|
||||
|
||||
return { success: true };
|
||||
|
||||
} catch (error) {
|
||||
logger.error('[PRINT] ❌ Ошибка:', error.message);
|
||||
try {
|
||||
if (tempFile && fs.existsSync(tempFile)) {
|
||||
fs.unlinkSync(tempFile);
|
||||
}
|
||||
} catch (e) {}
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
});
|
||||
|
||||
// ✅ 2. ОСНОВНАЯ ЛОГИКА ПЕЧАТИ HTML (оставлена для совместимости)
|
||||
async function printHtmlDirectLogic(data) {
|
||||
let tempHtml = null;
|
||||
let tempPdf = null;
|
||||
let printWindow = null;
|
||||
let savedPdfPath = null;
|
||||
|
||||
try {
|
||||
if (!data.html) {
|
||||
throw new Error('HTML-код не передан');
|
||||
}
|
||||
|
||||
logger.info('[PRINT-HTML] ========== СОХРАНЕНИЕ PDF ==========');
|
||||
logger.info('[PRINT-HTML] Длина HTML:', data.html.length);
|
||||
|
||||
const desktopPath = app.getPath('desktop');
|
||||
const documentsPath = app.getPath('documents');
|
||||
|
||||
let saveDir = documentsPath;
|
||||
const possiblePaths = [
|
||||
path.join(documentsPath, 'payslips'),
|
||||
path.join(documentsPath, 'Расчетные листки'),
|
||||
path.join(desktopPath, 'payslips'),
|
||||
path.join(desktopPath, 'Расчетные листки'),
|
||||
documentsPath
|
||||
];
|
||||
|
||||
for (const dir of possiblePaths) {
|
||||
if (fs.existsSync(dir)) {
|
||||
saveDir = dir;
|
||||
logger.info('[PRINT-HTML] Найдена папка для сохранения:', saveDir);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const now = new Date();
|
||||
const dateStr = now.getFullYear() +
|
||||
String(now.getMonth() + 1).padStart(2, '0') +
|
||||
String(now.getDate()).padStart(2, '0') + '_' +
|
||||
String(now.getHours()).padStart(2, '0') +
|
||||
String(now.getMinutes()).padStart(2, '0') +
|
||||
String(now.getSeconds()).padStart(2, '0');
|
||||
|
||||
const fileName = `test_${dateStr}.pdf`;
|
||||
savedPdfPath = path.join(saveDir, fileName);
|
||||
|
||||
logger.info('[PRINT-HTML] PDF будет сохранен в:', savedPdfPath);
|
||||
|
||||
const tempDir = app.getPath('temp');
|
||||
tempHtml = path.join(tempDir, `print_${Date.now()}.html`);
|
||||
tempPdf = path.join(tempDir, `print_${Date.now()}.pdf`);
|
||||
|
||||
let htmlContent = data.html;
|
||||
if (!htmlContent || htmlContent.trim().length < 10) {
|
||||
logger.warn('[PRINT-HTML] HTML пустой, используем тестовый');
|
||||
htmlContent = `
|
||||
<div style="text-align:center;padding:20px;font-size:16pt;border:2px solid #000;">
|
||||
<b>ТЕСТ ПЕЧАТИ</b>
|
||||
<hr>
|
||||
<div>Дата: ${new Date().toLocaleString()}</div>
|
||||
<div>Табельный номер: TEST</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
const fullHtml = `<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Расчетный листок</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
font-family: 'Courier New', monospace;
|
||||
padding: 20px;
|
||||
background: white;
|
||||
color: black;
|
||||
font-size: 12pt;
|
||||
}
|
||||
.header {
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
font-size: 16pt;
|
||||
border-bottom: 2px solid #000;
|
||||
padding-bottom: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 4px 0;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
.total {
|
||||
font-weight: bold;
|
||||
border-top: 2px solid #000;
|
||||
padding-top: 10px;
|
||||
margin-top: 10px;
|
||||
font-size: 14pt;
|
||||
}
|
||||
.section-title {
|
||||
font-weight: bold;
|
||||
border-bottom: 1px dotted #000;
|
||||
margin: 10px 0;
|
||||
padding: 5px 0;
|
||||
font-size: 14pt;
|
||||
}
|
||||
.item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 3px 0 3px 15px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
.footer {
|
||||
text-align: center;
|
||||
font-size: 10pt;
|
||||
color: #666;
|
||||
border-top: 1px dashed #000;
|
||||
margin-top: 20px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
.debug-info {
|
||||
color: #999;
|
||||
font-size: 8pt;
|
||||
margin-top: 10px;
|
||||
padding: 5px;
|
||||
border: 1px dashed #ccc;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
${htmlContent}
|
||||
<div class="debug-info">
|
||||
Сгенерировано: ${new Date().toLocaleString()}
|
||||
</div>
|
||||
</body>
|
||||
</html>`;
|
||||
|
||||
fs.writeFileSync(tempHtml, fullHtml, 'utf8');
|
||||
logger.info('[PRINT-HTML] HTML файл создан:', tempHtml);
|
||||
logger.info('[PRINT-HTML] Размер HTML:', fs.statSync(tempHtml).size, 'байт');
|
||||
|
||||
printWindow = new BrowserWindow({
|
||||
width: 600,
|
||||
height: 800,
|
||||
show: true,
|
||||
webPreferences: {
|
||||
nodeIntegration: false,
|
||||
contextIsolation: true,
|
||||
webSecurity: false,
|
||||
allowRunningInsecureContent: true,
|
||||
}
|
||||
});
|
||||
|
||||
printWindow.webContents.openDevTools();
|
||||
|
||||
logger.info('[PRINT-HTML] Загрузка HTML в окно...');
|
||||
await printWindow.loadFile(tempHtml);
|
||||
|
||||
await new Promise((resolve) => {
|
||||
printWindow.webContents.once('did-finish-load', () => {
|
||||
logger.info('[PRINT-HTML] Событие did-finish-load');
|
||||
resolve();
|
||||
});
|
||||
setTimeout(() => {
|
||||
logger.info('[PRINT-HTML] Таймаут загрузки');
|
||||
resolve();
|
||||
}, 10000);
|
||||
});
|
||||
|
||||
const contentCheck = await printWindow.webContents.executeJavaScript(`
|
||||
(function() {
|
||||
const body = document.body;
|
||||
if (!body) return { hasBody: false, contentLength: 0 };
|
||||
const text = body.textContent || '';
|
||||
const html = body.innerHTML || '';
|
||||
return {
|
||||
hasBody: true,
|
||||
contentLength: text.length,
|
||||
htmlLength: html.length,
|
||||
preview: text.substring(0, 200)
|
||||
};
|
||||
})()
|
||||
`);
|
||||
|
||||
logger.info('[PRINT-HTML] Проверка контента:', contentCheck);
|
||||
|
||||
if (contentCheck.contentLength < 10) {
|
||||
logger.warn('[PRINT-HTML] ⚠️ В окне нет контента!');
|
||||
await printWindow.webContents.executeJavaScript(`
|
||||
if (!document.body || document.body.textContent.length < 10) {
|
||||
document.body.innerHTML = '<div style="padding:20px;font-size:20pt;color:red;">ОШИБКА: Контент не загрузился!</div>';
|
||||
}
|
||||
`);
|
||||
}
|
||||
|
||||
await printWindow.webContents.executeJavaScript(`
|
||||
document.body.style.display = 'block';
|
||||
document.body.style.visibility = 'visible';
|
||||
document.body.style.opacity = '1';
|
||||
window.scrollTo(0, 0);
|
||||
document.querySelectorAll('*').forEach(el => {
|
||||
el.style.display = 'block';
|
||||
el.style.visibility = 'visible';
|
||||
el.style.opacity = '1';
|
||||
});
|
||||
console.log('Контент в окне:', document.body.textContent.length, 'символов');
|
||||
console.log('HTML:', document.body.innerHTML.substring(0, 500));
|
||||
`);
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
|
||||
const finalCheck = await printWindow.webContents.executeJavaScript(`
|
||||
document.body ? document.body.textContent.length : 0
|
||||
`);
|
||||
logger.info('[PRINT-HTML] Финальная проверка контента:', finalCheck, 'символов');
|
||||
|
||||
logger.info('[PRINT-HTML] Конвертация в PDF...');
|
||||
const pdfData = await printWindow.webContents.printToPDF({
|
||||
pageSize: {
|
||||
width: 226800,
|
||||
height: 500000
|
||||
},
|
||||
margins: {
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0
|
||||
},
|
||||
printBackground: true,
|
||||
landscape: false
|
||||
});
|
||||
|
||||
fs.writeFileSync(tempPdf, pdfData);
|
||||
logger.info('[PRINT-HTML] PDF создан, размер:', pdfData.length, 'байт');
|
||||
|
||||
const pdfBuffer = fs.readFileSync(tempPdf);
|
||||
const isPdf = pdfBuffer.slice(0, 4).toString() === '%PDF';
|
||||
logger.info('[PRINT-HTML] Это PDF?', isPdf);
|
||||
|
||||
if (!isPdf) {
|
||||
logger.error('[PRINT-HTML] ⚠️ Файл НЕ является PDF!');
|
||||
throw new Error('Созданный файл не является PDF');
|
||||
}
|
||||
|
||||
if (printWindow) {
|
||||
printWindow.close();
|
||||
printWindow = null;
|
||||
}
|
||||
|
||||
if (pdfData.length < 500) {
|
||||
logger.warn('[PRINT-HTML] ⚠️ PDF слишком маленький:', pdfData.length, 'байт');
|
||||
logger.info('[PRINT-HTML] Пробуем еще раз с большим ожиданием...');
|
||||
|
||||
const retryWindow = new BrowserWindow({
|
||||
width: 600,
|
||||
height: 800,
|
||||
show: false,
|
||||
webPreferences: {
|
||||
nodeIntegration: false,
|
||||
contextIsolation: true,
|
||||
webSecurity: false,
|
||||
allowRunningInsecureContent: true,
|
||||
}
|
||||
});
|
||||
|
||||
await retryWindow.loadFile(tempHtml);
|
||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||
|
||||
const retryPdf = await retryWindow.webContents.printToPDF({
|
||||
pageSize: { width: 226800, height: 500000 },
|
||||
margins: { top: 0, bottom: 0, left: 0, right: 0 },
|
||||
printBackground: true,
|
||||
landscape: false
|
||||
});
|
||||
|
||||
fs.writeFileSync(tempPdf, retryPdf);
|
||||
logger.info('[PRINT-HTML] Повторный PDF создан, размер:', retryPdf.length, 'байт');
|
||||
retryWindow.close();
|
||||
}
|
||||
|
||||
fs.copyFileSync(tempPdf, savedPdfPath);
|
||||
logger.info('[PRINT-HTML] ✅ PDF сохранен в:', savedPdfPath);
|
||||
logger.info('[PRINT-HTML] Размер сохраненного PDF:', fs.statSync(savedPdfPath).size, 'байт');
|
||||
|
||||
const printerName = data.printerName || 'GP-80250 Series';
|
||||
logger.info('[PRINT-HTML] Отправка PDF на печать, принтер:', printerName);
|
||||
|
||||
let printResult = null;
|
||||
let printSuccess = false;
|
||||
|
||||
try {
|
||||
logger.info('[PRINT-HTML] Попытка печати...');
|
||||
printResult = await print(tempPdf, {
|
||||
printer: printerName,
|
||||
scale: 'fit',
|
||||
paperSize: 'A4'
|
||||
});
|
||||
printSuccess = true;
|
||||
logger.info('[PRINT-HTML] Печать успешна:', printResult);
|
||||
} catch (err) {
|
||||
logger.warn('[PRINT-HTML] Печать не удалась:', err.message);
|
||||
try {
|
||||
printResult = await print(tempPdf);
|
||||
printSuccess = true;
|
||||
logger.info('[PRINT-HTML] Печать успешна (без параметров)');
|
||||
} catch (err2) {
|
||||
logger.error('[PRINT-HTML] Все попытки печати не удались:', err2.message);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (tempHtml && fs.existsSync(tempHtml)) fs.unlinkSync(tempHtml);
|
||||
if (tempPdf && fs.existsSync(tempPdf)) fs.unlinkSync(tempPdf);
|
||||
} catch (e) {}
|
||||
|
||||
logger.info('[PRINT-HTML] ========== ЗАВЕРШЕНО ==========');
|
||||
logger.info('[PRINT-HTML] ✅ PDF сохранен в:', savedPdfPath);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
pdfPath: savedPdfPath,
|
||||
printSuccess: printSuccess,
|
||||
message: `PDF сохранен в: ${savedPdfPath}`
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
logger.error('[PRINT-HTML] ❌ Ошибка:', error.message);
|
||||
logger.error('[PRINT-HTML] Стек:', error.stack);
|
||||
|
||||
try {
|
||||
if (printWindow) printWindow.close();
|
||||
if (tempHtml && fs.existsSync(tempHtml)) fs.unlinkSync(tempHtml);
|
||||
if (tempPdf && fs.existsSync(tempPdf)) fs.unlinkSync(tempPdf);
|
||||
} catch (e) {}
|
||||
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ 3. Обработчик print-html-direct (для совместимости)
|
||||
ipcMain.handle('print-html-direct', async (event, data) => {
|
||||
return await printHtmlDirectLogic(data);
|
||||
});
|
||||
|
||||
// ✅ 4. ОСНОВНОЙ ОБРАБОТЧИК - ГЕНЕРАЦИЯ PDF В ПАМЯТИ (БЕЗ СОХРАНЕНИЯ НА ДИСК)
|
||||
ipcMain.handle('print-payslip-form', async (event, data = {}) => {
|
||||
try {
|
||||
logger.info('[PRINT-PAYSLIP] ========== ГЕНЕРАЦИЯ PDF В ПАМЯТИ ==========');
|
||||
logger.info('[PRINT-PAYSLIP] tnom:', data.tnom);
|
||||
logger.info('[PRINT-PAYSLIP] period:', data.period);
|
||||
|
||||
// --- 1. Получаем данные из бэкенда ---
|
||||
let payslipData = null;
|
||||
|
||||
try {
|
||||
const backendUrl = appConfig.backendUrl || 'http://localhost:3001';
|
||||
const url = `${backendUrl}/api/payslip/data/${data.tnom}?period=${data.period}`;
|
||||
logger.info('[PRINT-PAYSLIP] Запрос к бэкенду:', url);
|
||||
|
||||
const response = await axios.get(url, { timeout: 10000 });
|
||||
payslipData = response.data;
|
||||
logger.info('[PRINT-PAYSLIP] Данные получены');
|
||||
} catch (error) {
|
||||
logger.warn('[PRINT-PAYSLIP] Не удалось получить данные из бэкенда:', error.message);
|
||||
// Заглушка на случай, если бэкенд не отвечает
|
||||
payslipData = {
|
||||
fullName: 'Иванов Иван Иванович',
|
||||
tabNumber: data.tnom || '00001',
|
||||
department: 'Отдел разработки',
|
||||
position: 'Инженер-программист',
|
||||
daysWorked: '22',
|
||||
month: 'Ноябрь',
|
||||
year: '2025',
|
||||
period: data.period || '112025',
|
||||
accrued: [
|
||||
{ code: '01', description: 'Оклад', amount: 2891.87 },
|
||||
{ code: '02', description: 'Премия', amount: 0 }
|
||||
],
|
||||
totalAccrued: 2891.87,
|
||||
deductions: [
|
||||
{ code: '101', description: 'НДФЛ', amount: 376.36 },
|
||||
{ code: '102', description: 'Профсоюз', amount: 85.50 }
|
||||
],
|
||||
totalDeductions: 461.86,
|
||||
totalPaid: 2430.01,
|
||||
bankTransfers: [
|
||||
{ bank: 'Сбербанк', amount: 2430.01 }
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
// --- 2. Генерируем PDF в памяти (Buffer) ---
|
||||
const pdfBuffer = await generatePayslipPDF(payslipData);
|
||||
logger.info('[PRINT-PAYSLIP] PDF сгенерирован, размер:', pdfBuffer.length, 'байт');
|
||||
|
||||
if (pdfBuffer.length < 100) {
|
||||
throw new Error('Сгенерированный PDF слишком маленький');
|
||||
}
|
||||
|
||||
// --- 3. Преобразуем Buffer в base64 для передачи через IPC ---
|
||||
const pdfBase64 = pdfBuffer.toString('base64');
|
||||
|
||||
logger.info('[PRINT-PAYSLIP] PDF сконвертирован в base64, длина:', pdfBase64.length);
|
||||
|
||||
// --- 4. Возвращаем base64 PDF ---
|
||||
return {
|
||||
success: true,
|
||||
pdfBase64: pdfBase64,
|
||||
fileName: `${data.tnom || 'payslip'}.pdf`
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
logger.error('[PRINT-PAYSLIP] ❌ Ошибка:', error.message);
|
||||
logger.error('[PRINT-PAYSLIP] Стек:', error.stack);
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
});
|
||||
|
||||
// ✅ 5. Обработчик получения списка принтеров
|
||||
ipcMain.handle('get-printers', async () => {
|
||||
return [
|
||||
{
|
||||
name: 'GP-80250 Series',
|
||||
displayName: 'GP-80250 Series',
|
||||
isDefault: true,
|
||||
status: 'ready'
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
// ✅ 6. Обработчик получения конфига
|
||||
ipcMain.handle('get-app-config', async (event) => {
|
||||
return appConfig;
|
||||
});
|
||||
|
||||
// ============================================
|
||||
// 🚀 СОЗДАНИЕ ГЛАВНОГО ОКНА
|
||||
// ============================================
|
||||
|
||||
function createWindow() {
|
||||
const preloadPath = path.join(__dirname, 'preload.js');
|
||||
try {
|
||||
@@ -90,94 +617,6 @@ function createWindow() {
|
||||
});
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 📄 ПЕЧАТЬ PDF ЧЕРЕЗ pdf-to-printer
|
||||
// ============================================
|
||||
|
||||
ipcMain.handle('print-pdf', async (event, data = {}) => {
|
||||
let tempFile = null;
|
||||
|
||||
try {
|
||||
if (!data.pdfUrl) {
|
||||
throw new Error('URL файла PDF не передан');
|
||||
}
|
||||
|
||||
const cleanUrl = data.pdfUrl.split('#')[0];
|
||||
logger.info('[PRINT] Загрузка PDF:', cleanUrl);
|
||||
|
||||
// ✅ Скачиваем PDF
|
||||
const response = await axios.get(cleanUrl, {
|
||||
responseType: 'arraybuffer',
|
||||
timeout: 30000
|
||||
});
|
||||
|
||||
const pdfBuffer = Buffer.from(response.data);
|
||||
logger.info('[PRINT] Размер PDF:', pdfBuffer.length, 'байт');
|
||||
|
||||
if (pdfBuffer.length < 100) {
|
||||
throw new Error('PDF слишком маленький или пустой');
|
||||
}
|
||||
|
||||
// ✅ Сохраняем во временный файл
|
||||
const tempDir = app.getPath('temp');
|
||||
tempFile = path.join(tempDir, `print_${Date.now()}.pdf`);
|
||||
fs.writeFileSync(tempFile, pdfBuffer);
|
||||
logger.info('[PRINT] Временный файл создан:', tempFile);
|
||||
|
||||
// ✅ ПЕЧАТАЕМ ЧЕРЕЗ pdf-to-printer
|
||||
const printerName = 'GP-80250 Series';
|
||||
logger.info('[PRINT] Отправляем на печать через pdf-to-printer:', printerName);
|
||||
|
||||
// ✅ Опции печати
|
||||
const options = {
|
||||
printer: printerName,
|
||||
// scale: 'fit', // Масштабирование
|
||||
// paperSize: 'A4',
|
||||
// landscape: false,
|
||||
// copies: 1,
|
||||
// silent: true // Тихая печать
|
||||
};
|
||||
|
||||
const result = await print(tempFile, options);
|
||||
logger.info('[PRINT] Результат печати:', result);
|
||||
|
||||
// ✅ Удаляем временный файл
|
||||
try {
|
||||
if (tempFile && fs.existsSync(tempFile)) {
|
||||
fs.unlinkSync(tempFile);
|
||||
logger.info('[PRINT] Временный файл удален');
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
logger.info('[PRINT] ✅ Документ отправлен на печать');
|
||||
return true;
|
||||
|
||||
} catch (error) {
|
||||
logger.error('[PRINT] ❌ Ошибка:', error.message);
|
||||
try {
|
||||
if (tempFile && fs.existsSync(tempFile)) {
|
||||
fs.unlinkSync(tempFile);
|
||||
}
|
||||
} catch (e) {}
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
// ============================================
|
||||
// 📄 ПОЛУЧЕНИЕ СПИСКА ПРИНТЕРОВ
|
||||
// ============================================
|
||||
|
||||
ipcMain.handle('get-printers', async () => {
|
||||
return [
|
||||
{
|
||||
name: 'GP-80250 Series',
|
||||
displayName: 'GP-80250 Series',
|
||||
isDefault: true,
|
||||
status: 'ready'
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
// ============================================
|
||||
// 🚀 ЗАПУСК
|
||||
// ============================================
|
||||
@@ -226,8 +665,4 @@ app.on('activate', () => {
|
||||
|
||||
app.on('will-quit', () => {
|
||||
globalShortcut.unregisterAll();
|
||||
});
|
||||
|
||||
ipcMain.handle('get-app-config', async (event) => {
|
||||
return appConfig;
|
||||
});
|
||||
Reference in New Issue
Block a user