Enhance Excel export functionality in backend and frontend; implement Excel file generation and update download logic for correct file extension

This commit is contained in:
Torsten Schulz (local)
2025-10-18 00:03:11 +02:00
parent 6a519fc4d4
commit 6b89272335
5 changed files with 872 additions and 23 deletions

View File

@@ -16,23 +16,23 @@
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.18.2",
"cors": "^2.8.5",
"helmet": "^7.1.0",
"morgan": "^1.10.0",
"dotenv": "^16.3.1",
"mysql2": "^3.6.5",
"sequelize": "^6.35.2",
"bcrypt": "^5.1.1",
"jsonwebtoken": "^9.0.2",
"nodemailer": "^6.9.7",
"cors": "^2.8.5",
"crypto": "^1.0.1",
"dotenv": "^16.3.1",
"exceljs": "^4.4.0",
"express": "^4.18.2",
"express-session": "^1.18.0",
"helmet": "^7.1.0",
"jsonwebtoken": "^9.0.2",
"morgan": "^1.10.0",
"mysql2": "^3.6.5",
"nodemailer": "^6.9.7",
"passport": "^0.7.0",
"passport-google-oauth20": "^2.0.0",
"express-session": "^1.18.0"
"sequelize": "^6.35.2"
},
"devDependencies": {
"nodemon": "^3.0.2"
}
}

View File

@@ -34,7 +34,7 @@ class ExportController {
}
/**
* Exportiert Daten als Excel
* Exportiert Daten als Excel (.xlsx)
*/
async exportExcel(req, res) {
try {
@@ -47,11 +47,11 @@ class ExportController {
});
}
const excel = await ExportService.exportExcel(userId, startDate, endDate);
const buffer = await ExportService.exportExcel(userId, startDate, endDate);
res.setHeader('Content-Type', 'text/csv; charset=utf-8');
res.setHeader('Content-Disposition', `attachment; filename="zeiterfassung_${startDate}_${endDate}.csv"`);
res.send(excel);
res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
res.setHeader('Content-Disposition', `attachment; filename="zeiterfassung_${startDate}_${endDate}.xlsx"`);
res.send(buffer);
} catch (error) {
console.error('Fehler beim Excel-Export:', error);
res.status(500).json({

View File

@@ -29,17 +29,49 @@ class ExportService {
}
/**
* Exportiert Daten als Excel (CSV mit Excel-kompatiblem Encoding)
* Exportiert Daten als echte Excel-Datei (.xlsx)
* @param {number} userId - User-ID
* @param {string} startDate - Startdatum
* @param {string} endDate - Enddatum
* @returns {Promise<string>} Excel-kompatibles CSV mit BOM
* @returns {Promise<Buffer>} Excel-Datei als Buffer
*/
async exportExcel(userId, startDate, endDate) {
const csv = await this.exportCSV(userId, startDate, endDate);
const ExcelJS = require('exceljs');
const data = await this._getExportData(userId, startDate, endDate);
// UTF-8 BOM für Excel
return '\ufeff' + csv;
// Erstelle Workbook
const workbook = new ExcelJS.Workbook();
const worksheet = workbook.addWorksheet('Zeiterfassung');
// Definiere Spalten
worksheet.columns = [
{ header: 'Datum', key: 'date', width: 12 },
{ header: 'Uhrzeit', key: 'time', width: 10 },
{ header: 'Aktivität', key: 'activity', width: 20 },
{ header: 'Gesamt-Netto-Arbeitszeit', key: 'netWorkTime', width: 25 }
];
// Style für Header
worksheet.getRow(1).font = { bold: true };
worksheet.getRow(1).fill = {
type: 'pattern',
pattern: 'solid',
fgColor: { argb: 'FFE0E0E0' }
};
// Füge Daten hinzu
data.forEach(row => {
worksheet.addRow({
date: row.date,
time: row.time,
activity: row.activity,
netWorkTime: row.netWorkTime
});
});
// Generiere Buffer
const buffer = await workbook.xlsx.writeBuffer();
return buffer;
}
/**

View File

@@ -153,7 +153,11 @@ async function downloadFile(format) {
const downloadUrl = window.URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = downloadUrl
a.download = `zeiterfassung_${form.value.startDate}_${form.value.endDate}.csv`
// Richtiger Dateiname je nach Format
const extension = format === 'excel' ? 'xlsx' : 'csv'
a.download = `zeiterfassung_${form.value.startDate}_${form.value.endDate}.${extension}`
document.body.appendChild(a)
a.click()
document.body.removeChild(a)

815
package-lock.json generated

File diff suppressed because it is too large Load Diff