feat: Office-Preview im neuen Tab + DOCX/XLSX/Text bearbeitbar

Preview-System komplett ueberarbeitet:
- Neuer Tab: Doppelklick oder Auge-Icon oeffnet Vorschau im neuen Tab
- Dedizierte PreviewView mit Toolbar (Zurueck, Bearbeiten, Speichern, Download)
- Token wird als Query-Parameter an Preview/Download-URLs angehaengt (kein 404 mehr)

Unterstuetzte Formate:
- PDF: Inline-Anzeige im iFrame
- Bilder: Zentrierte Anzeige mit Schatten
- DOCX: HTML-Darstellung mit Formatierung (Headings, Bold, Italic, Tabellen)
- XLSX: Tabellen-Ansicht mit Sheet-Tabs
- PPTX: Folien-Navigation (vor/zurueck)
- Text/Code: Monospace mit Syntax

Bearbeitung (neu!):
- DOCX: ContentEditable-Editor, Bold/Italic/Headings bleiben erhalten,
  Speichern schreibt zurueck als .docx (python-docx)
- XLSX: Direkt in der Tabelle bearbeiten (Zellen anklicken),
  Speichern schreibt zurueck als .xlsx (openpyxl)
- Text/Code: Textarea-Editor, Speichern als UTF-8

Backend: POST /files/<id>/save mit type-spezifischer Konvertierung
- html -> DOCX (Headings, Bold/Italic/Underline erhalten)
- spreadsheet -> XLSX (Zahlen werden automatisch konvertiert)
- text -> direkt als Datei

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Stefan Hacker
2026-04-11 21:07:59 +02:00
parent 67118a34fd
commit a2ded7f97c
4 changed files with 432 additions and 1 deletions
+17 -1
View File
@@ -74,9 +74,16 @@
</template>
</Column>
<Column header="" style="width: 140px">
<Column header="" style="width: 180px">
<template #body="{ data }">
<div class="row-actions">
<Button
v-if="!data.is_folder"
icon="pi pi-eye"
text rounded size="small"
title="Vorschau"
@click.stop="openPreview(data)"
/>
<Button
:icon="data.is_folder ? 'pi pi-box' : 'pi pi-download'"
text rounded size="small"
@@ -285,6 +292,15 @@ function handleDoubleClick(event) {
const data = event.data
if (data.is_folder) {
navigateTo(data.id)
} else {
openPreview(data)
}
}
function openPreview(data) {
const previewable = /\.(pdf|docx?|xlsx?|pptx?|txt|md|json|xml|csv|py|js|html|css|yml|yaml|png|jpe?g|gif|svg|webp|bmp)$/i
if (previewable.test(data.name)) {
window.open(`/preview/${data.id}`, '_blank')
} else {
downloadFile(data)
}