PDF-Schedule-Generator optimized

This commit is contained in:
Torsten Schulz
2024-09-13 19:06:58 +02:00
parent f2a8863dc7
commit d0b42ca537
3 changed files with 131 additions and 66 deletions

View File

@@ -1,43 +0,0 @@
<script setup>
import { ref } from 'vue'
defineProps({
msg: String,
})
const count = ref(0)
</script>
<template>
<h1>{{ msg }}</h1>
<div class="card">
<button type="button" @click="count++">count is {{ count }}</button>
<p>
Edit
<code>components/HelloWorld.vue</code> to test HMR
</p>
</div>
<p>
Check out
<a href="https://vuejs.org/guide/quick-start.html#local" target="_blank"
>create-vue</a
>, the official Vue + Vite starter
</p>
<p>
Learn more about IDE Support for Vue in the
<a
href="https://vuejs.org/guide/scaling-up/tooling.html#ide-support"
target="_blank"
>Vue Docs Scaling up Guide</a
>.
</p>
<p class="read-the-docs">Click on the Vite and Vue logos to learn more</p>
</template>
<style scoped>
.read-the-docs {
color: #888;
}
</style>

View File

@@ -0,0 +1,95 @@
import jsPDF from 'jspdf';
import html2canvas from 'html2canvas';
class PDFGenerator {
constructor(margin = 20, columnGap = 10) {
this.pdf = new jsPDF('p', 'mm', 'a4');
this.margin = margin;
this.columnGap = columnGap;
this.pageHeight = 295 - margin * 2; // A4 height in mm minus Ränder
this.columnWidth = (210 - margin * 2 - columnGap) / 2; // Zwei Spalten mit Lücke dazwischen
this.position = margin;
this.yPos = this.position;
this.xPos = margin;
this.isLeftColumn = true;
}
async addSchedule(element) {
const canvas = await html2canvas(element, { scale: 2 });
const imgData = canvas.toDataURL('image/png');
const imgWidth = 210 - this.margin * 2; // A4 width in mm minus Ränder
const imgHeight = (canvas.height * imgWidth) / canvas.width;
let heightLeft = imgHeight;
let position = this.margin;
this.pdf.addImage(imgData, 'PNG', this.margin, position, imgWidth, imgHeight);
heightLeft -= this.pageHeight;
while (heightLeft >= 0) {
position = heightLeft - imgHeight + this.margin;
this.pdf.addPage();
this.pdf.addImage(imgData, 'PNG', this.margin, position, imgWidth, imgHeight);
heightLeft -= this.pageHeight;
}
}
addNewPage() {
this.pdf.addPage();
this.xPos = this.margin;
this.yPos = this.position;
this.isLeftColumn = true;
}
addHeader(title) {
this.pdf.setFontSize(12);
this.pdf.setFont('helvetica', 'bold');
this.pdf.text(title, this.margin, this.position);
this.pdf.setLineWidth(0.5);
this.pdf.line(this.margin, this.position + 2, 210 - this.margin, this.position + 2);
this.yPos += 10;
this.position = this.yPos;
}
addAddress(clubName, addressLines) {
this.pdf.setFontSize(10);
// Vereinname fett drucken
this.pdf.setFont('helvetica', 'bold');
this.pdf.text(clubName, this.xPos, this.yPos);
this.yPos += 5;
this.pdf.setFont('helvetica', 'normal');
addressLines.forEach(line => {
this.pdf.text(line, this.xPos, this.yPos);
this.yPos += 5;
});
this.yPos += 10; // Abstand zwischen den Adressen
// Spaltenwechsel oder neuer Seite bei Bedarf
this.checkColumnOverflow();
}
checkColumnOverflow() {
if (this.isLeftColumn) {
if (this.yPos > this.pageHeight) {
this.xPos += this.columnWidth + this.columnGap; // Zur rechten Spalte wechseln
this.yPos = this.position; // Zurück zum Anfang der neuen Spalte
this.isLeftColumn = false;
}
} else {
if (this.yPos > this.pageHeight) {
this.pdf.addPage();
this.xPos = this.margin; // Zurück zur linken Spalte auf der neuen Seite
this.yPos = this.position;
this.isLeftColumn = true;
}
}
}
save(filename) {
this.pdf.save(filename);
}
}
export default PDFGenerator;

View File

@@ -61,6 +61,7 @@ import { mapGetters } from 'vuex';
import apiClient from '../apiClient.js';
import jsPDF from 'jspdf';
import html2canvas from 'html2canvas';
import PDFGenerator from '../components/PDFGenerator.js';
export default {
name: 'ScheduleView',
@@ -145,32 +146,42 @@ export default {
return club ? club.name : '';
},
async generatePDF() {
const element = this.$el.querySelector('.flex-item > div');
const element = this.$el.querySelector('.flex-item > div'); // Das richtige div-Element auswählen
if (element) {
const canvas = await html2canvas(element, {
scale: 2
const pdfGen = new PDFGenerator();
await pdfGen.addSchedule(element);
pdfGen.addNewPage();
pdfGen.addHeader('Hallen-Adressen');
const uniqueLocations = this.getUniqueLocations();
uniqueLocations.forEach((addressLines, clubName) => {
pdfGen.addAddress(clubName, addressLines);
});
const imgData = canvas.toDataURL('image/png');
const pdf = new jsPDF('p', 'mm', 'a4');
const margin = 20;
const imgWidth = 210 - margin * 2;
const pageHeight = 295 - margin * 2;
const imgHeight = (canvas.height * imgWidth) / canvas.width;
let heightLeft = imgHeight;
let position = margin;
pdf.addImage(imgData, 'PNG', margin, position, imgWidth, imgHeight);
heightLeft -= pageHeight;
while (heightLeft >= 0) {
position = heightLeft - imgHeight;
pdf.addPage();
pdf.addImage(imgData, 'PNG', margin, position, imgWidth, imgHeight);
heightLeft -= pageHeight;
}
pdf.save('Spielpläne.pdf');
pdfGen.save('Spielpläne.pdf');
} else {
console.error('No matches found to generate PDF.');
}
},
getUniqueLocations() {
const uniqueLocations = new Map();
this.matches.forEach(match => {
const location = match.location;
const clubName = match.homeTeam.name;
const addressLines = [
location.name,
location.address,
`${location.zip} ${location.city}`
];
const addressKey = addressLines.join('; '); // Unique key für die Map
if (!uniqueLocations.has(addressKey)) {
uniqueLocations.set(clubName, addressLines);
}
});
return uniqueLocations;
}
},
async created() {
@@ -215,9 +226,11 @@ tr:hover {
background-color: #f5f5f5;
}
th, td {
th,
td {
white-space: nowrap;
}
.hover-info {
margin-top: 10px;
background-color: #eef;
@@ -226,8 +239,8 @@ th, td {
border-radius: 4px;
max-width: 300px;
position: fixed;
top:16em;
left:14em;
top: 16em;
left: 14em;
}
.modal {