18 KiB
Label Designer
标签批量设计与排版工具:可视化编辑标签模板、导入 Excel 数据、预览整页排版并导出 PDF。
模板以 JSON 保存,导出文件扩展名为 .lad(Label Application Data)。
快速开始
npm install
npm run dev # 开发服务器,默认 http://localhost:3000
npm run build # 生产构建
npm run preview # 预览构建结果
模板文件概览
一个模板描述单张标签的外观与排版参数。批量数据(Excel 行)在导出阶段关联,不写入模板 JSON。
最小合法结构
导入外部模板时,JSON 至少包含:
| 字段 | 类型 | 说明 |
|---|---|---|
width |
number | 标签宽度 (mm) |
height |
number | 标签高度 (mm) |
elements |
array | 元素列表 |
完整根对象 LabelTemplate
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
id |
string | 否 | 模板唯一 ID,应用内自动生成 |
name |
string | 否 | 模板显示名称 |
width |
number | 是 | 标签画布宽度 (mm) |
height |
number | 是 | 标签画布高度 (mm) |
elements |
TemplateElement[] |
是 | 标签上的所有元素 |
variableList |
string[] | 否 | 声明的变量名列表(统一管理) |
variableDefaults |
Record<string, string> | 否 | 设计预览用默认值,变量名 → 文本 |
paper |
PaperConfig |
否 | 纸张排版;缺省时使用 A4 默认配置 |
cutLine |
CutLineConfig |
否 | PDF 裁切参考线;缺省时启用灰色虚线 |
坐标与单位
- 所有尺寸、位置、边距、间距:毫米 (mm)
- 字号:磅 (pt)
- 元素坐标
(x, y)为相对标签画布左上角的距离 - 旋转
rotation:顺时针角度(度),默认0
变量与占位符
语法
在元素的 content 中使用 {变量名} 引用数据。导出时由 Excel 列映射或默认值填充。
表格元素的变量写在各单元格的 tableCells["行,列"].content 中(见下文 表格 一节),同样参与变量汇总与数据映射。
| 占位符 | 说明 |
|---|---|
{变量名} |
普通变量,如 {货位编号}、{物料名称} |
{#INDEX} |
内置占位符:当前数据行在导入数据中的 1-based 序号(第 1 行 → 1) |
变量名规则:
- 写在花括号内,不含
} #INDEXreserved,不要作为自定义变量名- 可在
variableList中预先声明;也会从content、表格单元格 content 与显示条件中自动汇总
导出时的变量映射(非模板字段)
导出 Excel 时,每个变量需关联数据源,映射关系保存在浏览器本地,不在模板 JSON 内:
| 映射值 | 含义 |
|---|---|
| Excel 列名 | 取该列单元格值 |
__use_default__ |
使用模板 variableDefaults 中的默认值 |
__use_row_index__ |
使用数据序号(与 {#INDEX} 相同,1-based) |
列名与变量名相同时会自动匹配。
元素 TemplateElement
每个元素是标签上的一个图层,按数组顺序从下到上绘制。
通用字段
| 字段 | 类型 | 说明 |
|---|---|---|
id |
string | 元素唯一 ID |
type |
'text' | 'barcode' | 'qrcode' | 'image' | 'table' |
元素类型 |
name |
string | 图层名称(设计器显示用) |
x, y |
number | 位置 (mm) |
width, height |
number | 尺寸 (mm) |
rotation |
number | 旋转角度,默认 0 |
content |
string | 静态文本或 {变量} 组合 |
fontFamily |
'sans' | 'serif' | 'mono' |
字体族 |
backgroundColor |
string | 背景色,如 #ffffff、transparent |
backgroundOpacity |
number | 背景不透明度 0–100 |
padding |
number | 内边距 (mm),四边统一 |
paddingTop/Right/Bottom/Left |
number | 分边内边距,覆盖 padding |
borderWidth |
number | 边框线宽 (mm),0 为无边框 |
borderColor |
string | 边框颜色 |
borderWidthTop/Right/Bottom/Left |
number | 分边边框宽度 |
borderColorTop/Right/Bottom/Left |
string | 分边边框颜色 |
borderRadius |
number | 圆角半径 (mm) |
borderRadiusTL/TR/BR/BL |
number | 分角圆角 |
borderRadiusCorners |
object | 指定哪些角圆角,如 { "tl": true, "tr": true } |
locked |
boolean | 锁定后设计器不可拖动 |
值提取(text / barcode / qrcode / image / 表格单元格 均有效)
对 变量替换完成后的完整 content 字符串 做提取(先替换 {变量},再提取)。
| 字段 | 类型 | 说明 |
|---|---|---|
textExtractMode |
'none' | 'split' | 'prefix' | 'suffix' |
提取方式;缺省且存在 textSplitDelimiter 时视为 split |
textSplitDelimiter |
string | 分隔符,如 - |
textSplitIndex |
number | 按分隔符拆分后取第 N 段(从 1 开始),段数不足则为空 |
textExtractLength |
number | 前 N / 后 N 位时的位数 |
提取前会对完整内容 trim() 去除首尾空格。
示例:content: "ABC-{货位编号}",货位编号 = A-1-2-3
→ 替换后 ABC-A-1-2-3 → 分隔符 -、第 3 段 → 显示 1
显示前后缀(仅 type: "text")
在值提取 之后 追加,仅影响显示,不参与「内容值」显示条件判断。
| 字段 | 类型 | 说明 |
|---|---|---|
textDisplayPrefix |
string | 前缀字符,如 ¥ |
textDisplaySuffix |
string | 后缀字符,如 元 |
显示控制
| 字段 | 类型 | 说明 |
|---|---|---|
visibilityMode |
'always' | 'conditional' |
始终显示 / 按条件显示 |
visibilityRules |
VisibilityRule[] |
条件列表,全部满足才显示 |
VisibilityRule
| 字段 | 类型 | 说明 |
|---|---|---|
target |
'variable' | 'content' |
条件对象:变量值 或 本元素内容值(提取后、不含前后缀) |
variable |
string | target=variable 时的变量名 |
operator |
见下表 | 比较运算符 |
value |
string | gt / lt / eq / neq 时的比较目标 |
| operator | 含义 |
|---|---|
empty |
为空 |
not_empty |
不为空 |
gt |
大于(数值优先比较,否则字符串) |
lt |
小于 |
eq |
等于 |
neq |
不等于 |
文本元素 type: "text"
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
fontSize |
number | — | 字号 (pt) |
textAlign |
'left' | 'center' | 'right' |
— | 水平对齐 |
verticalAlign |
'top' | 'middle' | 'bottom' |
middle |
垂直对齐 |
wordWrap |
boolean | true |
自动换行 |
textWritingMode |
'horizontal' | 'vertical' |
horizontal |
排列方向 |
fontWeight |
'normal' | 'bold' |
— | 粗体 |
fontStyle |
'normal' | 'italic' |
normal |
斜体 |
textUnderline |
boolean | — | 下划线 |
textLineThrough |
boolean | — | 删除线 |
textScaleX, textScaleY |
number | 1 |
水平/垂直拉伸 |
letterSpacing |
number | — | 字间距 (mm) |
textColor |
string | — | 文字颜色 |
textFormat |
'text' | 'number' | 'percent' | 'currency' |
text |
显示格式 |
decimalPlaces |
number | 2 |
数值/百分比/货币的小数位 |
numberPartDisplay |
'full' | 'integer' | 'fraction' |
full |
数值拆分显示(如价格拆成整数与小数两部分) |
数值格式作用于每个 {变量} 替换值;提取与前后缀见上文顺序。
条形码 type: "barcode"
| 字段 | 类型 | 说明 |
|---|---|---|
barcodeFormat |
'CODE128' | 'CODE39' | 'EAN13' | 'ITF' |
码制 |
showText |
boolean | 是否在条码下方显示可读文本 |
content |
string | 条码数据,通常为 {变量} |
输出模式下内容为空则不渲染该元素。
二维码 type: "qrcode"
| 字段 | 类型 | 说明 |
|---|---|---|
content |
string | 二维码内容(URL、文本等) |
二维码元素宽高相等(设计器约束)。内容为空时不渲染。
图片 type: "image"
| 字段 | 类型 | 说明 |
|---|---|---|
content |
string | 图片 URL、data: URI,或 {变量} |
imageFit |
'contain' | 'cover' | 'fill' |
缩放方式,默认 contain |
表格 type: "table"
表格由行数、列数及各行高、各列宽定义网格;单元格可单独设置内容与文本样式,支持合并。
元素级 content 对表格通常留空;单元格内容默认为空,在 tableCells 中按坐标单独设置。
表格结构字段
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
tableRows |
number | 3 |
行数 (1–50) |
tableCols |
number | 3 |
列数 (1–50) |
tableRowHeights |
number[] | — | 各行高度 (mm),长度须等于 tableRows |
tableColWidths |
number[] | — | 各列宽度 (mm),长度须等于 tableCols |
tableBorderWidth |
number | 0.2 |
单元格网格线宽 (mm);0 时不绘制网格线 |
tableBorderColor |
string | #374151 |
网格线颜色 |
tableCells |
Record<string, TableCellData> | — | 单元格数据,键为 "行,列"(从 0 开始) |
尺寸关系
- 内容区(网格区域)= 元素宽高减去
padding sum(tableRowHeights)= 内容区高度sum(tableColWidths)= 内容区宽度- 增大内边距时,元素宽高会自动增大以保持行高/列宽不变
拖拽缩放表格四角时,各行高、各列宽会 等比缩放。
TableCellData(tableCells 的值)
| 字段 | 类型 | 说明 |
|---|---|---|
content |
string | 单元格文本或 {变量};缺省为空 |
mergeAnchor |
{ row, col } |
被合并单元格指向主单元格坐标 |
rowSpan |
number | 主单元格合并行数,默认 1 |
colSpan |
number | 主单元格合并列数,默认 1 |
fontSize |
number | 字号 (pt),缺省继承元素级 |
fontFamily |
'sans' | 'serif' | 'mono' |
字体 |
textAlign |
'left' | 'center' | 'right' |
水平对齐 |
verticalAlign |
'top' | 'middle' | 'bottom' |
垂直对齐 |
fontWeight |
'normal' | 'bold' |
粗体 |
fontStyle |
'normal' | 'italic' |
斜体 |
textUnderline |
boolean | 下划线 |
textLineThrough |
boolean | 删除线 |
textScaleX, textScaleY |
number | 水平/垂直拉伸 |
letterSpacing |
number | 字间距 (mm) |
textWritingMode |
'horizontal' | 'vertical' |
排列方向 |
textColor |
string | 文字颜色 |
backgroundColor |
string | 单元格背景色 |
wordWrap |
boolean | 自动换行 |
textFormat |
同文本元素 | 显示格式 |
decimalPlaces |
number | 小数位数 |
numberPartDisplay |
同文本元素 | 数值拆分显示 |
textExtractMode 等 |
同值提取字段 | 对该单元格 content 生效 |
textDisplayPrefix / textDisplaySuffix |
string | 显示前后缀 |
未在 tableCells 中声明的坐标视为空白单元格,使用元素级默认字体/对齐等绘制属性。
合并单元格
- 主单元格(左上角)存
rowSpan/colSpan - 被合并的从属单元格存
mergeAnchor: { row, col }指向主单元格 - 合并区域内仅主单元格参与内容与样式;渲染时内部网格线自动省略
表格示例片段
{
"id": "el_table",
"type": "table",
"name": "明细表",
"x": 2,
"y": 2,
"width": 66,
"height": 20,
"content": "",
"fontSize": 9,
"textAlign": "center",
"verticalAlign": "middle",
"fontWeight": "normal",
"fontFamily": "sans",
"barcodeFormat": "CODE128",
"showText": false,
"tableRows": 2,
"tableCols": 3,
"tableRowHeights": [6, 6],
"tableColWidths": [22, 22, 22],
"tableBorderWidth": 0.2,
"tableBorderColor": "#374151",
"tableCells": {
"0,0": { "content": "物料", "fontWeight": "bold" },
"0,1": { "content": "数量", "fontWeight": "bold" },
"0,2": { "content": "货位", "fontWeight": "bold" },
"1,0": { "content": "{物料名称}" },
"1,1": { "content": "{数量}" },
"1,2": { "content": "{货位编号}" }
}
}
合并 2×2 区域示例(主单元格 "0,0",rowSpan: 2,colSpan: 2):
"tableCells": {
"0,0": { "content": "标题", "rowSpan": 2, "colSpan": 2 },
"0,1": { "mergeAnchor": { "row": 0, "col": 0 } },
"1,0": { "mergeAnchor": { "row": 0, "col": 0 } },
"1,1": { "mergeAnchor": { "row": 0, "col": 0 } }
}
纸张排版 PaperConfig
控制标签如何排列到 PDF 页面上(每模板独立保存)。
| 字段 | 类型 | 说明 |
|---|---|---|
type |
'A4' | 'A5' | 'A6' | 'custom' |
纸张规格 |
width, height |
number | 纸张尺寸 (mm);预设规格可省略自定义值 |
orientation |
'portrait' | 'landscape' |
纵向 / 横向 |
marginTop/Right/Bottom/Left |
number | 页边距 (mm) |
columnGap, rowGap |
number | 标签横向 / 纵向间距 (mm) |
flow |
'left-right-top-bottom' | 'top-bottom-left-right' |
标签填充顺序 |
默认 paper(未指定时)
{
"type": "A4",
"width": 210,
"height": 297,
"orientation": "portrait",
"marginTop": 10,
"marginRight": 10,
"marginBottom": 10,
"marginLeft": 10,
"columnGap": 2,
"rowGap": 2,
"flow": "left-right-top-bottom"
}
裁切线 CutLineConfig
PDF 中标签之间的裁切参考线(预览与导出一致)。
| 字段 | 类型 | 说明 |
|---|---|---|
enabled |
boolean | 是否显示 |
style |
'dashed' | 'solid' |
线型 |
width |
number | 线宽 (mm),默认 0.1 |
color |
string | 颜色,默认 #cccccc |
完整示例
{
"id": "demo_label_001",
"name": "货位标签示例",
"width": 70,
"height": 40,
"variableList": ["货位编号", "物料名称", "批次号"],
"variableDefaults": {
"货位编号": "A-01-05",
"物料名称": "示例物料",
"批次号": "2026061101"
},
"paper": {
"type": "A4",
"width": 210,
"height": 297,
"orientation": "portrait",
"marginTop": 10,
"marginRight": 10,
"marginBottom": 10,
"marginLeft": 10,
"columnGap": 2,
"rowGap": 2,
"flow": "left-right-top-bottom"
},
"cutLine": {
"enabled": true,
"style": "dashed",
"width": 0.1,
"color": "#cccccc"
},
"elements": [
{
"id": "el_title",
"type": "text",
"name": "货位前缀",
"x": 2,
"y": 2,
"width": 66,
"height": 8,
"content": "货位:",
"fontSize": 9,
"fontWeight": "bold",
"textAlign": "left",
"fontFamily": "sans",
"barcodeFormat": "CODE128",
"showText": false,
"textColor": "#333333"
},
{
"id": "el_location",
"type": "text",
"name": "货位段",
"x": 18,
"y": 2,
"width": 20,
"height": 8,
"content": "{货位编号}",
"fontSize": 10,
"fontWeight": "bold",
"textAlign": "left",
"fontFamily": "mono",
"barcodeFormat": "CODE128",
"showText": false,
"textExtractMode": "split",
"textSplitDelimiter": "-",
"textSplitIndex": 2,
"visibilityMode": "conditional",
"visibilityRules": [
{
"target": "content",
"variable": "",
"operator": "not_empty"
}
]
},
{
"id": "el_name",
"type": "text",
"name": "物料名称",
"x": 2,
"y": 12,
"width": 66,
"height": 10,
"content": "{物料名称}",
"fontSize": 8,
"fontWeight": "normal",
"textAlign": "left",
"fontFamily": "sans",
"barcodeFormat": "CODE128",
"showText": false,
"wordWrap": true
},
{
"id": "el_barcode",
"type": "barcode",
"name": "条码",
"x": 2,
"y": 24,
"width": 40,
"height": 14,
"content": "{货位编号}",
"fontSize": 8,
"textAlign": "center",
"fontWeight": "normal",
"fontFamily": "mono",
"barcodeFormat": "CODE128",
"showText": true,
"textExtractMode": "suffix",
"textExtractLength": 8
},
{
"id": "el_qr",
"type": "qrcode",
"name": "二维码",
"x": 48,
"y": 22,
"width": 18,
"height": 18,
"content": "https://example.com/{货位编号}",
"fontSize": 8,
"textAlign": "center",
"fontWeight": "normal",
"fontFamily": "sans",
"barcodeFormat": "CODE128",
"showText": false
}
]
}
说明:
barcodeFormat与showText在所有元素类型上为历史 schema 要求字段,文本/二维码/图片元素也需填写(常用CODE128+showText: false)。
内容解析顺序(导出 / 预览)
对单个元素,最终显示值按以下顺序计算:
- 将
content中的{变量}替换为数据行值(文本元素可对每个变量做数值格式化;表格对每格tableCells[*].content同样处理) - 对完整字符串做 值提取(trim → 分隔符 / 前 N / 后 N)
- 文本元素及 表格单元格 追加 显示前后缀(
textDisplayPrefix/textDisplaySuffix) - 根据 显示控制 决定是否绘制该元素
表格单元格 content 为空时不绘制该格文字;网格线仍按 tableBorderWidth 绘制(为 0 时不显示)。
导入 / 导出模板
| 操作 | 说明 |
|---|---|
| 应用内导出 | 生成 .lad JSON 文件 |
| 应用内导入 | 校验 width、height、elements 后追加到模板库 |
| 本地存储 | 浏览器 localStorage 键 label_templates_list_v2 |
导入时会执行 normalizeTemplate:补全默认 paper、cutLine,并移除已废弃的数据源字段。
导出 PDF 流程(与模板的关系)
- 选择模板 → 设计标签(编辑 JSON 中的
elements等) - 导入 Excel → 配置 变量列映射(非模板部分)
- 设置纸张排版、数据范围、裁切线(排版参数来自模板
paper/cutLine) - 预览确认 → 导出 PDF
PDF 每页按 paper 网格排列标签,裁切线可选,标签图由 Canvas 渲染为 PNG/JPEG 嵌入 PDF。
类型定义源码
完整 TypeScript 类型见 src/types.ts(含 TableCellData)。
表格工具函数见 src/tableUtils.ts。
模板规范化逻辑见 src/utils.ts 中的 normalizeTemplate。