Kuvert-Absender (Feld direkt sichtbar), Textmengen-/Session-Aenderungen
- neuauftrag: Absenderfeld fuers Kuvert IMMER sichtbar (nicht mehr an 'Kuverts generieren' gebunden), Payloads (Vorschau + Anlage) reichen envelope_sender durch. - orders: manual-order speichert envelope_sender, Preview-Proxy reicht sender durch. - bootstrap: Feld envelope_sender. - (enthaelt weiterhin die Session-Aenderungen: signature-order, mehrseitige Vorschau, 'In manuellen Druck uebernehmen', gesendet-Nummern, Generierungs-Fehleranzeige, seitenumbruch-Hinweis.) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -277,6 +277,7 @@ const collections = [
|
||||
f('needs_envelope', 'boolean', { interface: 'boolean', default: false }),
|
||||
dropdown('envelope_labeling', ['empfaenger', 'freitext', 'keine']),
|
||||
f('envelope_text', 'text', { interface: 'input-multiline', note: 'Individueller Kuverttext (bei envelope_labeling = freitext).' }),
|
||||
f('envelope_sender', 'text', { interface: 'input-multiline', note: 'Absender fürs Kuvert (oben links, 5 Zeilen); manuelle Aufträge. Empfänger rutscht dann unten rechts.' }),
|
||||
dropdown('envelope_format', ['c6', 'dinlang'], { note: 'Automatisch aus Format.' }),
|
||||
dropdown('motif_mode', ['katalog', 'upload', 'ohne', 'eigene_karten']),
|
||||
m2o('motif', 'motifs'),
|
||||
|
||||
@@ -78,14 +78,14 @@ const NeuAuftrag = defineComponent({
|
||||
// Mehrseitiges Schriftstück (Auto-Fluss über mehrere A4-Seiten).
|
||||
mehrseitig: false, seitenInfo: '',
|
||||
// Kuvert-Adresse je Empfänger: '5zeilen' (5 Zeilen) oder 'individuell' (einzeilig).
|
||||
adressModus: '5zeilen', kuverts: false, realistic: true, customer_email: '', text: '', rows: [leereZeile()],
|
||||
adressModus: '5zeilen', kuverts: false, envelopeSender: '', realistic: true, customer_email: '', text: '', rows: [leereZeile()],
|
||||
importOpen: false, importText: '',
|
||||
docs: [], previewSession: null, pLaedt: false, msg: '', err: '', hinweis: '', lb: '', lbDoc: null,
|
||||
});
|
||||
// Kuverts nur generieren, wenn angehakt (Beschriftung aus der Empfängeradresse).
|
||||
const kuvertPayload = () => (neu.kuverts
|
||||
? { needs_envelope: true, envelope_labeling: 'empfaenger', envelope_text: '' }
|
||||
: { needs_envelope: false, envelope_labeling: 'keine', envelope_text: '' });
|
||||
? { needs_envelope: true, envelope_labeling: 'empfaenger', envelope_text: '', envelope_sender: (neu.envelopeSender || '').trim() }
|
||||
: { needs_envelope: false, envelope_labeling: 'keine', envelope_text: '', envelope_sender: '' });
|
||||
|
||||
async function laden() {
|
||||
try {
|
||||
@@ -109,6 +109,35 @@ const NeuAuftrag = defineComponent({
|
||||
try { verbotenRe.value = wl ? new RegExp(`[^${wl}\\n\\r\\t]`, 'g') : null; } catch { verbotenRe.value = null; }
|
||||
neu.product = produkte.value[0]?.key || 'briefe';
|
||||
neu.format = neuFormate()[0]?.key || '';
|
||||
// Übernahme aus dem Produktions-Board (localStorage): Text/Format/Schrift
|
||||
// und alle Empfänger vorbefüllen, danach den Schlüssel wieder entfernen.
|
||||
try {
|
||||
const raw = localStorage.getItem('skrift_neuauftrag_uebernahme');
|
||||
if (raw) {
|
||||
localStorage.removeItem('skrift_neuauftrag_uebernahme');
|
||||
const d = JSON.parse(raw);
|
||||
if (d && typeof d === 'object') {
|
||||
if (d.product) neu.product = d.product;
|
||||
if (d.format) neu.format = d.format;
|
||||
if (d.font) neu.font = d.font;
|
||||
if (typeof d.text === 'string') neu.text = d.text;
|
||||
if (typeof d.multi_page === 'boolean') neu.mehrseitig = d.multi_page;
|
||||
if (typeof d.realistic === 'boolean') neu.realistic = d.realistic;
|
||||
if (d.fontSizeLetter) neu.fontSizeLetter = d.fontSizeLetter;
|
||||
if (d.fontSizeEnvelope) neu.fontSizeEnvelope = d.fontSizeEnvelope;
|
||||
if (typeof d.kuverts === 'boolean') neu.kuverts = d.kuverts;
|
||||
if (d.adressModus) neu.adressModus = d.adressModus;
|
||||
if (Array.isArray(d.rows) && d.rows.length) {
|
||||
neu.rows = d.rows.map((r) => ({
|
||||
free_text: String(r.free_text || ''),
|
||||
placeholders: (r.placeholders && typeof r.placeholders === 'object') ? { ...r.placeholders } : {},
|
||||
}));
|
||||
}
|
||||
neu.previewSession = null;
|
||||
neu.msg = 'Aus Auftrag übernommen – anpassen, dann Vorschau/Anlegen.';
|
||||
}
|
||||
}
|
||||
} catch { /* localStorage nicht verfügbar → ignorieren */ }
|
||||
} catch (e) {
|
||||
neu.err = e?.response?.data?.errors?.[0]?.message || e?.message || 'Laden fehlgeschlagen.';
|
||||
} finally { geladen.value = true; }
|
||||
@@ -237,7 +266,7 @@ const NeuAuftrag = defineComponent({
|
||||
const kv = kuvertPayload();
|
||||
const res = await api.post('/skrift-orders/preview', {
|
||||
text: neu.text, font: neu.font, format: neu.format, realistic: neu.realistic,
|
||||
envelope_labeling: kv.envelope_labeling, envelope_text: kv.envelope_text,
|
||||
envelope_labeling: kv.envelope_labeling, envelope_text: kv.envelope_text, envelope_sender: kv.envelope_sender,
|
||||
font_scale_letter: neu.fontSizeLetter / 100, font_scale_envelope: neu.fontSizeEnvelope / 100,
|
||||
multi_page: neu.mehrseitig,
|
||||
...briefSigPayload(),
|
||||
@@ -278,6 +307,33 @@ const NeuAuftrag = defineComponent({
|
||||
neu.err = e?.response?.data?.error || e?.message || 'Seitenzählung fehlgeschlagen.';
|
||||
}
|
||||
}
|
||||
// Das Backend generiert die Dokumente asynchron (fire-and-forget). Fehler
|
||||
// landen als status_history-Eintrag 'fehler' am Auftrag, Erfolg als 'in_queue'.
|
||||
// Wir pollen kurz und zeigen ein Problem DIREKT hier im Dashboard – statt dass
|
||||
// es still verschluckt wird und „nie Dokumente" entstehen.
|
||||
async function ueberwacheGenerierung(orderId, nr) {
|
||||
for (let i = 0; i < 20; i += 1) {
|
||||
await new Promise((r) => setTimeout(r, 1500));
|
||||
let sh = [];
|
||||
try {
|
||||
sh = (await api.get('/items/status_history', { params: {
|
||||
'filter[order][_eq]': orderId, sort: '-date_created', limit: 4, fields: 'status,note',
|
||||
} })).data?.data || [];
|
||||
} catch { continue; }
|
||||
const fehler = sh.find((s) => s.status === 'fehler');
|
||||
if (fehler) {
|
||||
neu.err = `Generierung fehlgeschlagen: ${fehler.note || 'unbekannter Grund'}`;
|
||||
neu.msg = '';
|
||||
return;
|
||||
}
|
||||
if (sh.some((s) => s.status === 'in_queue')) {
|
||||
neu.msg = `✓ Auftrag ${nr} angelegt & generiert – liegt im Produktions-Board unter „Neu". Felder bleiben stehen.`;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Kein eindeutiges Signal (langsame Generierung oder Datei-Produkt ohne Kuvert).
|
||||
neu.msg = `✓ Auftrag ${nr} angelegt – Dokumente werden noch generiert. Status im Produktions-Board prüfen.`;
|
||||
}
|
||||
async function absenden() {
|
||||
// Empfänger sind optional – ohne Empfänger genau ein Schriftstück (Blankozeile).
|
||||
const ent = entries();
|
||||
@@ -291,16 +347,19 @@ const NeuAuftrag = defineComponent({
|
||||
font_scale_letter: neu.fontSizeLetter / 100, font_scale_envelope: neu.fontSizeEnvelope / 100,
|
||||
multi_page: neu.mehrseitig,
|
||||
...briefSigPayload(),
|
||||
needs_envelope: kv.needs_envelope, envelope_labeling: kv.envelope_labeling, envelope_text: kv.envelope_text,
|
||||
needs_envelope: kv.needs_envelope, envelope_labeling: kv.envelope_labeling, envelope_text: kv.envelope_text, envelope_sender: kv.envelope_sender,
|
||||
shipping_type: 'sammel',
|
||||
customer_email: neu.customer_email.trim() || undefined,
|
||||
text_template: neu.text, preview_session: neu.previewSession || undefined,
|
||||
entries: payloadEntries,
|
||||
});
|
||||
const nr = res.data?.order_number || '';
|
||||
const oid = res.data?.id;
|
||||
// Felder bewusst NICHT zurücksetzen – der letzte Auftrag bleibt stehen,
|
||||
// damit er sich als Vorlage für den nächsten leicht anpassen lässt.
|
||||
neu.msg = `✓ Auftrag ${nr} angelegt – liegt im Produktions-Board unter „Neu". Felder bleiben stehen; mit „Formular leeren" zurücksetzen.`;
|
||||
neu.msg = `✓ Auftrag ${nr} angelegt – Dokumente werden generiert …`;
|
||||
// Generierung im Hintergrund überwachen; ein Backend-Fehler erscheint dann hier.
|
||||
if (oid) ueberwacheGenerierung(oid, nr);
|
||||
} catch (e) { neu.err = e?.response?.data?.error || e?.message || 'Anlegen fehlgeschlagen.'; neu.msg = ''; }
|
||||
}
|
||||
function zuruecksetzen() {
|
||||
@@ -308,7 +367,7 @@ const NeuAuftrag = defineComponent({
|
||||
product: produkte.value[0]?.key || 'briefe', format: neuFormate()[0]?.key || '', font: 'tilda',
|
||||
fontSizeLetter: 100, fontSizeEnvelope: 100,
|
||||
sigSvg: '', sigLabel: '', sigX: 0, sigY: 0, sigScale: 100, mehrseitig: false, seitenInfo: '',
|
||||
adressModus: '5zeilen', kuverts: false, realistic: true, customer_email: '', text: '', rows: [leereZeile()],
|
||||
adressModus: '5zeilen', kuverts: false, envelopeSender: '', realistic: true, customer_email: '', text: '', rows: [leereZeile()],
|
||||
importOpen: false, importText: '',
|
||||
docs: [], previewSession: null, pLaedt: false, msg: '', err: '', hinweis: '', lb: '',
|
||||
});
|
||||
@@ -449,6 +508,17 @@ const NeuAuftrag = defineComponent({
|
||||
h('input', { type: 'checkbox', checked: neu.kuverts, onChange: (e) => { neu.kuverts = e.target.checked; neu.previewSession = null; }, style: 'width:16px;height:16px;' }),
|
||||
'Kuverts generieren',
|
||||
]),
|
||||
h('div', { style: 'margin-top:14px;' }, [
|
||||
label('Absender aufs Kuvert (oben links, max. 5 Zeilen, optional)'),
|
||||
h('textarea', {
|
||||
value: neu.envelopeSender, rows: 5,
|
||||
placeholder: 'Max Mustermann\nMusterstraße 1\n12345 Musterstadt',
|
||||
style: WFELD + 'width:100%;font-family:var(--family-monospace,monospace);',
|
||||
onInput: (e) => { neu.envelopeSender = saeubere(String(e.target.value)).split('\n').slice(0, 5).join('\n'); neu.previewSession = null; },
|
||||
}),
|
||||
h('div', { style: 'font-size:11px;color:var(--foreground-subdued,#888);margin-top:4px;line-height:1.5;' },
|
||||
'Ist ein Absender gesetzt, rutscht der Empfänger auf dem Kuvert automatisch nach unten rechts. Wirkt nur, wenn „Kuverts generieren" aktiv ist.'),
|
||||
]),
|
||||
neu.importOpen ? h('div', { style: 'margin-top:12px;' }, [
|
||||
subdued('Aus Excel kopieren und einfügen – eine Zeile pro Empfänger, Spalten mit Tab/Semikolon/Komma. '
|
||||
+ 'Reihenfolge: erst individuelle Platzhalter, dann ' + (neu.adressModus === 'individuell' ? 'die eine Adresszeile.' : 'Zeile 1–5.'), 'margin-bottom:6px;'),
|
||||
@@ -478,6 +548,8 @@ const NeuAuftrag = defineComponent({
|
||||
neu.previewSession = null;
|
||||
requestAnimationFrame(() => { try { ta.selectionStart = ta.selectionEnd = s + 1; } catch {} });
|
||||
} }),
|
||||
h('div', { style: 'font-size:11px;color:var(--foreground-subdued,#888);margin-top:4px;line-height:1.5;' },
|
||||
'Tipp: [[seitenumbruch]] erzwingt an dieser Stelle eine neue Seite (mehrseitiger Brief). [[signatur1]] setzt die Unterschrift.'),
|
||||
// ── Unterschrift auf den Brief ─────────────────────────────────────
|
||||
h('div', { style: 'margin-top:16px;border-top:1px solid rgba(30,80,165,.15);padding-top:14px;' }, [
|
||||
label('Unterschrift auf den Brief (optional) · Platzhalter [[signatur1]]'),
|
||||
|
||||
@@ -677,6 +677,8 @@ const handler = (router, { services, getSchema, logger }) => {
|
||||
text: istFreitext ? clean(String(body.envelope_text || '')) : adressBlock(e),
|
||||
format: umschlagFormat, font, realisticHandwriting: realistisch,
|
||||
placeholders: platzhalterVon(e, i), fontScale: fsEnv,
|
||||
// Absender aufs Kuvert (oben links); Empfänger rutscht dann unten rechts.
|
||||
sender: clean(String(body.envelope_sender || '')),
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -1566,6 +1568,7 @@ const handler = (router, { services, getSchema, logger }) => {
|
||||
envelope_labeling: body.envelope_labeling ?? null,
|
||||
envelope_text: clean(body.envelope_text) ?? null,
|
||||
envelope_format: envelopeFormat,
|
||||
envelope_sender: clean(body.envelope_sender) ?? null,
|
||||
text_template: clean(body.text_template) ?? null,
|
||||
preview_session: typeof body.preview_session === 'string' ? body.preview_session : null,
|
||||
entries_count: entries.length,
|
||||
|
||||
@@ -612,14 +612,33 @@ const Board = defineComponent({
|
||||
}
|
||||
|
||||
// ── Empfänger-Drawer (mit Dokument-Vorschau je Zeile) ──
|
||||
const dateiNummer = (name) => {
|
||||
const z = String(name || '').replace(/\.[^.]*$/, '').replace(/\D/g, '');
|
||||
return z ? parseInt(z, 10) : null;
|
||||
// Zerlegt einen Dateinamen in Brief-Nummer und Seite:
|
||||
// letter_001_p2.svg → { nummer:1, seite:2 }, letter_003.svg → { nummer:3, seite:1 }.
|
||||
const briefTeile = (name) => {
|
||||
const base = String(name || '').replace(/\.[^.]*$/, '');
|
||||
const m = base.match(/(\d+)(?:_p(\d+))?$/i);
|
||||
return { nummer: m ? parseInt(m[1], 10) : null, seite: (m && m[2]) ? parseInt(m[2], 10) : 1 };
|
||||
};
|
||||
// Distinkte Brief-Nummern eines Typs, aufsteigend – für positionsbasierte
|
||||
// Zuordnung Empfänger↔Brief (robust gegen 0-/1-basierte Namen). Je Nummer die
|
||||
// nach Seite sortierten Dateien (mehrseitige Briefe = mehrere Einträge).
|
||||
const briefeNachRang = (kind) => {
|
||||
const map = new Map();
|
||||
for (const f of empf.files.filter((x) => x.kind === kind)) {
|
||||
const { nummer, seite } = briefTeile(f.name);
|
||||
const key = nummer ?? f.name;
|
||||
if (!map.has(key)) map.set(key, []);
|
||||
map.get(key).push({ f, seite });
|
||||
}
|
||||
const keys = [...map.keys()].sort((a, b) => ((typeof a === 'number' && typeof b === 'number') ? a - b : String(a).localeCompare(String(b))));
|
||||
return keys.map((k) => map.get(k).sort((a, b) => a.seite - b.seite).map((x) => x.f));
|
||||
};
|
||||
// Vorschau-Blob-URLs des Schriftstücks (Array) sauber freigeben.
|
||||
const revokePl = () => { (Array.isArray(empf.pl) ? empf.pl : []).forEach((p) => { try { URL.revokeObjectURL(p.url); } catch { /* egal */ } }); };
|
||||
async function openEmpf(order) {
|
||||
empf.open = true; empf.mode = 'order'; empf.titel = `Empfänger · ${order.order_number}`;
|
||||
empf.order = order.order_number; empf.suche = ''; empf.rows = []; empf.files = []; empf.info = '';
|
||||
empf.selected = null; empf.pl = ''; empf.pe = ''; empf.perr = ''; empf.lb = ''; empf.laden = true;
|
||||
empf.order = order.order_number; empf.orderObj = order; empf.suche = ''; empf.rows = []; empf.files = []; empf.info = '';
|
||||
empf.selected = null; revokePl(); empf.pl = []; empf.pe = ''; empf.perr = ''; empf.lb = ''; empf.laden = true;
|
||||
try {
|
||||
const [ent, fl] = await Promise.all([
|
||||
api.get('/items/order_entries', { params: { 'filter[order][_eq]': order.id, sort: 'letter_number', limit: -1, fields: '*' } }),
|
||||
@@ -631,7 +650,7 @@ const Board = defineComponent({
|
||||
}
|
||||
function openReqInfo(req) {
|
||||
empf.open = true; empf.mode = 'request'; empf.titel = `Anfrage · ${req.name}`; empf.suche = ''; empf.info = '';
|
||||
empf.files = []; empf.selected = null; empf.pl = ''; empf.pe = ''; empf.lb = '';
|
||||
empf.files = []; empf.selected = null; revokePl(); empf.pl = []; empf.pe = ''; empf.lb = '';
|
||||
const a = req.address && typeof req.address === 'object' ? Object.entries(req.address).map(([k, v]) => `${k}: ${v}`).join(' · ') : (req.address || '');
|
||||
empf.rows = [
|
||||
{ nr: '✉', zeile: `${req.name} · ${req.email}${req.phone ? ' · ' + req.phone : ''}`, extra: '' },
|
||||
@@ -644,27 +663,67 @@ const Board = defineComponent({
|
||||
const res = await api.get('/skrift-orders/files/' + encodeURIComponent(empf.order) + '/download', { params: { rel: f.rel }, responseType: 'blob' });
|
||||
return URL.createObjectURL(res.data);
|
||||
}
|
||||
// Positionsbasierter Abgleich: der i-te Empfänger → die i-te Datei. Robust
|
||||
// gegen alte (0-basierte) und neue (1-basierte) Dateinamen.
|
||||
const nachNummer = (kind) => empf.files
|
||||
.filter((f) => f.kind === kind)
|
||||
.sort((a, b) => (dateiNummer(a.name) ?? 0) - (dateiNummer(b.name) ?? 0));
|
||||
async function previewEntry(nr, rang) {
|
||||
empf.selected = nr; empf.perr = ''; empf.pLoad = true;
|
||||
if (empf.pl) URL.revokeObjectURL(empf.pl); if (empf.pe) URL.revokeObjectURL(empf.pe);
|
||||
empf.pl = ''; empf.pe = '';
|
||||
revokePl(); if (empf.pe) URL.revokeObjectURL(empf.pe);
|
||||
empf.pl = []; empf.pe = '';
|
||||
try {
|
||||
const brief = nachNummer('schriftstueck')[rang] || empf.files.find((f) => f.kind === 'schriftstueck' && dateiNummer(f.name) === nr);
|
||||
const kuvert = nachNummer('umschlag')[rang] || empf.files.find((f) => f.kind === 'umschlag' && dateiNummer(f.name) === nr);
|
||||
if (brief) empf.pl = await empfBlob(brief);
|
||||
// Positionsbasiert: der rang-te Empfänger → die rang-te Brief-Nummer.
|
||||
// Mehrseitige Briefe liefern hier mehrere Seiten-Dateien.
|
||||
const seiten = briefeNachRang('schriftstueck')[rang] || [];
|
||||
const kuvert = (briefeNachRang('umschlag')[rang] || [])[0] || null;
|
||||
const geladen = [];
|
||||
for (let i = 0; i < seiten.length; i += 1) {
|
||||
geladen.push({ url: await empfBlob(seiten[i]), label: seiten.length > 1 ? `Seite ${i + 1}` : '' });
|
||||
}
|
||||
empf.pl = geladen;
|
||||
if (kuvert) empf.pe = await empfBlob(kuvert);
|
||||
if (!brief && !kuvert) {
|
||||
if (!seiten.length && !kuvert) {
|
||||
empf.perr = empf.files.length
|
||||
? 'Für diese Nummer sind noch keine Dateien generiert.'
|
||||
: 'Für diesen Auftrag wurden noch keine Dateien generiert (erst nach Abschluss/Generierung).';
|
||||
}
|
||||
} catch (e) { empf.perr = 'Vorschau fehlgeschlagen.'; } finally { empf.pLoad = false; }
|
||||
}
|
||||
// Auftrag in den manuellen Druck (neuauftrag-Modul) übernehmen: Text, Schrift,
|
||||
// Format, Kuvert-Option und ALLE Empfänger (free_text + Platzhalter) per
|
||||
// localStorage übergeben und dorthin navigieren – dort editierbar/neu druckbar.
|
||||
async function uebernehmenManuell() {
|
||||
const o = empf.orderObj;
|
||||
if (!o) return;
|
||||
try {
|
||||
const full = (await api.get('/items/orders/' + o.id, { params: {
|
||||
fields: 'text_template,font,multi_page,realistic,font_scale_letter,font_scale_envelope,needs_envelope,product.key,format.key',
|
||||
} })).data?.data || {};
|
||||
const ent = (await api.get('/items/order_entries', { params: {
|
||||
'filter[order][_eq]': o.id, sort: 'letter_number', limit: -1, fields: 'free_text,placeholders,letter_number',
|
||||
} })).data?.data || [];
|
||||
const rows = ent.map((e) => ({
|
||||
free_text: String(e.free_text || ''),
|
||||
placeholders: (e.placeholders && typeof e.placeholders === 'object') ? e.placeholders : {},
|
||||
}));
|
||||
const daten = {
|
||||
product: full.product?.key || '',
|
||||
format: full.format?.key || o.format?.key || '',
|
||||
font: full.font || 'tilda',
|
||||
text: full.text_template || '',
|
||||
multi_page: !!full.multi_page,
|
||||
realistic: full.realistic !== false,
|
||||
fontSizeLetter: Math.round((Number(full.font_scale_letter) || 1) * 100),
|
||||
fontSizeEnvelope: Math.round((Number(full.font_scale_envelope) || 1) * 100),
|
||||
kuverts: !!full.needs_envelope,
|
||||
adressModus: rows.some((r) => r.free_text.includes('\n')) ? '5zeilen' : 'individuell',
|
||||
rows,
|
||||
};
|
||||
localStorage.setItem('skrift_neuauftrag_uebernahme', JSON.stringify(daten));
|
||||
// Zum manuellen Druck-Modul wechseln. Die Admin-App liegt unter /admin/ –
|
||||
// Basis aus dem aktuellen Pfad ableiten (robust gegen Unterpfad-Prefixe).
|
||||
const base = window.location.pathname.split('/admin/')[0] + '/admin/';
|
||||
window.location.href = base + 'skrift-neuauftrag';
|
||||
} catch (e) {
|
||||
flash('Übernahme fehlgeschlagen: ' + (e?.response?.data?.errors?.[0]?.message || e?.message || 'Fehler'));
|
||||
}
|
||||
}
|
||||
const empfTreffer = computed(() => {
|
||||
const q = empf.suche.trim().toLowerCase();
|
||||
if (!q) return empf.rows;
|
||||
@@ -1074,14 +1133,21 @@ const Board = defineComponent({
|
||||
: subdued('Keine Treffer.')),
|
||||
]);
|
||||
// Rechte Spalte (~30 %): Vorschau; Klick aufs Bild → große Lightbox.
|
||||
const vorschauBox = (titel, url) => h('div', { style: 'display:flex;flex-direction:column;gap:6px;' }, [
|
||||
h('div', { style: 'font-size:11px;text-transform:uppercase;letter-spacing:.05em;color:var(--foreground-subdued);' }, titel),
|
||||
h('div', {
|
||||
onClick: url ? () => { empf.lb = url; empf.lbTitel = titel; } : undefined,
|
||||
style: 'border:1px solid var(--border-subdued);border-radius:8px;background:#fff;display:flex;align-items:center;justify-content:center;min-height:150px;padding:8px;'
|
||||
+ (url ? 'cursor:zoom-in;' : ''),
|
||||
}, url ? h('img', { src: url, style: 'max-width:100%;max-height:30vh;object-fit:contain;' }) : subdued('—')),
|
||||
const vorschauBox = (titel, quelle) => {
|
||||
// quelle: Array [{url,label}] (Schriftstück, evtl. mehrseitig) oder einzelne URL (Kuvert).
|
||||
const bilder = Array.isArray(quelle) ? quelle : (quelle ? [{ url: quelle, label: '' }] : []);
|
||||
return h('div', { style: 'display:flex;flex-direction:column;gap:6px;' }, [
|
||||
h('div', { style: 'font-size:11px;text-transform:uppercase;letter-spacing:.05em;color:var(--foreground-subdued);' },
|
||||
titel + (bilder.length > 1 ? ` · ${bilder.length} Seiten` : '')),
|
||||
...(bilder.length ? bilder.map((b) => h('div', {
|
||||
onClick: () => { empf.lb = b.url; empf.lbTitel = titel + (b.label ? ' · ' + b.label : ''); },
|
||||
style: 'position:relative;border:1px solid var(--border-subdued);border-radius:8px;background:#fff;display:flex;align-items:center;justify-content:center;min-height:150px;padding:8px;cursor:zoom-in;',
|
||||
}, [
|
||||
b.label ? h('div', { style: 'position:absolute;top:4px;left:6px;font-size:10px;color:var(--foreground-subdued);background:var(--background-page);padding:0 4px;border-radius:4px;' }, b.label) : null,
|
||||
h('img', { src: b.url, style: 'max-width:100%;max-height:30vh;object-fit:contain;' }),
|
||||
])) : [h('div', { style: 'border:1px solid var(--border-subdued);border-radius:8px;background:#fff;display:flex;align-items:center;justify-content:center;min-height:150px;padding:8px;' }, subdued('—'))]),
|
||||
]);
|
||||
};
|
||||
const vorschau = istOrder ? h('div', { style: 'flex:0 0 30%;min-width:220px;display:flex;flex-direction:column;gap:14px;' },
|
||||
empf.selected == null ? [subdued('Empfänger anklicken → Vorschau (Klick aufs Bild = groß).')]
|
||||
: empf.pLoad ? [subdued('Vorschau wird geladen …')]
|
||||
@@ -1090,8 +1156,12 @@ const Board = defineComponent({
|
||||
vorschauBox('📄 Schriftstück', empf.pl),
|
||||
vorschauBox('✉ Kuvert', empf.pe),
|
||||
]) : null;
|
||||
const uebernahme = istOrder ? h('div', { style: 'padding:12px 20px;border-bottom:1px solid var(--border-subdued);display:flex;justify-content:flex-end;align-items:center;gap:10px;flex-wrap:wrap;' }, [
|
||||
subdued('Text, Format & alle Empfänger in den manuellen Druck laden – dort anpassen und neu anlegen.'),
|
||||
btn('✍ In manuellen Druck übernehmen', uebernehmenManuell, 'primary'),
|
||||
]) : null;
|
||||
const body = h('div', { style: 'padding:16px 20px;display:flex;gap:20px;flex-wrap:wrap;' }, [liste, vorschau]);
|
||||
return overlay([modalKopf(empf.titel, () => { empf.open = false; }), body], () => { empf.open = false; }, istOrder ? '980px' : '620px');
|
||||
return overlay([modalKopf(empf.titel, () => { empf.open = false; }), uebernahme, body].filter(Boolean), () => { empf.open = false; }, istOrder ? '980px' : '620px');
|
||||
}
|
||||
// Große Einzelvorschau (Lightbox) über dem Empfänger-Dialog.
|
||||
function lightboxModal() {
|
||||
|
||||
Reference in New Issue
Block a user