- Edit
- components/HelloWorld.vue to test HMR
-
- Check out - create-vue, the official Vue + Vite starter -
-- Learn more about IDE Support for Vue in the - Vue Docs Scaling up Guide. -
-Click on the Vite and Vue logos to learn more
- - - diff --git a/frontend/src/components/PDFGenerator.js b/frontend/src/components/PDFGenerator.js new file mode 100644 index 0000000..328acd8 --- /dev/null +++ b/frontend/src/components/PDFGenerator.js @@ -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; diff --git a/frontend/src/views/ScheduleView.vue b/frontend/src/views/ScheduleView.vue index 2fd01bd..89a7a15 100644 --- a/frontend/src/views/ScheduleView.vue +++ b/frontend/src/views/ScheduleView.vue @@ -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 {