Added PDF Generation

This commit is contained in:
2026-05-26 17:11:09 +02:00
parent c5d2e32355
commit af4c944de7
12 changed files with 525 additions and 34 deletions
+65 -5
View File
@@ -68,6 +68,13 @@
}
button:hover { filter: brightness(1.1); }
button:disabled { opacity: 0.5; cursor: not-allowed; }
button.secondary {
background: transparent;
border: 1px solid var(--accent);
color: var(--accent);
}
button.secondary:hover { background: rgba(59, 130, 246, 0.12); filter: none; }
.btn-row { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 1rem; }
button.danger {
background: transparent;
border: 1px solid var(--err);
@@ -364,9 +371,15 @@
<input type="number" step="0.1" min="0" x-model.number="configForm.spacing_mm" @input="refreshLayoutPreview()">
</div>
</div>
<button type="submit" :disabled="configSaving || !configForm.plate_id || !configForm.item_id">
<span x-text="configSaving ? 'Speichere…' : 'Konfiguration speichern'"></span>
</button>
<div class="btn-row">
<button type="submit" :disabled="configSaving || !configForm.plate_id || !configForm.item_id">
<span x-text="configSaving ? 'Speichere…' : 'Konfiguration speichern'"></span>
</button>
<button type="button" class="secondary" @click="downloadLayoutPDF()"
:disabled="pdfGenerating || !layoutPreview || !configForm.plate_id || !configForm.item_id">
<span x-text="pdfGenerating ? 'PDF…' : 'PDF-Vorschau'"></span>
</button>
</div>
<p class="msg-err" x-show="configError" x-text="configError"></p>
<p class="msg-ok" x-show="configSuccess" x-text="configSuccess"></p>
</form>
@@ -377,7 +390,7 @@
<div class="layout-stats">
<span><strong x-text="layoutPreview.count"></strong> Items passen</span>
<span><strong x-text="layoutPreview.columns + ' × ' + layoutPreview.rows"></strong> Raster</span>
<span>Fußabdruck <strong x-text="layoutPreview.footprint_width_mm.toFixed(1) + ' mm'"></strong></span>
<span>Item <strong x-text="layoutPreview.footprint_width_mm.toFixed(1) + ' mm'"></strong></span>
<span>Zellenabstand <strong x-text="layoutPreview.cell_width_mm.toFixed(1) + ' mm'"></strong></span>
<span>Druckfläche <strong x-text="layoutPreview.printable_width_mm.toFixed(1) + ' × ' + layoutPreview.printable_height_mm.toFixed(1) + ' mm'"></strong></span>
</div>
@@ -399,7 +412,13 @@
<h3 x-text="c.name || ('Konfiguration ' + c.id.slice(0, 8))"></h3>
<p class="muted" x-text="configSummary(c)"></p>
</div>
<button type="button" class="danger" @click="deleteConfiguration(c.id)">Löschen</button>
<div class="row-actions">
<button type="button" class="secondary" @click="downloadConfigurationPDF(c.id)"
:disabled="pdfGenerating || !!c.preview_error" x-show="c.preview && !c.preview_error">
PDF
</button>
<button type="button" class="danger" @click="deleteConfiguration(c.id)">Löschen</button>
</div>
</header>
<p class="msg-err" x-show="c.preview_error" x-text="c.preview_error"></p>
<div class="layout-preview-wrap" x-show="c.preview && !c.preview_error">
@@ -486,6 +505,7 @@
},
layoutPreview: null,
layoutLoading: false,
pdfGenerating: false,
_layoutTimer: null,
apiUrl(path) {
@@ -714,6 +734,46 @@
}
},
async downloadBlob(path, filename) {
this.pdfGenerating = true;
this.configError = '';
try {
const res = await fetch(this.apiUrl(path));
if (!res.ok) {
let msg = res.statusText;
try {
const err = await res.json();
if (err.error) msg = err.error;
} catch (_) {}
throw new Error(msg);
}
const blob = await res.blob();
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
URL.revokeObjectURL(url);
} catch (e) {
this.configError = String(e.message || e);
} finally {
this.pdfGenerating = false;
}
},
downloadLayoutPDF() {
const q = new URLSearchParams({
plate_id: this.configForm.plate_id,
item_id: this.configForm.item_id,
spacing_mm: String(Number(this.configForm.spacing_mm) || 0),
});
this.downloadBlob('/layout/pdf?' + q, 'layout-preview.pdf');
},
downloadConfigurationPDF(id) {
this.downloadBlob('/configurations/' + id + '/pdf', 'configuration-' + id.slice(0, 8) + '.pdf');
},
async deleteConfiguration(id) {
if (!confirm('Konfiguration wirklich löschen?')) return;
this.configError = '';