b4876c4efd
Co-authored-by: Cursor <cursoragent@cursor.com>
178 lines
8.9 KiB
HTML
178 lines
8.9 KiB
HTML
{% load i18n static %}
|
||
<!DOCTYPE html>
|
||
<html lang="ru">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<title>{% trans "Шаблон этикетки" %} — ACORE</title>
|
||
<meta name="csrf-token" content="{{ csrf_token }}">
|
||
<style>
|
||
* { box-sizing: border-box; }
|
||
body {
|
||
margin: 0;
|
||
font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
|
||
background: #f4f4f5;
|
||
color: #111;
|
||
}
|
||
.topbar {
|
||
display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap;
|
||
padding: 0.75rem 1rem; background: #fff; border-bottom: 1px solid #ddd;
|
||
}
|
||
.topbar h1 { font-size: 1rem; font-weight: 600; margin: 0; margin-right: auto; }
|
||
.topbar button, .topbar a {
|
||
font-size: 0.8125rem; padding: 0.4rem 0.85rem;
|
||
border-radius: 0.375rem; border: 1px solid #ccc;
|
||
background: #fff; cursor: pointer; text-decoration: none; color: #111;
|
||
}
|
||
.topbar .primary { background: #2563eb; color: #fff; border-color: #2563eb; }
|
||
.topbar .muted { color: #666; font-size: 0.75rem; }
|
||
.layout {
|
||
display: grid;
|
||
grid-template-columns: 12rem 1fr 16rem;
|
||
gap: 1rem;
|
||
padding: 1rem;
|
||
min-height: calc(100vh - 3.5rem);
|
||
}
|
||
.panel {
|
||
background: #fff; border: 1px solid #ddd; border-radius: 0.5rem; padding: 0.75rem;
|
||
}
|
||
.panel h2 { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.04em; color: #666; margin: 0 0 0.5rem; }
|
||
.palette button {
|
||
display: block; width: 100%; text-align: left;
|
||
margin-bottom: 0.35rem; padding: 0.4rem 0.55rem;
|
||
border: 1px solid #ddd; border-radius: 0.35rem; background: #fafafa; cursor: pointer; font-size: 0.8125rem;
|
||
}
|
||
.palette button:hover { background: #eef2ff; border-color: #93c5fd; }
|
||
.canvas-wrap {
|
||
display: flex; align-items: flex-start; justify-content: center;
|
||
padding: 1.5rem; overflow: auto; background: #e5e7eb; border-radius: 0.5rem; border: 1px solid #ddd;
|
||
min-height: 28rem;
|
||
}
|
||
#label-canvas {
|
||
position: relative;
|
||
background: #fff;
|
||
border: 1px dashed #94a3b8;
|
||
box-shadow: 0 8px 24px rgba(0,0,0,0.08);
|
||
touch-action: none;
|
||
}
|
||
.lbl-block {
|
||
position: absolute;
|
||
border: 1px solid #93c5fd;
|
||
background: rgba(219, 234, 254, 0.55);
|
||
overflow: hidden;
|
||
cursor: grab;
|
||
user-select: none;
|
||
font-size: 10px;
|
||
line-height: 1.15;
|
||
padding: 1px 2px;
|
||
}
|
||
.lbl-block.selected { border-color: #2563eb; box-shadow: 0 0 0 2px rgba(37,99,235,0.25); z-index: 5; }
|
||
.lbl-block .kind {
|
||
font-size: 9px; color: #1e40af; font-weight: 600; text-transform: uppercase;
|
||
}
|
||
.lbl-block .resize {
|
||
position: absolute; right: 0; bottom: 0; width: 10px; height: 10px;
|
||
background: #2563eb; cursor: nwse-resize;
|
||
}
|
||
.props label { display: block; font-size: 0.75rem; color: #555; margin: 0.5rem 0 0.2rem; }
|
||
.props input, .props select {
|
||
width: 100%; padding: 0.35rem 0.45rem; border: 1px solid #ccc; border-radius: 0.3rem; font-size: 0.8125rem;
|
||
}
|
||
.props .row { display: grid; grid-template-columns: 1fr 1fr; gap: 0.4rem; }
|
||
.props .check { display: flex; align-items: center; gap: 0.4rem; margin-top: 0.6rem; font-size: 0.8125rem; }
|
||
.props .danger { margin-top: 0.75rem; width: 100%; color: #b91c1c; border-color: #fca5a5; }
|
||
#status { font-size: 0.75rem; color: #16a34a; min-height: 1rem; }
|
||
#status.err { color: #b91c1c; }
|
||
@media (max-width: 960px) {
|
||
.layout { grid-template-columns: 1fr; }
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
{% csrf_token %}
|
||
<div class="topbar">
|
||
<h1>{% trans "Шаблон этикетки оборудования" %}</h1>
|
||
<span class="muted" id="status"></span>
|
||
<a href="/it-manager/?tab=equipment">{% trans "К оборудованию" %}</a>
|
||
<button type="button" class="primary" id="btn-save">{% trans "Сохранить" %}</button>
|
||
</div>
|
||
<div class="layout">
|
||
<div class="panel palette">
|
||
<h2>{% trans "Блоки" %}</h2>
|
||
<button type="button" data-add="qr">QR</button>
|
||
<button type="button" data-add="ean13">EAN-13</button>
|
||
<button type="button" data-add="model">{% trans "Модель" %}</button>
|
||
<button type="button" data-add="type">{% trans "Тип" %}</button>
|
||
<button type="button" data-add="inventory">{% trans "Инв. №" %}</button>
|
||
<button type="button" data-add="serial">{% trans "S/N" %}</button>
|
||
<button type="button" data-add="custom">{% trans "Свой текст" %}</button>
|
||
<h2 style="margin-top:1rem;">{% trans "Размер этикетки" %}</h2>
|
||
<label style="font-size:0.75rem; color:#555;">{% trans "Ширина, мм" %}</label>
|
||
<input type="number" id="tmpl-width" min="20" max="200" step="0.5" value="{{ template.width_mm }}">
|
||
<label style="font-size:0.75rem; color:#555; margin-top:0.4rem; display:block;">{% trans "Высота, мм" %}</label>
|
||
<input type="number" id="tmpl-height" min="15" max="200" step="0.5" value="{{ template.height_mm }}">
|
||
<label style="font-size:0.75rem; color:#555; margin-top:0.4rem; display:block;">{% trans "Масштаб, px/мм" %}</label>
|
||
<input type="number" id="tmpl-scale" min="2" max="12" step="0.5" value="4">
|
||
</div>
|
||
<div class="canvas-wrap">
|
||
<div id="label-canvas"></div>
|
||
</div>
|
||
<div class="panel props" id="props-panel">
|
||
<h2>{% trans "Свойства" %}</h2>
|
||
<div id="props-empty" style="font-size:0.8125rem; color:#888;">{% trans "Выберите блок на макете" %}</div>
|
||
<div id="props-fields" style="display:none;">
|
||
<div style="font-size:0.8125rem; font-weight:600;" id="prop-type-label"></div>
|
||
<div class="row">
|
||
<div>
|
||
<label>X мм</label>
|
||
<input type="number" id="prop-x" step="0.5">
|
||
</div>
|
||
<div>
|
||
<label>Y мм</label>
|
||
<input type="number" id="prop-y" step="0.5">
|
||
</div>
|
||
</div>
|
||
<div class="row">
|
||
<div>
|
||
<label>W мм</label>
|
||
<input type="number" id="prop-w" step="0.5" min="2">
|
||
</div>
|
||
<div>
|
||
<label>H мм</label>
|
||
<input type="number" id="prop-h" step="0.5" min="2">
|
||
</div>
|
||
</div>
|
||
<label>{% trans "Шрифт, pt" %}</label>
|
||
<input type="number" id="prop-font" step="0.5" min="6" max="24">
|
||
<label>{% trans "Выравнивание" %}</label>
|
||
<select id="prop-align">
|
||
<option value="left">{% trans "Слева" %}</option>
|
||
<option value="center">{% trans "По центру" %}</option>
|
||
<option value="right">{% trans "Справа" %}</option>
|
||
</select>
|
||
<label class="check"><input type="checkbox" id="prop-bold"> {% trans "Жирный" %}</label>
|
||
<div id="prop-custom-wrap" style="display:none;">
|
||
<label id="prop-text-label">{% trans "Текст" %}</label>
|
||
<textarea id="prop-text" maxlength="500" rows="3"
|
||
style="width:100%; padding:0.35rem 0.45rem; border:1px solid #ccc; border-radius:0.3rem; font-size:0.8125rem; resize:vertical;"></textarea>
|
||
<div id="prop-qr-hints" style="display:none; font-size:0.7rem; color:#666; margin-top:0.35rem; line-height:1.35;">
|
||
{% trans "Плейсхолдеры:" %}
|
||
<code>{inventory}</code>
|
||
<code>{serial}</code>
|
||
<code>{model}</code>
|
||
<code>{type}</code>
|
||
<code>{id}</code>
|
||
<code>{eq}</code>
|
||
<code>{ean13}</code>.
|
||
{% trans "Пусто = инв. № или EQ{id}." %}
|
||
{% trans "Можно свой текст и несколько строк." %}
|
||
</div>
|
||
</div>
|
||
<button type="button" class="danger" id="prop-delete">{% trans "Удалить блок" %}</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{{ template_data|json_script:"label-template-data" }}
|
||
<script src="{% static 'assets/js/label-template-editor.js' %}"></script>
|
||
</body>
|
||
</html>
|