# 电子面单打印组件

电子面单打印组件运行在商家本地电脑上,应用端通过 WebSocket 协议与之通信,完成面单的预览和打印。无论是使用商家后台打单,还是 ISV 打单软件,均需安装此组件。

# 下载与安装

打印组件支持 Windows(64 位 / 32 位)和 Mac 系统,请前往使用指南页面获取最新安装包:详见成长中心:微信小店「打印组件」使用指南

安装注意事项:

  • Windows 安装时提示「需要 64 位系统」,请改用 32 位安装包
  • 请务必使用最新版组件。旧版未卸载直接安装新版会导致电脑上存在两个打印组件,旧版打印新面单可能出现差异,建议先卸载旧版本
  • 安装完成后需保持打印组件处于运行状态

# 使用前准备

打印面单前需按以下顺序完成设置:

  1. 开通电子面单:在微信小店后台绑定快递公司网点/月结账号
  2. 设置快递面单模板:选择或自定义面单打印样式
  3. 下载安装并运行打印组件:安装后保持运行状态
  4. 完成打印设置:确认打印机连接正常

# 对接流程总览

前端通过 WebSocket 与打印组件通信,完整流程如下:

其中前 4 步(连接 → 校验版本 → 获取打印机 → 预览/打印)均通过 WebSocket 指令完成,后 2 步(打印成功通知 → 订单发货)回到服务端 API 调用。下面逐步说明每个指令的用法。

# 通信机制

打印组件安装后会在本地启动一个 WebSocket Server,监听 127.0.0.1:12705。前端页面作为 WebSocket Client 连接上去,双方通过 JSON 消息通信。

通信模式为请求-响应:前端发送一条 JSON 指令,打印组件处理后返回一条 JSON 结果,通过 requestID 字段匹配请求和响应。

%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#0ab8a6', 'primaryTextColor': '#ffffff', 'primaryBorderColor': '#089e8d', 'lineColor': '#0ab8a6', 'actorBkg': '#0ab8a6', 'actorBorder': '#089e8d', 'actorTextColor': '#ffffff', 'actorLineColor': '#0ab8a6', 'signalColor': '#0ab8a6', 'signalTextColor': '#333333', 'labelBoxBkgColor': '#e8f8f6', 'labelBoxBorderColor': '#0ab8a6', 'labelTextColor': '#333333', 'noteBkgColor': '#e8f8f6', 'noteBorderColor': '#0ab8a6', 'noteTextColor': '#333333'}}}%% sequenceDiagram participant C as 前端页面(Client) participant S as 打印组件(Server @ 127.0.0.1:12705) C->>S: ws.send({ command, requestID, ... }) Note right of S: 收到指令,执行操作 S->>C: onmessage({ command, requestID, ... }) Note left of C: 通过 requestID 匹配响应

关键特性:

  • 仅限本机通信:地址为 127.0.0.1,打印组件必须安装在与前端页面同一台电脑上
  • 长连接复用:连接建立后可反复发送多条指令,无需每次重连
  • 所有指令共用同一个连接:获取打印机列表、预览、打印等操作都在同一条 WebSocket 连接上完成
  • 通过 command 字段区分指令类型getPrinterList / getAppInfo / preview / print
  • 通过 requestID 字段匹配响应:前端为每条指令分配唯一 ID,打印组件在响应中原样返回

# Step 1:连接打印组件

打印组件监听本地固定端口,通过 WebSocket 建立连接:

const ws = new WebSocket('ws://127.0.0.1:12705');

ws.onopen = () => {
    console.log('与打印组件建立连接成功');
};

ws.onerror = (err) => {
    console.error('连接失败,请检查打印组件是否已启动');
};

连接失败时检查:1)打印组件是否已在本机启动;2)本地防火墙是否放行 12705 端口。

# Step 2:获取打印机列表

连接成功后,查询本机已连接的打印设备:

ws.send(JSON.stringify({
    requestID: 'req_001',
    command: 'getPrinterList'
}));

ws.onmessage = (e) => {
    const resp = JSON.parse(e.data || '{}');
    if (resp.command === 'getPrinterList') {
        // resp.printerList: [{ name: '_KM_202_NEW_', displayName: 'KM-202(NEW)', status: 3 }]
        console.log('可用打印机:', resp.printerList);
    }
};

返回的 printerList 中,name 用于后续打印指令的 printer 参数。

# Step 3:预览面单

正式打印前可先预览,返回预览图片 URL:

ws.send(JSON.stringify({
    command: 'preview',
    requestID: 'req_003',
    taskList: [{
        taskID: 'task_001',
        printInfo: '<获取打印报文接口返回的 print_info>',
        printNum: { curNum: 1, sumNum: 1 }
    }]
}));
// 返回:{ previewUrl: 'xxx' }

print_info服务端 API 返回,但打印组件在前端本地通过 WebSocket 工作,两者不在同一侧。传递路径如下:

%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#0ab8a6', 'primaryTextColor': '#ffffff', 'primaryBorderColor': '#089e8d', 'lineColor': '#0ab8a6', 'actorBkg': '#0ab8a6', 'actorBorder': '#089e8d', 'actorTextColor': '#ffffff', 'actorLineColor': '#0ab8a6', 'signalColor': '#0ab8a6', 'signalTextColor': '#333333', 'labelBoxBkgColor': '#e8f8f6', 'labelBoxBorderColor': '#0ab8a6', 'labelTextColor': '#333333', 'noteBkgColor': '#e8f8f6', 'noteBorderColor': '#0ab8a6', 'noteTextColor': '#333333', 'activationBkgColor': '#e8f8f6', 'activationBorderColor': '#0ab8a6', 'sequenceNumberColor': '#ffffff'}}}%% sequenceDiagram participant S as 商家服务端 participant F as 前端页面 participant P as 本地打印组件 S->>S: 调用取号/获取打印报文接口<br/>拿到 print_info(字符串) S->>F: 通过业务接口返回 print_info F->>P: WebSocket 发送打印指令<br/>print_info 放入 taskList[].printInfo P->>F: 返回打印结果(success/fail) F->>S: 打印成功,调用打印成功通知 API S->>S: 调用订单发货 API 完成履约

print_info 本身是一段不透明的字符串(包含面单的布局、收寄件信息、条码等渲染数据),开发者无需解析其内容,只需原样透传:

  1. 服务端调用 [API] 电子面单取号 / ewaybill_createorder(传了 template_id 时直接返回)或 [API] 获取打印报文 / get_print_content,从响应中取出 print_info
  2. 服务端通过自己的业务接口(如 HTTP API)将 print_info 返回给前端页面
  3. 前端拿到后,直接放入下方打印指令的 taskList[].printInfo 字段发送即可

# Step 4:打印面单

核心打印指令,将 print_info 发送给打印组件出单:

ws.send(JSON.stringify({
    command: 'print',
    version: '2.0',
    requestID: 'req_004',
    printType: 1,            // 1: 固定高度面单;2: 自定义 HTML 内容
    size: { width: 76, height: 130 },  // 非标准尺寸或 printType=2 时必传(单位 mm)
    printer: '_KM_202_NEW_', // 从 getPrinterList 获取的打印机 name
    taskList: [{
        taskID: 'task_001',
        printInfo: '<获取打印报文接口返回的 print_info>',
        printNum: { curNum: 1, sumNum: 1 },
        splitControl: 0,      // 0: 自动分页(默认);1: 禁止分页;2: 强制分页
        showDeliveryLogo: 1   // 0: 不展示快递logo;1: 展示(默认)
    }]
}));

主结构体参数

参数 类型 必填 说明
command String 固定为 'print'
version String 固定为 '2.0'
requestID String 请求唯一标识,调用方保证唯一
printType Number 1:打印固定高度面单(标准 130×76mm);2:打印自定义 HTML 内容(printInfo 需为 base64 编码的 HTML)
size Object 纸张尺寸 { width, height }(单位 mm)。printType=2 或面单尺寸非标准 130×76mm 时必传
printer String 打印机标识,对应 getPrinterList 返回的 name 字段
taskList Array 打印任务列表,每个元素为一个 PrintTask

PrintTask 参数(taskList 数组中的对象):

参数 类型 必填 说明
taskID String 任务唯一标识,调用方保证唯一
printInfo String 打印报文。printType=1 时为接口返回的 print_infoprintType=2 时为 base64 格式的 HTML
printNum Object 打印计数:{ curNum: 当前张数, sumNum: 总张数 }
splitControl Number 分页控制,默认 00:自动分页 / 1:禁止分页 / 2:强制分页(内容在第二页)
showDeliveryLogo Number 快递公司 logo,默认 10:不展示 / 1:展示
customInfo Object 自定义模板:templateUrl(模板 URL)+ data(模板数据),详见下方「自定义模板详解」
extendData Object 覆盖寄件人信息:sender.address(provinceName / cityName / countyName / detailInfo)、sender.userNamesender.telNumber

# 自定义模板详解

打印指令的 customInfo 参数支持自定义面单下方的附加内容区域(如商品清单、店铺 logo、条形码等)。模板引擎基于 Mustache.js

官方参考模板:https://mmec-shop-1258344707.cos.ap-shanghai.myqcloud.com/shop/public/2023-11-10/a80c0110-3fb8-4190-bdcc-10124b7dd0ce.html(可直接访问查看模板结构)

# 模板语法

语法 说明 示例
{{name}} 变量渲染(HTML 转义) {{shopName}}测试小店
{{{name}}} 变量渲染(不转义 HTML) {{{description}}}商品<br>描述
{{#list}}...{{/list}} 循环渲染列表 遍历商品列表
{{^list}}...{{/list}} 取反(列表为空时显示) 无商品时显示提示

# 条形码与二维码

模板中可使用专用标签生成条形码和二维码:

条形码

<bar-code content="{{productBarcode}}" width="300" height="60"
  config='{"displayValue": true, "textAlign": "left", "format": "auto"}'></bar-code>

config 完整配置(基于 JsBarcode):

配置项 类型 默认值 说明
format String "auto" (CODE128) 条码类型
width Number 2 单条条码宽度
height Number 100 条码高度
displayValue Boolean false 条码下方是否显示值
text String undefined 覆盖条码下方显示的文本内容
fontOptions String "" 字体样式:"bold" / "italic" / "bold italic"
font String "monospace" 字体名称
textAlign String "center" 文字水平位置:"left" / "center" / "right"
textPosition String "bottom" 文字垂直位置:"top" / "bottom"
textMargin Number 2 条码与文字间距
fontSize Number 20 文字字号
background String "#ffffff" 背景颜色
lineColor String "#000000" 条码颜色
margin Number 10 四周通用间距
marginTop Number undefined 上方间距(优先级高于 margin)
marginBottom Number undefined 下方间距(优先级高于 margin)
marginLeft Number undefined 左侧间距(优先级高于 margin)
marginRight Number undefined 右侧间距(优先级高于 margin)

二维码

<qr-code content="{{productQRcode}}" width="120" height="120"></qr-code>

# 完整模板示例

<div>
  <div style="font-weight: 700;">店铺名称 {{shopName}}</div>
  {{#productInfo}}
  <div>{{name}}, 数量: {{count}}, 编码: {{code}}, {{{description}}}</div>
  {{/productInfo}}
  <img src="{{imgSrc}}" width="300" height="50"/>
  <bar-code content="{{productBarcode}}" width="300" height="60"
    config='{"displayValue": true}'></bar-code>
  <qr-code content="{{productQRcode}}" width="120" height="120"></qr-code>
</div>

对应 customInfo.datatemplateUrl 指向上方 HTML 模板的托管地址):

customInfo: {
    templateUrl: 'https://mmec-shop-1258344707.cos.ap-shanghai.myqcloud.com/shop/public/2023-11-10/a80c0110-3fb8-4190-bdcc-10124b7dd0ce.html', // 官方参考模板,实际使用时替换为自己的模板地址
    data: {
        shopName: "测试小店",
        productInfo: [
            { name: "商品1", count: 1, code: "SKU001", description: "商品描述" },
            { name: "商品2", count: 2, code: "SKU002", description: "商品<br>描述" }
        ],
        imgSrc: "https://example.com/logo.png",
        productBarcode: "BARCODE001",
        productQRcode: "https://example.com/product"
    }
}

# 子母单打印

大件商品需拆分多个包裹时使用子母单。支持子母单的快递公司详见 [API] 电子面单子件追加 / ewaybill_addsuborder

# 取号方式

两种方式获取子母单:

  1. 取号时一次性获取:调用 [API] 电子面单取号 / ewaybill_createorder 时传入 package_quantity 参数(2~300),一次性获取母单号 + N 个子单号
  2. 取号后逐步追加:先正常取号获取母单,再通过 [API] 电子面单子件追加 / ewaybill_addsuborder 逐步追加子件

# 版本要求

打印组件需 1.52.0 及以上版本才支持子母单打印样式。建议打印前通过 getAppInfo 指令校验版本号,低版本需提示用户升级:

ws.send(JSON.stringify({
    requestID: 'req_version',
    command: 'getAppInfo'
}));
// 返回:{ appInfo: { version: '1.52.0' } }

以下三个接口都可以获取到子母单打印所需的 print_info

print_info 传给 1.52.0+ 打印组件后,默认会把所有子母单都打印出来(一个打印任务,输出多张面单)。

# 单独打印某个子单

如果只需打印某个特定子单,调用 [API] 获取打印报文 / get_print_content 时传入 sub_waybill_id(子单运单号),返回的 print_info 仅包含该子单,传入打印组件后只会打印该子单的面单。

# Step 5:打印结果处理与状态流转

打印指令发出后,通过 onmessage 监听打印组件返回的结果,根据成功或失败执行不同的后续操作。

ws.onmessage = (e) => {
    const resp = JSON.parse(e.data || '{}');
    if (resp.command === 'print') {
        resp.results.forEach(r => {
            if (r.success) {
                console.log(`任务 ${r.taskID} 打印成功`);
                // 1. 调用服务端 API:打印成功通知(ewaybill_printorder)
                // 2. 调用服务端 API:订单发货(senddelivery)
            } else {
                console.error(`任务 ${r.taskID} 失败:${r.failureReason}`);
                // 排查打印机状态后重试
            }
        });
    }
};

返回参数

参数 类型 说明
requestID String 与请求中的 requestID 一致
command String 固定为 "print"
results Array 打印结果列表,与请求中 taskList 一一对应
results[].taskID String 与请求中的 taskID 一致
results[].success Boolean true 打印成功 / false 打印失败
results[].failureReason String 失败原因,成功时为空字符串

打印成功后必须完成两步状态流转

  1. 通知平台出单结果:调用 [API] 打印成功通知 / ewaybill_printorder 扭转电子面单状态。批量场景使用 [API] 批量打印通知 / ewaybill_batchprintorder
  2. 完成订单发货:调用 [API] 订单发货 / senddelivery 将快递单号绑定到订单,流转订单状态为已发货

如果漏掉第 1 步,平台无法感知面单已出单;如果漏掉第 2 步,订单会一直停留在待发货状态。

# WebSocket 指令速查

指令 command 功能 关键参数
getPrinterList 获取打印机列表
getAppInfo 获取版本号
preview 预览面单 taskList[].printInfo
print 打印面单 taskList[].printInfoprintTypeprinter
文档变更日志(1条)
2026 年 04 月 14 日
新增 电子面单打印组件 开发指南