Add tape printing page with live preview and printer probe
This commit is contained in:
1 parent
b1c071ff88
commit
60f83fc002
1 file changed
+213
@@ -0,0 +1,213 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% block title %}Tape Printing - Admin{% endblock %}
|
||||||
|
|
||||||
|
{% block extra_css %}
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/labels-bricklink.css') }}">
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="bl">
|
||||||
|
{% set section = 'labels' %}
|
||||||
|
{% set bar_sub = 'Brother VC-500W continuous tape' %}
|
||||||
|
{% set crumbs = [{'name': 'My Labels', 'url': url_for('labels.index')}, {'name': 'Tape printing'}] %}
|
||||||
|
{% include 'admin/labels/_nav.html' %}
|
||||||
|
|
||||||
|
<form method="POST" action="{{ url_for('tape.index') }}" id="tapeForm">
|
||||||
|
<input type="hidden" name="ids" value="{{ ids }}">
|
||||||
|
|
||||||
|
<div class="row g-2 mt-0">
|
||||||
|
<!-- Options -->
|
||||||
|
<div class="col-lg-4">
|
||||||
|
<div class="bl-panel">
|
||||||
|
<div class="bl-panel-head"><span>Tape & layout</span></div>
|
||||||
|
<div class="bl-panel-body">
|
||||||
|
<label class="fld">Tape loaded in the printer</label>
|
||||||
|
<select name="tape" class="w-100 mb-2" id="tapeSelect">
|
||||||
|
{% for key, preset in tapes.items() %}
|
||||||
|
<option value="{{ key }}" {% if key == options.tape %}selected{% endif %}>{{ preset.name }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
<div class="k mb-2" style="text-transform:none;">{{ tape.note }}</div>
|
||||||
|
|
||||||
|
<div class="row g-2">
|
||||||
|
<div class="col-6">
|
||||||
|
<label class="fld">Copies each</label>
|
||||||
|
<input type="number" name="copies" min="1" max="99" class="w-100"
|
||||||
|
value="{{ options.copies }}">
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<label class="fld">Text size</label>
|
||||||
|
<input type="number" name="font_scale" step="0.05" min="0.5" max="2.5"
|
||||||
|
class="w-100" value="{{ options.font_scale }}">
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<label class="fld">Image width %</label>
|
||||||
|
<input type="number" name="image_percent" min="0" max="60"
|
||||||
|
class="w-100" value="{{ options.image_percent }}">
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<label class="fld">Padding (mm)</label>
|
||||||
|
<input type="number" name="padding_mm" step="0.1" min="0" max="10"
|
||||||
|
class="w-100" value="{{ options.padding_mm }}">
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<label class="fld">Fixed length (mm)</label>
|
||||||
|
<input type="number" name="fixed_length_mm" step="1" min="12" max="430"
|
||||||
|
class="w-100" value="{{ options.fixed_length_mm or '' }}"
|
||||||
|
placeholder="auto">
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<label class="fld">Align</label>
|
||||||
|
<select name="align" class="w-100">
|
||||||
|
<option value="left" {% if options.align == 'left' %}selected{% endif %}>Left</option>
|
||||||
|
<option value="center" {% if options.align == 'center' %}selected{% endif %}>Centre</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr class="my-2">
|
||||||
|
<label class="d-flex align-items-center gap-2 mb-1" style="font-size:12px;">
|
||||||
|
<input type="checkbox" name="rotate" value="1" {% if options.rotate %}checked{% endif %}>
|
||||||
|
Rotate for tape feed
|
||||||
|
</label>
|
||||||
|
<div class="k mb-2" style="text-transform:none;">
|
||||||
|
On by default. If your first test print comes out sideways, untick this.
|
||||||
|
</div>
|
||||||
|
<label class="d-flex align-items-center gap-2" style="font-size:12px;">
|
||||||
|
<input type="checkbox" name="outline" value="1" {% if options.outline %}checked{% endif %}>
|
||||||
|
Outline each label
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bl-panel">
|
||||||
|
<div class="bl-panel-head"><span>Show on label</span></div>
|
||||||
|
<div class="bl-panel-body">
|
||||||
|
{% for value, text in field_options %}
|
||||||
|
<label class="d-flex align-items-center gap-2" style="font-size:12px;">
|
||||||
|
<input type="checkbox" name="fields" value="{{ value }}"
|
||||||
|
{% if value in options.fields %}checked{% endif %}> {{ text }}
|
||||||
|
</label>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Preview and actions -->
|
||||||
|
<div class="col-lg-8">
|
||||||
|
<div class="bl-panel">
|
||||||
|
<div class="bl-panel-head">
|
||||||
|
<span>Printer</span>
|
||||||
|
<span>
|
||||||
|
{% if printer.host %}
|
||||||
|
<span class="num">{{ printer.host }}:{{ printer.port }}</span>
|
||||||
|
{% else %}
|
||||||
|
<span class="bl-tag warn">Not configured</span>
|
||||||
|
{% endif %}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="bl-panel-body">
|
||||||
|
{% if printer.host %}
|
||||||
|
<div class="d-flex gap-2 flex-wrap align-items-center">
|
||||||
|
<button type="button" class="bl-btn" id="testBtn">
|
||||||
|
<i class="bi bi-plug"></i> Test connection
|
||||||
|
</button>
|
||||||
|
<button type="submit" formaction="{{ url_for('tape.print_tape') }}"
|
||||||
|
class="bl-btn bl-btn-go">
|
||||||
|
<i class="bi bi-printer"></i> Print {{ labels|length }} label{{ '' if labels|length == 1 else 's' }} to tape
|
||||||
|
</button>
|
||||||
|
<button type="submit" formaction="{{ url_for('tape.download_pdf') }}"
|
||||||
|
class="bl-btn">
|
||||||
|
<i class="bi bi-file-pdf"></i> Download PDF
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div id="testResult" class="mt-2" style="font-size:12px;"></div>
|
||||||
|
{% else %}
|
||||||
|
<p class="mb-2">
|
||||||
|
No tape printer address set yet. Put the VC-500W's IP address into
|
||||||
|
<a href="{{ url_for('labels.settings') }}">Label Settings</a> and it can be
|
||||||
|
printed to directly over the network.
|
||||||
|
</p>
|
||||||
|
<button type="submit" formaction="{{ url_for('tape.download_pdf') }}" class="bl-btn">
|
||||||
|
<i class="bi bi-file-pdf"></i> Download PDF instead
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bl-panel">
|
||||||
|
<div class="bl-panel-head">
|
||||||
|
<span>Preview — {{ labels|length }} label{{ '' if labels|length == 1 else 's' }} at actual proportions</span>
|
||||||
|
<button type="submit" class="bl-btn bl-btn-sm">Refresh preview</button>
|
||||||
|
</div>
|
||||||
|
<div class="bl-panel-body">
|
||||||
|
<div class="bl-tape-preview">
|
||||||
|
{% for label in labels[:12] %}
|
||||||
|
<img class="mb-2" alt="{{ label.display_title }}"
|
||||||
|
src="{{ url_for('tape.preview', id=label.id, tape=options.tape,
|
||||||
|
font_scale=options.font_scale,
|
||||||
|
image_percent=options.image_percent,
|
||||||
|
padding_mm=options.padding_mm,
|
||||||
|
align=options.align,
|
||||||
|
outline='1' if options.outline else '',
|
||||||
|
fixed_length_mm=options.fixed_length_mm or '',
|
||||||
|
fields=options.fields) }}">
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% if labels|length > 12 %}
|
||||||
|
<div class="k mt-2" style="text-transform:none;">
|
||||||
|
Showing the first 12 of {{ labels|length }}. All of them print.
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="k mt-2" style="text-transform:none;">
|
||||||
|
Rendered at 313 dpi on {{ tape.tape_mm }}mm tape
|
||||||
|
({{ tape.printable_mm }}mm printable). Preview is shown flat;
|
||||||
|
the job is rotated if "Rotate for tape feed" is ticked.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block extra_js %}
|
||||||
|
<script>
|
||||||
|
document.getElementById('testBtn')?.addEventListener('click', function () {
|
||||||
|
const out = document.getElementById('testResult');
|
||||||
|
out.innerHTML = '<span class="bl-tag">Probing…</span>';
|
||||||
|
const body = new URLSearchParams();
|
||||||
|
fetch('{{ url_for("tape.test_printer") }}', {method: 'POST', body: body})
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(d => {
|
||||||
|
if (!d.ok) {
|
||||||
|
out.innerHTML = '<span class="bl-tag err">No answer</span> ' +
|
||||||
|
(d.error || 'Unknown error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const dash = '—';
|
||||||
|
const rows = [
|
||||||
|
['Model', d.model || dash],
|
||||||
|
['State', (d.state || dash) + (d.state_message ? ' (' + d.state_message + ')' : '')],
|
||||||
|
['Accepts', (d.formats || []).join(', ') || dash],
|
||||||
|
['Will send as', d.usable_format || dash],
|
||||||
|
['Resolution', (d.resolutions || []).join(', ') || dash]
|
||||||
|
];
|
||||||
|
out.innerHTML = '<span class="bl-tag ok">Connected</span> ' +
|
||||||
|
(d.advice || '') +
|
||||||
|
'<table class="bl-table mt-2"><tbody>' +
|
||||||
|
rows.map(r => '<tr><td style="width:120px;font-weight:600;">' +
|
||||||
|
r[0] + '</td><td>' + r[1] + '</td></tr>').join('') +
|
||||||
|
'</tbody></table>';
|
||||||
|
})
|
||||||
|
.catch(e => { out.innerHTML = '<span class="bl-tag err">Failed</span> ' + e; });
|
||||||
|
});
|
||||||
|
|
||||||
|
// Changing the tape re-renders the preview.
|
||||||
|
['tapeSelect'].forEach(id => {
|
||||||
|
document.getElementById(id)?.addEventListener('change',
|
||||||
|
() => document.getElementById('tapeForm').submit());
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
Reference in new issue
Block a user