bff6d70b17
Aplicatie Flask + SQLite pentru productie alimentara: cantarirea componentelor unui produs finit si tiparirea etichetei prin QZ Tray. - produse finite (retete) formate din componente cantarite - designer vizual de etichete (mm/px, zona sigura, campuri variabile, cod QR cu continut din mai multe bucati) - legare produs finit -> sablon de eticheta - navigare pe doua taburi: Cantarire / Setari - administrare: componente, produse finite, sabloane, campuri, istoric - backup, resetare baza de date, checklist de pregatire, /healthz - CLI de administrare si serviciu systemd pentru productie - vendor/syncpulse-scale-bridge (GPL-3.0-or-later) pentru citirea cantarului
90 lines
3.2 KiB
HTML
90 lines
3.2 KiB
HTML
<!doctype html>
|
||
<html lang="ro">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title>Etichetă {{ payload.fields.recipe_code }} · {{ payload.fields.total_weight }} {{ payload.fields.unit }}</title>
|
||
<style>
|
||
@page { size: {{ payload.template.width_mm }}mm {{ payload.template.height_mm }}mm; margin: 0; }
|
||
* { box-sizing: border-box; }
|
||
html, body { margin: 0; padding: 0; background: #e9edf1; }
|
||
.preview-bar {
|
||
position: sticky; top: 0; z-index: 2;
|
||
display: flex; align-items: center; gap: 12px;
|
||
padding: 10px 16px; background: #0f172a; color: #fff;
|
||
font: 14px/1.4 system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
|
||
}
|
||
.preview-bar button {
|
||
margin-left: auto; padding: 8px 16px; border: 0; border-radius: 6px;
|
||
background: #14b8a6; color: #04201d; font-weight: 700; cursor: pointer;
|
||
}
|
||
.preview-stage { padding: 24px; display: flex; justify-content: center; }
|
||
.label-sheet {
|
||
width: {{ payload.template.width_mm }}mm;
|
||
height: {{ payload.template.height_mm }}mm;
|
||
background: #fff;
|
||
overflow: hidden;
|
||
box-shadow: 0 6px 24px rgba(15, 23, 42, .18);
|
||
}
|
||
{{ payload.css|safe }}
|
||
@media print {
|
||
html, body { background: #fff; }
|
||
.preview-bar, .preview-stage { padding: 0; }
|
||
.preview-bar { display: none !important; }
|
||
.label-sheet { box-shadow: none; margin: 0; }
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
{% if preview %}
|
||
<div class="preview-bar">
|
||
<strong>Previzualizare șablon</strong>
|
||
<span>{{ payload.template.name }} · {{ payload.template.width_mm|weight }} × {{ payload.template.height_mm|weight }} mm</span>
|
||
<button type="button" onclick="window.print()">Tipărește previzualizarea</button>
|
||
</div>
|
||
{% endif %}
|
||
|
||
<div class="preview-stage">
|
||
<div class="label-sheet" id="label-sheet">
|
||
{{ payload.html|safe }}
|
||
</div>
|
||
</div>
|
||
|
||
<script id="label-payload" type="application/json">{{ payload|tojson }}</script>
|
||
<script src="{{ url_for('static', filename='js/vendor/qz-tray.js') }}"></script>
|
||
<script src="{{ url_for('static', filename='js/qz-print.js') }}"></script>
|
||
<script>
|
||
(function () {
|
||
var payload = JSON.parse(document.getElementById('label-payload').textContent);
|
||
var auto = {{ 'true' if auto else 'false' }};
|
||
var preview = {{ 'true' if preview else 'false' }};
|
||
|
||
function acknowledge() {
|
||
if (preview) return;
|
||
try {
|
||
fetch(payload.ack_url, { method: 'POST', headers: { 'Accept': 'application/json' } });
|
||
} catch (e) { /* tipărirea a reușit; confirmarea este auxiliară */ }
|
||
}
|
||
|
||
function doPrint() {
|
||
if (window.CantarirePrint && typeof window.CantarirePrint.print === 'function') {
|
||
Promise.resolve(window.CantarirePrint.print(payload)).then(function (result) {
|
||
if (result && result.method === 'qz') acknowledge();
|
||
}, function (err) {
|
||
console.error('Tipărirea QZ Tray a eșuat:', err);
|
||
});
|
||
return;
|
||
}
|
||
window.print();
|
||
acknowledge();
|
||
}
|
||
|
||
window.addEventListener('load', function () {
|
||
if (!auto) return;
|
||
setTimeout(doPrint, 120);
|
||
});
|
||
})();
|
||
</script>
|
||
</body>
|
||
</html>
|