Barcode / QRCode 条码二维码
通用布局能力:
RenderBarcode实例统一支持width、height、min/max、margin和槽位对齐;条码绘制使用布局后的实际size,不要直接修改该结果。详见组件通用布局属性。
RenderBarcode 用于在 DirectSurface UI 中绘制一维条码和二维码。它不依赖 DOM 组件,编辑模式可直接绘制到 Canvas;打印或 SVG 输出可使用同一套编码逻辑生成 SVG vnode。
当前支持:
code128code39ean13qrcode
历史拼写 ena13 会被兼容归一化为 ean13。
API 总览
import { RenderBarcode, barcodeToSvgVNode, measureBarcode, normalizeBarcodeType, paintBarcode, type BarcodeMetrics, type BarcodeOptions, type BarcodeType,} from 'ds-ui'
| API | 类型 | 用途 |
|---|---|---|
RenderBarcode |
class | 在 DirectSurface UI 中绘制一维条码或二维码的 render object。 |
BarcodeOptions |
type | 条码/二维码配置。 |
BarcodeType |
type | 支持的编码类型:code128、code39、ean13、ena13、qrcode。 |
BarcodeMetrics |
type | 测量和有效性结果。 |
measureBarcode() |
function | 不绘制,仅测量尺寸和校验有效性。 |
paintBarcode() |
function | 直接向 CanvasRenderingContext2D 绘制。 |
barcodeToSvgVNode() |
function | 生成打印/SVG 场景可用的 vnode。 |
normalizeBarcodeType() |
function | 归一化编码类型,未知值回退到 code128。 |
最小装配顺序是:创建 RenderBarcode({ type, text, width, height })。医疗文档、标签和表单中应明确宽高,避免条码内容变化引起排版跳动。
何时使用
使用 Barcode / QRCode:
- 医疗腕带、标本标签、申请单号、病历号等一维条码。
- 移动端扫码、URL、文档定位、患者标识等二维码。
- 需要在 Canvas 编辑模式和 SVG/打印模式保持一致编码结果。
不要使用:
- 大量富图形、Logo 二维码、渐变彩色码。当前只支持基础黑白编码绘制。
- 需要交互识别的扫码器组件。
RenderBarcode只负责显示。 - 不稳定宽高的表单行内内容。医疗文档中条码区域应给定明确宽高,避免排版跳动。
最小示例
const barcode = new RenderBarcode({ type: 'code128', text: 'MR202605130001', width: 180, height: 56, showText: true,})
二维码:
const qrcode = new RenderBarcode({ type: 'qrcode', text: 'https://example.com/patient/123', width: 96, height: 96,})
更新值:
const barcode = new RenderBarcode({ type: 'code39', text: 'ABC123' })barcode.setValue('XYZ789')
setValue() 会请求布局,因为不同内容可能改变编码有效性和推荐尺寸。
BarcodeType
type BarcodeType = 'ean13' | 'ena13' | 'code39' | 'code128' | 'qrcode'
| 类型 | 说明 | 内容限制 |
|---|---|---|
code128 |
默认类型。当前实现是 Code 128-B。 | ASCII 32-127。中文等非 ASCII 内容无效。 |
code39 |
Code 39。 | 0-9、A-Z、空格、- . $ / + %。输入会转大写。 |
ean13 |
EAN-13 商品码。 | 12 或 13 位数字;13 位时校验位必须正确。 |
ena13 |
兼容历史拼写。 | 等同 ean13。 |
qrcode |
二维码。 | 使用内置 qrcodegen 编码,纠错等级固定为 MEDIUM。 |
未知 type 会归一化为 code128:
const type = normalizeBarcodeType('unknown')
BarcodeOptions
interface BarcodeOptions { type?: BarcodeType | string text?: string width?: number height?: number foreground?: string background?: string showText?: boolean quietZone?: number}
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
type |
BarcodeType | string |
'code128' |
编码类型。未知值按 code128 处理。 |
text |
string |
'' |
待编码文本。 |
width |
number |
一维 120,二维码 48 | 绘制宽度。最小归一化为 1。 |
height |
number |
一维 48,二维码跟随宽度 | 绘制高度。最小归一化为 1。 |
foreground |
string |
'#000' |
条/模块颜色。 |
background |
string |
'#fff' |
背景颜色。 |
showText |
boolean |
true |
一维码是否在底部绘制可读文本。二维码忽略该项。 |
quietZone |
number |
10 |
一维码左右静区模块数。二维码固定内部 border 为 2 个模块。 |
foreground 和 background 当前是 Canvas/SVG 可接受的颜色字符串,不做主题 token 解析。
RenderBarcode 属性和方法
| API | 类型 | 说明 |
|---|---|---|
type |
BarcodeOptions['type'] |
当前类型。 |
text |
string |
当前编码文本。 |
barcodeWidth |
number | undefined |
显式宽度。来自 options.width。 |
barcodeHeight |
number | undefined |
显式高度。来自 options.height。 |
foreground |
string | undefined |
前景色。 |
background |
string | undefined |
背景色。 |
showText |
boolean | undefined |
是否显示一维码文本。 |
quietZone |
number | undefined |
一维码静区。 |
setValue(text) |
void |
更新文本并请求布局。 |
debugState() |
BarcodeMetrics |
返回当前测量和有效性信息。 |
如果运行时直接改 type、barcodeWidth、barcodeHeight 等属性,应由调用方请求布局或重绘;只有 setValue() 内置了布局请求。
BarcodeMetrics
interface BarcodeMetrics { width: number height: number valid: boolean type: 'ean13' | 'code39' | 'code128' | 'qrcode' message?: string}
measureBarcode(options) 会返回同样结构,适合在排版前预估尺寸和有效性:
const metrics = measureBarcode({ type: 'ean13', text: '690123456789', width: 120, height: 48,}) if (!metrics.valid) { console.warn(metrics.message)}
注意:BarcodeMetrics 不暴露内部 modules,避免业务依赖编码内部结构。
Canvas 绘制
底层绘制函数:
const canvas = document.createElement('canvas')const ctx = canvas.getContext('2d')! const metrics = paintBarcode(ctx, 10, 20, { type: 'code128', text: 'MR-001', width: 180, height: 48,})
行为:
- 返回
BarcodeMetrics。 - 有效内容绘制背景和条/模块。
- 无效内容绘制占位框:一维码显示
CODE,二维码显示QR。 - 不抛出编码异常;错误信息放在
metrics.message。
RenderBarcode.performPaint() 会把当前布局尺寸作为 width/height 传给 paintBarcode(),因此 Canvas 绘制结果和实际布局尺寸一致。
SVG / 打印输出
打印或 SVG 场景使用:
const vnode = barcodeToSvgVNode({ type: 'qrcode', text: 'MR-001', width: 40, height: 40,})
barcodeToSvgVNode() 返回 snabbdom 风格 vnode:
- 有效一维码生成
path,可选text。 - 有效二维码生成
path。 - 无效内容生成占位
svg。 viewBox、width、height与测量结果一致。
医疗编辑器的 Canvas 模式使用 paintBarcode(),打印/SVG 模式使用 barcodeToSvgVNode(),两者共享编码和测量逻辑。
尺寸和排版
默认尺寸:
| 类型 | 默认宽度 | 默认高度 |
|---|---|---|
| 一维码 | 120 |
48 |
| 二维码 | 48 |
width 或 height 推导 |
尺寸规则:
width、height最小归一化为 1。- 一维码
showText !== false时,底部预留约 14px 绘制可读文本。 - 二维码使用
min(width, height)计算方形模块区域,并在剩余空间中居中。 - 二维码内部 border 固定为 2 个模块。
RenderBarcode.performLayout()会先measureBarcode(),再通过父约束constrainSize()得到最终尺寸。
医疗文档中建议显式设置条码/二维码宽高。二维码在病历数据元中通常应采用等比 resize;一维码可以允许宽高分别调整。
错误处理
常见无效情况:
| 类型 | 无效原因 |
|---|---|
ean13 |
不是 12/13 位数字,或 13 位校验位不正确。 |
code39 |
包含 Code 39 不支持的字符。 |
code128 |
包含 ASCII 32-127 以外字符,例如中文。 |
qrcode |
文本无法由内置 qrcodegen 编码。 |
示例:
const canvas = document.createElement('canvas')const ctx = canvas.getContext('2d')! const metrics = paintBarcode(ctx, 0, 0, { type: 'code128', text: '中文', width: 90, height: 32,}) if (!metrics.valid) { state.error = metrics.message}
无效内容会绘制占位,不会抛异常。这能保证病历编辑器和打印流程不中断,但业务层仍应提前校验关键码值。
缓存策略
编码计划内部使用 LRU 缓存:
- 最大缓存 256 个 plan。
- 缓存 key 包含类型、文本、宽高、
showText和quietZone。 - 命中后会更新最近使用顺序。
- 超过上限会删除最旧项。
这个缓存属于组件内部优化,不提供公开清理 API。它不缓存 Canvas、图片或 DOM 资源。
医疗编辑器集成
病历数据元 data-ele-barcode 使用同一套能力:
- 布局阶段通过
measureBarcode()得到非零 render 尺寸。 - Canvas 编辑模式通过
paintBarcode()绘制。 - SVG/打印模式通过
barcodeToSvgVNode()输出。 props.type支持ean13、ena13、code128、code39、qrcode。props.text是编码文本。props.width、props.height是文档内占位尺寸。
因此,编辑显示和打印输出不会走两套编码规则。
性能边界
- 一维码编码和绘制复杂度与文本长度、模块数相关。
- 二维码编码复杂度由
qrcodegen决定,绘制复杂度与二维码模块数相关。 - 大量重复码值会受益于内部 plan 缓存。
- 高频更新
text会触发布局,应避免在动画或输入过程中不断重建条码。 - 大批量打印建议复用测量结果或让文档布局缓存承担重复计算。
常见问题
为什么 code128 不能显示中文?
当前实现是 Code 128-B,只支持 ASCII 32-127。中文文本应使用二维码,或先由业务编码成 ASCII 标识。
为什么 ean13 输入 12 位也有效?
12 位输入会自动计算第 13 位校验码;13 位输入会校验最后一位。
二维码纠错等级能配置吗?
当前固定使用 qrcodegen.QrCode.Ecc.MEDIUM,没有公开配置项。
quietZone 对二维码有效吗?
quietZone 只影响一维码左右静区。二维码使用固定 2 模块 border。
无效内容会不会抛异常?
不会。测量和绘制都会返回 valid: false,并绘制占位提示。业务可以读取 message 做校验提示。
Canvas 和 SVG 会不会不一致?
不会刻意走两套规则。paintBarcode() 和 barcodeToSvgVNode() 都基于同一个 createBarcodePlan() 结果。