标签打印 8 个尺寸公式:小票、价签、吊牌、面单通用

标签打不出来、扫不出来、贴不上去,八成不是代码问题,是尺寸算错了。40×30 的价签和 100×180 的面单,算法其实是同一套,只是参数不同。

项目地址 :gitee.com/theGreatOld...

配图1:一张 40×30 标签的空间账

先看四类标签的实测参数

类型 纸张 条码区 模块宽 二维码 字号
商品价签 40×30 36×10 0.22mm 无 9pt
生鲜称重 60×40 50×12 0.25mm 12mm 10pt
服装吊牌 54×90 30×10 0.25mm 无 10pt
快递面单 100×180 84×20 0.42mm 无 10pt

这张表是几十次实测攒出来的,可以当起点,具体还要按打印机型号微调。

公式 1:可用区 = 纸张 − 死区

复制代码
可用宽 = 纸宽 − 2×死区     (热敏机死区常见 1-2mm)

40mm 纸按 36mm 算,比按 40mm 算安全。

公式 2:Code128 模块数

复制代码
模块数 = 11 × 字符数 + 35

12 位编码 → 167 个模块,36mm 宽下模块宽 0.215mm。

上面这套结构,vue-print-designer 里有一份现成实现,可以直接对照。

公式 3:模块宽度下限

复制代码
模块宽度 ≥ 0.2mm(203dpi)

低于这个值,打印出来边缘粘连,扫码枪识别率断崖下跌。

公式 4:条码总宽校验

复制代码
条码宽 = 模块数 × 模块宽

超过可用宽就缩短编码,别硬压字号------字号不影响条码。

公式 5:二维码最小边长

复制代码
最小边长 = 模块数 × 0.3mm + 8mm(静区)

V6(41×41)算下来约 20mm,40mm 标签放 URL 二维码基本不可能。

公式 6:pt 与 mm 换算

ini 复制代码
1pt = 0.3528mm    10pt ≈ 3.53mm

设计稿给 px 就按 1px = 0.75pt。

公式 7:每页行数

scss 复制代码
每页行数 = floor((纸高 − 上下边距 − 表头 − 表尾) / 行高)

小票按 80mm 纸、行高 4mm 算,一屏约 18 行。

公式 8:打印偏移补偿

scss 复制代码
实际坐标 = 模板坐标 + (dx, dy)   // 按打印机型号存

同一份模板换台机器,偏移方向可能相反,所以补偿必须按型号。

公式怎么落到代码里

公式只是纸面计算,真正落地要写成一个"参数计算器",输入纸张和编码,输出所有尺寸:

javascript 复制代码
function calcLabel({ paperW, paperH, deadZone = 2, code, modulesPerChar = 11 }) {
  const usableW = paperW - deadZone * 2
  const usableH = paperH - deadZone * 2

  // Code128 模块数:11 * 长度 + 35(含起止符与校验位)
  const modules = modulesPerChar * code.length + 35
  const moduleW = usableW / modules

  // 二维码:按内容长度估版本
  const qrSize = code.length <= 20 ? 25 : code.length <= 50 ? 33 : 41
  const qrEdge = qrSize * 0.3 + 8

  return {
    usableW, usableH, moduleW: +moduleW.toFixed(3),
    qrEdge: +qrEdge.toFixed(1),
    ok: moduleW >= 0.2 && qrEdge <= Math.min(usableW, usableH),
  }
}

calcLabel({ paperW: 40, paperH: 30, code: '6901234567892' })
// { usableW: 36, usableH: 26, moduleW: 0.202, qrEdge: 15.5, ok: true }

这个计算器的价值有两个:

  1. 选型时先算:客户说"我要在 40×30 上放 13 位条码 + URL 二维码",先跑一遍就知道放不下,不用做完再返工;
  2. 改参数时实时校验 :改纸张宽度,ok 立刻变 false,提示"条码过密"。

我们把这段逻辑放进了模板编辑器的属性面板------拖拽调整纸张尺寸时,右侧会实时显示模块宽度和二维码边长,超阈值变红。

怎么用这 8 个公式

配图2:生鲜称重标签,公式 1/2/3 全用上了

先算可用区,再定条码长度,最后反推字号。顺序反了会陷入"改字号 → 条码变长 → 放不下"的死循环。

配图3:模板市场里各类标签,本质是参数不同

三个让公式失效的场景

场景一:打印机不是 203dpi。 300dpi 机器上 0.2mm 能吃下,150dpi 的老机器就糊了。公式里的下限是相对 203dpi 的,换机器要重算。

场景二:纸张有预印刷内容。 比如带底纹的价签纸,条码压上去对比度不够,实际可读宽度要打折 10%。

场景三:标签是曲面(瓶身)。 贴在圆柱面上的条码会被拉伸,模块宽度要按 1.2 倍预留,否则扫码枪对不准。

公式给的是起点,这三个场景要求在起点上再加余量。

从公式到模板:一份 40×30 价签的完整配置

json 复制代码
{
  "paper": { "width": 40, "height": 30, "unit": "mm", "margin": 0 },
  "elements": [
    { "type": "text", "x": 2, "y": 1.5, "w": 36, "h": 7,
      "props": { "fontSize": 9, "bold": true, "align": "left" },
      "binding": "productName" },
    { "type": "barcode", "x": 2, "y": 9, "w": 36, "h": 10,
      "props": { "format": "CODE128", "displayValue": false },
      "binding": "sku" },
    { "type": "text", "x": 2, "y": 20, "w": 20, "h": 6,
      "props": { "fontSize": 8, "align": "left" }, "binding": "spec" },
    { "type": "text", "x": 24, "y": 20, "w": 14, "h": 6,
      "props": { "fontSize": 11, "bold": true, "align": "right" }, "binding": "price" }
  ]
}

把这份配置套进渲染器,输出就是一版可以直接打印的价签。改价签只需要改 props.fontSize 或纸张宽度,公式会自动重算条码区。

参数校验放在哪一层

三个位置都能校验,建议放在保存时:

javascript 复制代码
function validateTemplate(tpl) {
  const { width, height } = tpl.paper
  const usable = width - 4          // 假设左右各 2mm 死区

  for (const el of tpl.elements) {
    if (el.type !== 'barcode') continue
    const modules = 11 * (el.binding.length || 12) + 35
    const moduleW = usable / modules
    if (moduleW < 0.2) {
      return { ok: false, msg: `条码过密:模块宽 ${moduleW.toFixed(3)}mm,建议缩短编码或加宽标签` }
    }
  }
  return { ok: true }
}

保存时拦截,比打印出来才发现扫不出要省事得多。

一次真实的返工

生鲜区换价签,13 位商品码,40×30 纸。第一批打出来看着正常,第二天店长说扫不出来。

按公式 2、3 一算:13 位 → 178 个模块 → 36mm 下 0.202mm,正好卡在临界值。把条码区从 36mm 放宽到 40mm(去掉左右各 2mm 的留白),模块宽回到 0.225mm,问题消失。

纸没换、代码只改了一个参数,但如果没有公式,只能靠反复试。

这套参数在 vue-print-designer 的标签模板里都是变量:改纸宽,条码区和字号会自动重算,不用手工验算。

一个经验阈值:模块宽只要低于 0.22mm,就先按"可能扫不出"处理,别等门店反馈再返工。

这条阈值不是拍脑袋定的:203dpi 下 0.22mm 约等于 1.8 个点,低于它就容不下墨点扩散带来的边缘模糊,扫码枪的识别率会从 99% 掉到七八成。

相关推荐
时间的情敌1 小时前
Vue3 + Cesium 实战:本地上传 GLB 模型、模型上叠加 GLB、点击控制透明度
前端·vue.js·cesium
小蒜学长4 小时前
基于SpringBoot+Vue的云南地区扶贫助农系统的设计与实现(代码+数据库+LW)
java·vue.js·spring boot·后端·乡村振兴
雪芽蓝域zzs5 小时前
WebStorm Uniapp Support + Uniapp Tool + Uniappx Language Service讲解
vue.js·uni-app·webstorm
OpenTiny社区5 小时前
AI 生成的 Vue 代码,终于能直接用了!
前端·vue.js·github
tryCbest6 小时前
Vue3——第 12 篇:组合式 API(下)——Composables 与逻辑3
vue.js
LuHang21 小时前
从天地图切换到高德地图:一次真实的底图迁移实战
前端·vue.js·cesium
ruanno_top1 天前
Vue 3 后台权限路由:我漏掉了按钮级裁剪,还把 403 写成了 404
vue.js
栉甜1 天前
Vue2学习内容(上)
前端·javascript·vue.js·学习
小郎君6581 天前
(六)el-cascader 的展示值为什么不能直接 join:emitPath、路径还原与多选去重
vue.js·算法