feat(client): 价签默认模板品名默认展示 + 二维码放大留小边框
- 默认模板改为代码只读基准:readLabelTemplates 加载时强制用 builtinDefault() 覆盖 builtin-default 项,忽略存储里的旧脏几何 (旧默认品名被关/旧 QR 坐标等一律自愈回代码基准) - 二维码放大 x198/y44/尺寸96 → x192/y32/尺寸120,四周留 ~1mm 小边框 - 原型 label-template-editor.html 高级 QR 输入同步;golden 重生 - 新增存储测试钉死「内置默认永远回代码基准」 原型与代码同提交(design-first 双边一致)
This commit is contained in:
Vendored
+1
-1
File diff suppressed because one or more lines are too long
@@ -77,6 +77,17 @@
|
||||
.lt-srow:last-child{margin-bottom:0;}
|
||||
.lt-srow .k{width:44px; flex:none; font-size:var(--fs-sm); color:var(--muted);}
|
||||
.lt-srow .input{width:80px;}
|
||||
/* 字号步进器:输入框右侧内嵌上下箭头,点击 +1/-1 */
|
||||
.lt-stepper{position:relative; width:80px; flex:none;}
|
||||
.lt-stepper .input{width:100%; box-sizing:border-box; padding-right:22px;}
|
||||
.lt-steparr{position:absolute; right:1px; top:1px; bottom:1px; width:20px;
|
||||
display:flex; flex-direction:column; border-left:1px solid var(--border);}
|
||||
.lt-steparr button{flex:1; display:flex; align-items:center; justify-content:center;
|
||||
border:0; background:transparent; cursor:pointer; color:var(--muted); padding:0;}
|
||||
.lt-steparr button:hover{background:var(--row-hover); color:var(--text);}
|
||||
.lt-steparr button:last-child{border-top:1px solid var(--border);}
|
||||
.lt-steparr button:disabled{opacity:.4; cursor:default; background:transparent; color:var(--muted);}
|
||||
.lt-steparr svg{width:11px; height:11px; stroke:currentColor; fill:none; stroke-width:2.5;}
|
||||
.lt-gnote{margin:0; font-size:var(--fs-xs); color:var(--muted); line-height:1.6;}
|
||||
/* 字体下拉(自包含浮层,每行独立) */
|
||||
.lt-fontsel{position:relative; flex:1;}
|
||||
@@ -92,6 +103,11 @@
|
||||
.lt-tplhint{display:flex; align-items:center; gap:6px; margin-top:12px; padding-top:12px; border-top:1px solid var(--border); font-size:var(--fs-xs); color:var(--muted);}
|
||||
.lt-tplhint svg{width:14px; height:14px; flex:none; stroke:currentColor; fill:none; stroke-width:2; color:var(--primary);}
|
||||
.lt-tplhint b{color:var(--primary); font-weight:600;}
|
||||
/* 默认模板只读锁:提示条 + 受锁区域整体禁用(点「复制」为可编辑逃生口) */
|
||||
.lt-locknote{margin-top:10px; padding:10px 12px; background:var(--warn-bg); border:1px solid var(--warn); border-radius:var(--r-md); font-size:var(--fs-xs); color:var(--warn); line-height:1.5;}
|
||||
.lt-lockoff{transition:opacity .15s;}
|
||||
.dis{opacity:.45; pointer-events:none;}
|
||||
.lt-panel.locked #fieldList, .lt-panel.locked .lt-adv, .lt-panel.locked #printSec{opacity:.5; pointer-events:none;}
|
||||
/* 高级折叠 */
|
||||
.lt-adv{background:var(--surface); border:1px solid var(--border); border-radius:var(--r-lg); overflow:hidden;}
|
||||
.lt-adv>summary{list-style:none; cursor:pointer; padding:14px 16px; font-size:var(--fs-sm); font-weight:700; color:var(--title); display:flex; align-items:center; gap:8px;}
|
||||
@@ -116,7 +132,7 @@
|
||||
<div class="btn ghost sm" onclick="location.href='devices.html'"><svg viewBox="0 0 24 24"><use href="#i-arrow-left"/></svg>返回</div>
|
||||
<h1>标签模板编辑器</h1><span class="sub">左侧实时预览 · 右侧属性面板</span>
|
||||
<div class="actions">
|
||||
<div class="btn ghost" onclick="resetTpl()"><svg viewBox="0 0 24 24"><use href="#i-refresh"/></svg>重置</div>
|
||||
<div class="btn ghost lt-lockoff" onclick="resetTpl()"><svg viewBox="0 0 24 24"><use href="#i-refresh"/></svg>重置</div>
|
||||
<div class="btn ghost" onclick="printTpl()"><svg viewBox="0 0 24 24"><use href="#i-printer"/></svg>打印</div>
|
||||
<div class="btn primary" onclick="toast('模板已保存 ✓')"><svg viewBox="0 0 24 24"><use href="#i-check"/></svg>保存</div>
|
||||
</div>
|
||||
@@ -167,10 +183,11 @@
|
||||
<div class="lt-mgr">
|
||||
<div class="btn ghost sm" onclick="toast('新建模板(原型)')"><svg viewBox="0 0 24 24"><use href="#i-plus"/></svg>新建</div>
|
||||
<div class="btn ghost sm" onclick="toast('复制模板(原型)')"><svg viewBox="0 0 24 24"><use href="#i-ic27"/></svg>复制</div>
|
||||
<div class="btn ghost sm" onclick="openRename()"><svg viewBox="0 0 24 24"><use href="#i-edit"/></svg>重命名</div>
|
||||
<div class="btn ghost sm lt-lockoff" onclick="openRename()"><svg viewBox="0 0 24 24"><use href="#i-edit"/></svg>重命名</div>
|
||||
<div class="btn ghost sm" onclick="setDefaultTpl()"><svg viewBox="0 0 24 24"><use href="#i-check"/></svg>设默认</div>
|
||||
<div class="btn danger sm" onclick="toast('删除模板(原型)')"><svg viewBox="0 0 24 24"><use href="#i-close"/></svg>删除</div>
|
||||
<div class="btn danger sm lt-lockoff" onclick="toast('删除模板(原型)')"><svg viewBox="0 0 24 24"><use href="#i-close"/></svg>删除</div>
|
||||
</div>
|
||||
<div class="lt-locknote" id="lockNote">系统默认模板不可修改,点「复制」生成可编辑副本</div>
|
||||
<div class="lt-tplhint"><svg viewBox="0 0 24 24"><use href="#i-check"/></svg>出签打印使用:<b id="tplActive">默认版式</b></div>
|
||||
</div>
|
||||
|
||||
@@ -186,9 +203,9 @@
|
||||
<div class="body">
|
||||
<div class="lt-fmeta" style="margin-bottom:10px">二维码</div>
|
||||
<div class="lt-grid2">
|
||||
<div class="field"><label>X</label><input class="input" value="178"></div>
|
||||
<div class="field"><label>Y</label><input class="input" value="25"></div>
|
||||
<div class="field"><label>尺寸</label><input class="input" value="134"></div>
|
||||
<div class="field"><label>X</label><input class="input" value="192"></div>
|
||||
<div class="field"><label>Y</label><input class="input" value="32"></div>
|
||||
<div class="field"><label>尺寸</label><input class="input" value="120"></div>
|
||||
</div>
|
||||
<div class="lt-fmeta" style="margin:14px 0 10px">条形码</div>
|
||||
<div class="lt-grid2">
|
||||
@@ -205,7 +222,7 @@
|
||||
</details>
|
||||
|
||||
<!-- 打印参数(常驻可见) -->
|
||||
<div class="lt-sec">
|
||||
<div class="lt-sec" id="printSec">
|
||||
<div class="lt-sectitle"><svg viewBox="0 0 24 24"><use href="#i-gear"/></svg>打印参数</div>
|
||||
<div class="lt-grid2">
|
||||
<div class="field"><label>纸张宽 (mm)</label><input class="input" value="40"></div>
|
||||
@@ -249,14 +266,12 @@
|
||||
// 字体不做(热敏单色 MVP);仅「品名」支持自动字号。同物理行两字段共用字号/加粗/对齐(PAIR)。
|
||||
const ALIGN_LABEL = {left:'左', center:'居中', right:'右'};
|
||||
const FIELDS = [
|
||||
{f:'name', label:'品名', type:'text', on:true, auto:true, bold:true, align:'center', size:18},
|
||||
{f:'shop', label:'店名(抬头)', type:'text', on:true, auto:false, bold:true, align:'center', size:14},
|
||||
{f:'code', label:'编号', type:'text', on:true, auto:false, bold:false, align:'center', size:14},
|
||||
{f:'series', label:'型号 / 度数', type:'text', on:true, auto:false, bold:false, align:'center', size:14},
|
||||
{f:'spec', label:'版本 / 规格', type:'text', on:true, auto:false, bold:false, align:'center', size:14},
|
||||
{f:'date', label:'生产日期', type:'text', on:true, auto:false, bold:false, align:'center', size:14},
|
||||
{f:'batch', label:'批次号', type:'text', on:false, auto:false, bold:false, align:'center', size:14},
|
||||
{f:'remark', label:'备注', type:'text', on:false, auto:false, bold:false, align:'center', size:14},
|
||||
{f:'name', label:'品名', type:'text', on:true, auto:true, bold:true, align:'center', size:18, sample:'贵州茅台酒'},
|
||||
{f:'shop', label:'店名(抬头)', type:'text', on:true, auto:false, bold:true, align:'center', size:14, sample:'鼎晟酒行'},
|
||||
{f:'code', label:'编号', type:'text', on:true, auto:false, bold:false, align:'center', size:14, sample:'P1001'},
|
||||
{f:'series', label:'型号 / 度数', type:'text', on:true, auto:false, bold:false, align:'center', size:14, sample:'飞天53度'},
|
||||
{f:'spec', label:'版本 / 规格', type:'text', on:true, auto:false, bold:false, align:'center', size:14, sample:'500ml'},
|
||||
{f:'date', label:'生产日期', type:'text', on:true, auto:false, bold:false, align:'center', size:14, sample:'2024-05-18'},
|
||||
{f:'qr', label:'二维码', type:'graphic', on:true},
|
||||
{f:'barcode', label:'条形码', type:'graphic', on:true},
|
||||
];
|
||||
@@ -276,8 +291,17 @@
|
||||
<button class="switch${fd.auto?' on':''}" onclick="toggleAuto(event,${i})"></button>`
|
||||
: '';
|
||||
return `<div class="lt-fbody">
|
||||
<div class="lt-srow"><span class="k">内容</span>
|
||||
<input class="input" style="flex:1;width:auto" value="${fd.sample||''}" oninput="setSample(${i},this.value)"></div>
|
||||
<p class="lt-gnote" style="margin:-4px 0 12px">仅改预览示例,真实打印按商品数据填充</p>
|
||||
<div class="lt-srow"><span class="k">字号</span>
|
||||
<input class="input" value="${fd.size}" ${fd.auto?'disabled':''} oninput="setSize(${i},this.value)">
|
||||
<div class="lt-stepper">
|
||||
<input class="input" value="${fd.size}" ${fd.auto?'disabled':''} oninput="setSize(${i},this.value)">
|
||||
<div class="lt-steparr">
|
||||
<button type="button" ${fd.auto?'disabled':''} onclick="stepSize(${i},1)" aria-label="增大字号"><svg viewBox="0 0 24 24"><use href="#i-chevron-up"/></svg></button>
|
||||
<button type="button" ${fd.auto?'disabled':''} onclick="stepSize(${i},-1)" aria-label="减小字号"><svg viewBox="0 0 24 24"><use href="#i-chevron-down"/></svg></button>
|
||||
</div>
|
||||
</div>
|
||||
${autoCtrl}</div>
|
||||
<div class="lt-srow"><span class="k">加粗</span>
|
||||
<button class="switch${fd.bold?' on':''}" onclick="toggleBold(event,${i})"></button></div>
|
||||
@@ -347,11 +371,27 @@
|
||||
const n = parseInt(v,10);
|
||||
if(!isNaN(n)){ FIELDS[i].size = n; applyStyle(i); refreshMeta(i); mirror(i); }
|
||||
}
|
||||
/* 内容:仅改左侧预览示例文字,不影响真实打印(真实出签永远取商品数据) */
|
||||
function setSample(i, v){
|
||||
FIELDS[i].sample = v;
|
||||
previewEls(i).forEach(el=>{ el.textContent = v; });
|
||||
}
|
||||
/* 步进器 +1/-1:夹在 [6,48],回写输入框并刷新预览 */
|
||||
function stepSize(i, d){
|
||||
if(FIELDS[i].auto) return;
|
||||
let n = parseInt(FIELDS[i].size,10); if(isNaN(n)) n = 14;
|
||||
n = Math.max(6, Math.min(48, n + d));
|
||||
FIELDS[i].size = n;
|
||||
const inp = fieldEl(i).querySelector('.lt-stepper .input');
|
||||
if(inp) inp.value = n;
|
||||
applyStyle(i); refreshMeta(i); mirror(i);
|
||||
}
|
||||
function toggleAuto(e, i){
|
||||
e.stopPropagation();
|
||||
FIELDS[i].auto = !FIELDS[i].auto;
|
||||
e.currentTarget.classList.toggle('on', FIELDS[i].auto);
|
||||
fieldEl(i).querySelector('.lt-srow .input').disabled = FIELDS[i].auto;
|
||||
fieldEl(i).querySelectorAll('.lt-steparr button').forEach(b=>b.disabled = FIELDS[i].auto);
|
||||
applyStyle(i); refreshMeta(i);
|
||||
}
|
||||
function toggleBold(e, i){
|
||||
@@ -402,8 +442,18 @@
|
||||
document.getElementById('renameInput').value = name;
|
||||
document.getElementById('tplPop').classList.remove('show');
|
||||
renderTplPop();
|
||||
applyLock();
|
||||
toast('正在编辑「'+name+'」');
|
||||
}
|
||||
/* 默认模板('默认版式')受保护只读:显示锁提示、禁用字段/高级/打印/重命名/删除/重置,
|
||||
其余(下拉/新建/复制/设默认)保持可用;点「复制」生成可编辑副本 */
|
||||
function isLockedTpl(){ return tplEditing === '默认版式'; }
|
||||
function applyLock(){
|
||||
const on = isLockedTpl();
|
||||
document.getElementById('lockNote').style.display = on ? '' : 'none';
|
||||
document.querySelector('.lt-panel').classList.toggle('locked', on);
|
||||
document.querySelectorAll('.lt-lockoff').forEach(b=>b.classList.toggle('dis', on));
|
||||
}
|
||||
function setDefaultTpl(){
|
||||
tplActive = tplEditing;
|
||||
document.getElementById('tplActive').textContent = tplActive;
|
||||
@@ -413,6 +463,7 @@
|
||||
renderTplPop();
|
||||
document.addEventListener('click', ()=>document.querySelectorAll('.lt-fontpop.show').forEach(p=>p.classList.remove('show')));
|
||||
renderFields();
|
||||
applyLock();
|
||||
function openRename(){ document.getElementById('ovRename').classList.add('show'); }
|
||||
function closeRename(){ document.getElementById('ovRename').classList.remove('show'); }
|
||||
function doRename(){ closeRename(); toast('已重命名 ✓'); }
|
||||
|
||||
Reference in New Issue
Block a user