接入指南

打印组件接入文档

将传思网"打印模板"能力嵌入到您的网站,让您的用户在线设计模板、下发到本地客户端并触发打印。共三步:完成身份核验(申请 API Key 并换取 Token)→ 安装本地 ClientPrinter 客户端 → 引入 wacp.js SDK 调用「设计 / 下发 / 打印」。

在线设计 下发到本地 本地打印

1身份核验(申请 API Key + 换取 Token)

使用 wacp.js 的设计器与打印能力前,需要一个有效的 API Key 并换取 Poster 服务 Token(打印模板与海报共用同一类型 Token)。详细步骤请参阅:

概要:注册账号 → 申请 API Key → 用 ApiKey + serviceType=Poster 换取 Token(有效期 1 小时)→ 把 Token 传给 SDK。
注意:OCR / ASR / TTS 的 Token 不可用于打印模板接入,必须使用 serviceType=Poster
Token 使用范围:Token 仅在调用 wacp.openDesigner 打开设计器时使用(设计器打开前会先校验 Token 有效性)。
后续 wacp.sendTemplate(下发模板)和 wacp.print(打印)完全走本机链路,无需 Token、断网亦可继续操作

2安装本地打印客户端 ClientPrinter

打印动作发生在用户的本机(依赖系统打印机驱动),因此需要在每台需要打印的电脑上安装 ClientPrinter。它兼容 Windows 7 / 10 / 11(32 位 / 64 位)

安装后的客户端为 SDK 提供两项能力:

  • 注册 acp:// 自定义协议,浏览器可自动唤起客户端,无需用户手动启动;
  • 持久化已下发的模板,断网仍可继续打印;客户端不持久化任何 Token
首次接入须知:
• 点击上方按钮下载安装包后,双击运行并按提示完成系统打印机驱动安装;
• 安装后需允许其开机自启,否则用户每次需手动启动;
• 浏览器首次唤起 acp:// 时会弹出"是否允许打开 ClientPrinter",请勾选"始终允许"以避免反复确认;
• 未检测到客户端运行时,SDK 会自动弹出下载提示。

3引入 SDK 并完成三段式调用

3.1 引入 wacp.js

在您的网站页面引入 wacp.js

html
<script src="https://your-webapi.com/printtemplate/wacp.js"></script>
<script>
  var wacp = new Wacp();
</script>

3.2 打开模板设计器(openDesigner)

调用 wacp.openDesigner(token, options, onReady) 在新窗口打开设计器。Token 会被同步校验(调用服务端 API 验证有效性),校验通过后才加载设计器。用户保存模板时通过 onReady 回调拿到 templateContent(模板完整内容),由三方自行存储。

参数 必填 类型 说明
token string Poster 服务 Token。打开前会先调用服务端 API 校验有效性,校验失败则拒绝加载设计器。
options.templateData object 三方自行存储的模板内容对象。不传 则创建空白模板;传入 则加载已有模板用于继续编辑。
options.dataSourceFields object 设计器内可绑定的数据源字段映射,{ 英文键: '中文显示名' },例如 { userName: '姓名', orderId: '工号' }
options.dpi number 新建模板时画布的初始 DPI,常用 203(热敏标签/小票)/ 300(激光)/ 600(照片)。仅未传 templateData 时生效;不传时设计器默认 203,用户仍可在工具栏修改。详见下方「DPI 参数怎么填」。
options.width / options.height number 新建模板时画布初始尺寸(单位:像素)。仅未传 templateData 时生效,未传时设计器自带默认。
onReady function 设计器保存或退出时回调 { templateContent, error, type }templateContent 为模板完整内容(对象),由三方自行持久化存储。
js
// 打开设计器(独立窗口)
wacp.openDesigner(
  'a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6',   // Token(必填,服务端校验通过后才加载设计器)
  {
    templateData: myTplContent,            // 传入已有模板内容进行编辑;留空则新建
    dpi: 203,                              // 新建画布初始 DPI(203 / 300 / 600)
    dataSourceFields: {
      userName: '姓名',
      orderId:  '工号'
    }
  },
  function (ev) {
    if (ev.error) {
      console.error('[wacp] 设计器返回错误:', ev.error);
      return;
    }
    // ev.templateContent 为模板完整内容,由三方自行存储
    localStorage.setItem('myTplContent', JSON.stringify(ev.templateContent));
    console.log('模板已保存,templateContent:', ev.templateContent);
  }
);
设计器要求:必须在「打印参数」面板中填写 widthMm(毫米宽)/ heightMm(毫米高)/ dpi(建议 203 / 300),保存时会强校验,否则拒绝保存。
关于模板存储:模板内容由三方自行存储,SDK 不保留任何云端副本。保存回调返回的 templateContent 即为完整模板数据,建议业务侧序列化为 JSON 存储到自己的数据库。

3.3 下发模板到本地(sendTemplate)

用户首次打印(或模板更新后)需把模板内容下发到本地 ClientPrinter。与旧版 downloadTemplate 不同,sendTemplate 直接通过本机 WebSocket 链路传递模板内容,无需 Token、无需联网。模板 ID 由三方自行生成和管理,作为 ClientPrinter 本地的唯一标识。

参数 必填 类型 说明
templateId string 模板 ID,由三方自行生成和管理,用于 ClientPrinter 本地唯一标识。建议使用有意义的命名,如 工号牌_2026版 或业务主键。
templateData object|string 模板内容,openDesigner 保存时回调返回的 templateContent 对象,或其 JSON 字符串。
name string 本地显示名称,仅用于客户端「查看本机模板」下拉列表展示;允许重复,本地仍以 templateId 唯一。未传时默认使用 templateId 作为名称。
js
// 不传名称:本地以 templateId 作为显示名
wacp.sendTemplate('my_template_id', myTplContent)
  .then(function (info) {
    // info = { templateId, name }
    console.log('模板已下发到本地:', info.templateId, info.name);
  })
  .catch(function (err) {
    // 常见错误:本地 ClientPrinter 未运行(acp:// 唤起失败)/ templateData 为空
    alert('下发失败:' + err.message);
  });

// 传入中文名称:客户端下拉列表中会显示"工号牌-2026版"
wacp.sendTemplate('my_template_id', myTplContent, '工号牌-2026版');
无需 Token:sendTemplate 完全走本机 WebSocket 链路,不需要 Token,断网也可正常操作。这是与旧版 downloadTemplate(需走服务端中转)的核心区别。
关于名称重复:本地客户端「查看本机模板」的下拉框仅展示 name,不拼接 ID。如果多个模板使用同名称,用户从视觉上无法区分,但选中后系统仍会按唯一的 templateId 加载;建议业务侧在名称中附加可识别后缀(如「工号牌-A版」/「工号牌-B版」)。

3.4 触发本地打印(print)

打印动作完全在用户本机执行,无需联网。print 支持两种模式:单步打印(直接传入模板内容,SDK 自动下发并打印)和分步打印(先 sendTemplate 下发,再 print 仅传 templateId)。一次调用支持多条数据 + 多份份数。

参数 必填 类型 说明
templateId string 模板 ID,由三方自行生成。若同时传入 templateData,SDK 会自动先下发再打印;若已通过 sendTemplate 下发过,可仅传 templateId。
payload.rows object[] 非空数组,每个对象一条打印记录。键名必须与设计器中绑定的 dataSourceFields 一致。
payload.copies number 每条记录的打印份数,默认 1
templateData object|string 模板内容,openDesigner 保存时回调返回的 templateContent 对象,或其 JSON 字符串。SDK 会自动调用 sendTemplate 下发后再打印。
js
// 单步打印:直接传入模板内容,SDK 自动下发 + 打印
wacp.print('my_template_id', {
  rows: [
    { userName: '张三', orderId: 'A001' },
    { userName: '李四', orderId: 'A002' }
  ],
  copies: 1
}, myTplContent)
.then(function (res) {
  console.log('已下达打印任务:', res);
})
.catch(function (err) {
  console.error('打印失败:', err.message);
});

// 分步打印:先 sendTemplate 下发,后续打印仅传 templateId
wacp.sendTemplate('my_template_id', myTplContent).then(function () {
  return wacp.print('my_template_id', { rows: [{ userName: '张三', orderId: 'A001' }], copies: 1 }, myTplContent);
});
单步 vs 分步:每次调用 print 传入 templateData 时,SDK 会自动先执行 sendTemplate 再打印,确保模板为最新版本。如果模板未变更且已下发过,可使用分步模式减少网络开销(仅走本机链路)。

3.5 事件订阅

事件 触发时机
connectedSDK 与本地 ClientPrinter 建链成功。
disconnected本地连接断开(用户关闭客户端、网络异常等)。
printed本地完成一次打印,payloadtaskCount 等统计字段。
error调用链路中的任何错误(连接超时、token 过期、模板不存在)。
idCardRead / idCardConnected /
idCardDisconnected / idCardError /
idCardReaderSwitched / idCardReaderEnabled
身份证读卡器推送事件,详见下文「3.7 身份证读卡器」。
js
wacp.on('connected', function () { console.log('客户端已连接'); });
wacp.on('printed',   function (e) { console.log('打印完成', e); });
wacp.on('error',     function (e) { console.error('SDK 错误', e); });

扩展能力(本地存取 / 读卡器 / 系统)

除「设计 / 下发 / 打印」主链路外,wacp.js 还封装了 ClientPrinter 在本机提供的应用能力,可调用本机键值存取、身份证读卡器、系统动作等,本节能力全部走本机链路,不需 token

3.6 本机键值对存取 (setItems / getItems / removeItems)

将 UI 常用参数(如部门名、上次选择、设备号)写入本机 Settings 表,断电不丢;同一台机器上不同页面/会话都能读到。存储 value 统一以字符串语义调度。

js
// 写入(同名键会被覆盖)
wacp.setItems({
  deptName: '行政部',
  userName: '张三',
  lastTplId: 'tpl_20260520_xxx'
});

// 读取:返回只包含存在的 key
wacp.getItems(['deptName', 'userName']).then(function (m) {
  console.log(m.deptName, m.userName); // 行政部 张三
});

// 删除
wacp.removeItems(['deptName']);
与浏览器 localStorage 的区别:setItems 写入的是本机 ClientPrinter,与浏览器、域名均无关;同一台电脑上的任何三方网站都能读取。

3.7 身份证读卡器

ClientPrinter 内置两种读卡器驱动:HS100N(USB 免驱)与 SerialPort(串口 IDM40 类)。SDK 提供状态查询 / 切换 / 开关控制以及 推送式读卡事件

方法 说明
switchIDCardReader(type)切换型号,type'HS100N''SerialPort'。返回 { readerType }
getIDCardReaderType()查询当前型号。返回 { readerType }
setIDCardReaderEnabled(enabled)开启 / 关闭读卡。返回 { enabled, readerType }
getIDCardReaderEnabled()查询读卡开关状态。返回 { enabled, readerType }

推送事件(服务端主动广播):

事件名 触发时机 / payload
idCardRead读到身份证。ev.data 为包含姓名、性别、身份证号、地址、照片 base64 等字段的对象。
idCardConnected读卡器设备已连接(USB 插入 / 串口打开)。
idCardDisconnected读卡器设备已拔出,插回后会自动重新广播 connected
idCardError读卡过程异常(SDK 异常、设备占用等)。
idCardReaderSwitched调用 switchIDCardReader 后会同时返回 Promise,也会广播该事件。
idCardReaderEnabled读卡开关变更(包括在 ClientPrinter 本机 UI 手动切换)。
js
// 订阅读卡事件(在调用 setIDCardReaderEnabled(true) 之前订阅以免丢事件)
wacp.on('idCardRead', function (ev) {
  // ev.data 示例:
  // {
  //   Name: '张三', Sex: '男', Nation: '汉', Birth: '19900101',
  //   Address: '北京市海淀区中关村大街1号',
  //   IDCardNo: '110108199001011234',
  //   Department: '北京市公安局海淀分局',
  //   ValidStart: '20200101', ValidEnd: '20400101',
  //   PhotoBase64: 'data:image/bmp;base64,...'  // 按驱动不同可能为 JPG
  // }
  console.log('读到身份证', ev.data);
});
wacp.on('idCardError',        function (ev) { console.error('读卡错误', ev.data); });
wacp.on('idCardDisconnected', function () { /* 提示用户重插 */ });

// 选择型号 + 开启读卡
wacp.switchIDCardReader('HS100N')
    .then(function () { return wacp.setIDCardReaderEnabled(true); })
    .then(function (r) { console.log('读卡已开启', r); });
设备要求:
HS100N 需插入 USB 读卡器,ClientPrinter 会自动检测;
SerialPort(IDM40 类串口读卡器)需要使用手动点亮其驱动,ClientPrinter 会自动探测串口;
• 设备拔插后会自动推送 idCardConnected / idCardDisconnected,业务侧只需代码订阅事件即可。

3.8 系统能力

方法 说明
createDesktopShortcut(name, url)在用户桌面创建一个网页快捷方式,使用默认浏览器打开。按机器头一次使用时快速引导。
getClientNum()获取本机 ClientPrinter 的唯一通道号 ClientNum,返回 { clientNum }
clearPrintTasks()清空本机打印任务队列,不影响已下发给驱动的任务。
clearTemplates()清空本机所有已下发的模板(谨慎),后续需重新 sendTemplate
connect()显式建立 WebSocket 连接。默认采用懒建连,等首次业务调用时自动建连。
js
// 1)创建桌面快捷方式
wacp.createDesktopShortcut('打印组件 Demo',
    'https://tools.eainfor.com/printtemplate/demo-printer.html');

// 2)查询本机通道号
wacp.getClientNum().then(function (r) { console.log(r.clientNum); });

// 3)集中作业中零凌时清队
wacp.clearPrintTasks();

3.9 屏幕截图(screenshot)

调用本机 ClientPrinter 内置的截图工具,用户在屏幕上框选区域、添加标注后,点击截图工具栏「上传」按钮,SDK 会把截图以 JPEG Base64 形式(含 data:image/jpeg;base64, 前缀)返回给三方,便于直接上传到自己的服务器或做本地预览。由于需要用户交互操作,超时时间为 120 秒

前置条件:截图功能需要用户本机已安装并运行 ClientPrinter 客户端。调用前建议先监听 connected 事件,确保 WebSocket 链路已建立。
js
// 发起截图(会弹出本机截图工具,用户框选 → 标注 → 上传)
wacp.screenshot()
  .then(function (result) {
    // result.imageBase64 为 JPEG Base64(含 data:image/jpeg;base64, 前缀)
    if (!result.imageBase64) {
      console.warn('截图返回空数据');
      return;
    }

    // 1)直接预览
    var img = document.createElement('img');
    img.src = result.imageBase64;
    document.body.appendChild(img);

    // 2)或上传到自己的服务器
    var base64Only = result.imageBase64;
    if (base64Only.indexOf('data:') === 0) {
      base64Only = base64Only.split(',')[1];
    }
    var binary = atob(base64Only);
    var bytes = new Uint8Array(binary.length);
    for (var i = 0; i < binary.length; i++) {
      bytes[i] = binary.charCodeAt(i);
    }
    var blob = new Blob([bytes], { type: 'image/jpeg' });
    var fd = new FormData();
    fd.append('file', blob, 'screenshot.jpg');

    fetch('/api/upload', { method: 'POST', body: fd })
      .then(function (resp) { return resp.json(); })
      .then(function (data) { console.log('上传成功', data); })
      .catch(function (err) { console.error('上传失败', err); });
  })
  .catch(function (err) {
    // 超时:用户 120 秒内未完成操作
    // 取消:用户在截图工具栏点击「取消」
    console.error('截图失败:', err.message);
  });

参数与返回值:

项目 说明
方法签名 wacp.screenshot()
入参 无。
返回值 Promise<{ imageBase64: string }> 截图 JPEG Base64,含 data:image/jpeg;base64, 前缀。
超时 120 秒。超时或用户主动取消时 Promise reject,错误信息含「截图超时」或「用户取消」。
前置条件 ClientPrinter 已启动并与 wacp.js 建立 WebSocket 连接(监听 connected 事件确认)。
注意事项:
screenshot()本机链路,不需要 token,但需要 ClientPrinter 在运行。
• 截图完成后用户点击工具栏「上传」按钮才会返回结果;点击「取消」会 reject。
• 返回的 Base64 可直接赋值给 &lt;img src&amp;gt; 预览,或解码为二进制后上传到服务器。
• 建议在截图期间给用户一个「截图中...」的加载提示,完成后隐藏。
js
// 推荐:配合 connected 事件,确保客户端就绪后再发起截图
var ready = false;
wacp.on('connected', function () { ready = true; });

function takeScreenshot() {
  if (!ready) {
    alert('请先启动 ClientPrinter 客户端');
    return;
  }
  var btn = document.getElementById('btnScreenshot');
  btn.textContent = '⏳ 截图中...';
  btn.disabled = true;

  wacp.screenshot()
    .then(function (r) {
      btn.textContent = '📷 发起截图';
      btn.disabled = false;
      if (r.imageBase64) {
        console.log('截图成功,数据长度:', r.imageBase64.length);
      }
    })
    .catch(function (err) {
      btn.textContent = '📷 发起截图';
      btn.disabled = false;
      console.error('截图失败', err.message);
    });
}

在线体验

我们提供了一个集成 Demo 启动页,覆盖「换 Token / 设计模板 / 下发到本地 / 触发打印」全链路,可在不写代码的情况下走通流程:

建议先在 Demo 启动页跑通一次完整链路(确认本机 ClientPrinter 已安装、能成功唤起 + 出纸),再按上面的示例代码集成到您自己的网站。

DPI 参数怎么填

设计器顶部工具栏的 DPI 输入框(默认 203)决定模板的 毫米 ↔ 像素 折算关系:

formula
画布像素宽 = widthMm × dpi ÷ 25.4
画布像素高 = heightMm × dpi ÷ 25.4

所以 DPI 必须与目标打印机的实际 DPI 一致,否则本地客户端会按比例缩放,导致出纸尺寸偏大或偏小。

推荐取值

打印机类型 推荐 DPI
热敏标签机(条码、胸卡)203
热敏小票机203
普通激光 / 喷墨打印机300
高精度照片打印600
快速判断(标签机/小票机):型号末尾常带数字。例如 XX-203 即为 203 dpi,XX-300 即为 300 dpi。

如何在 Windows 查看打印机 DPI

打开 控制面板 → 设备和打印机,右键目标打印机 → 打印机属性,然后按下表查找:

选项卡 查看位置
常规 部分驱动会在"型号 / Model"行显示 203 dpi 等字样。
高级 打印默认值...图形质量,会有"分辨率"下拉框,列出 203 dpi / 300 dpi 等可选值。
设备设置 标签机驱动通常在这里有"打印头分辨率 / Print Head Resolution"选项。
注意:办公激光 / 喷墨打印机的"打印质量"里可能列出 600 dpi / 1200 dpi,那是最大输出分辨率,不是物理 DPI。普通办公场景按 300 填即可,更高反而可能引起缩放问题。

实在查不到?用试错法

如果没有规格书也找不到驱动属性,按推荐值填一个,然后通过设计器右上角的 🖨️ 打印测试页 试打一张,再用尺子量纸面:

  • 量出来比设计的 → 填的 DPI 小了,调高(如 203 → 300);
  • 量出来比设计的 → 填的 DPI 大了,调低(如 300 → 203);
  • 量出来一致 → DPI 就是对的。

常见问题

Q1:浏览器一直提示"无法连接到本地 ClientPrinter"
• 确认本机已安装并运行 ClientPrinter(任务栏托盘有对应图标);未安装请点击 下载 ClientPrinter
• 确认浏览器允许 acp:// 协议唤起(Chrome 首次需勾选"始终允许");
• 默认监听端口为 8080,若被占用可在 ClientPrinter 配置中修改,并同步传入 new Wacp({ wsUrl: 'ws://127.0.0.1:新端口/' })
Q2:保存模板提示"模板缺少 print 节"
• 设计器右侧「打印参数」面板必须填写 widthMm / heightMm / dpi(均为正数);
• dpi 推荐:热敏标签 203 / 热敏小票 203 / 普通激光打印机 300。
Q3:打印出纸为空白或字段未替换
• 检查 rows[i] 的键名是否与设计器中绑定的 dataSourceFields 一致(区分大小写);
• 若设计器使用 {{key}} 占位符,请确保 key 已加入 dataSourceFields 并在 rows 中提供值。
Q4:打印物理尺寸与设计器预览不一致
print.dpi 必须与目标打印机实际 DPI 匹配,否则会被等比缩放;
• 横向打印机请将 print.rotate90 设为 true,仅做长宽交换,无需重新设计内容。
Q5:Token 报错"服务类型不匹配"或"Token 校验失败"
• 换取 Token 时 serviceType 必须为 Poster
• OCR / ASR / TTS 的 Token 不可用于打印模板接入,需要单独换取;
• Token 仅在调用 wacp.openDesigner 时使用(会同步校验有效性),sendTemplateprint 无需 Token。
Q6:sendTemplate 或 print 报"templateId 不能为空"
templateId 由三方自行生成和管理,不再由 SDK 自动生成;
• 建议使用有意义的命名(如业务主键或模板名称),确保同一模板在 ClientPrinter 中唯一;
• 示例:EMP_CARD_2026工号牌_2026版