打印组件接入文档
将传思网"打印模板"能力嵌入到您的网站,让您的用户在线设计模板、下发到本地客户端并触发打印。共三步:完成身份核验(申请 API Key 并换取 Token)→ 安装本地 ClientPrinter 客户端 → 引入 wacp.js SDK 调用「设计 / 下发 / 打印」。
1身份核验(申请 API Key + 换取 Token)
使用 wacp.js 的设计器与打印能力前,需要一个有效的 API Key 并换取 Poster 服务 Token(打印模板与海报共用同一类型 Token)。详细步骤请参阅:
Poster 换取 Token(有效期 1 小时)→ 把 Token 传给 SDK。注意:OCR / ASR / TTS 的 Token 不可用于打印模板接入,必须使用
serviceType=Poster。
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:
<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 为模板完整内容(对象),由三方自行持久化存储。 |
// 打开设计器(独立窗口)
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),保存时会强校验,否则拒绝保存。
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 作为名称。 |
// 不传名称:本地以 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版');
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 下发后再打印。 |
// 单步打印:直接传入模板内容,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);
});
print 传入 templateData 时,SDK 会自动先执行 sendTemplate 再打印,确保模板为最新版本。如果模板未变更且已下发过,可使用分步模式减少网络开销(仅走本机链路)。
3.5 事件订阅
| 事件 | 触发时机 |
|---|---|
| connected | SDK 与本地 ClientPrinter 建链成功。 |
| disconnected | 本地连接断开(用户关闭客户端、网络异常等)。 |
| printed | 本地完成一次打印,payload 含 taskCount 等统计字段。 |
| error | 调用链路中的任何错误(连接超时、token 过期、模板不存在)。 |
| idCardRead / idCardConnected / idCardDisconnected / idCardError / idCardReaderSwitched / idCardReaderEnabled | 身份证读卡器推送事件,详见下文「3.7 身份证读卡器」。 |
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 统一以字符串语义调度。
// 写入(同名键会被覆盖)
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 手动切换)。 |
// 订阅读卡事件(在调用 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 连接。默认采用懒建连,等首次业务调用时自动建连。 |
// 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 链路已建立。
// 发起截图(会弹出本机截图工具,用户框选 → 标注 → 上传)
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 可直接赋值给
<img src&gt; 预览,或解码为二进制后上传到服务器。• 建议在截图期间给用户一个「截图中...」的加载提示,完成后隐藏。
// 推荐:配合 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)决定模板的 毫米 ↔ 像素 折算关系:
画布像素宽 = 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 就是对的。
常见问题
• 确认本机已安装并运行 ClientPrinter(任务栏托盘有对应图标);未安装请点击 下载 ClientPrinter;
• 确认浏览器允许
acp:// 协议唤起(Chrome 首次需勾选"始终允许");• 默认监听端口为
8080,若被占用可在 ClientPrinter 配置中修改,并同步传入 new Wacp({ wsUrl: 'ws://127.0.0.1:新端口/' })。
• 设计器右侧「打印参数」面板必须填写
widthMm / heightMm / dpi(均为正数);• dpi 推荐:热敏标签 203 / 热敏小票 203 / 普通激光打印机 300。
• 检查
rows[i] 的键名是否与设计器中绑定的 dataSourceFields 一致(区分大小写);• 若设计器使用
{{key}} 占位符,请确保 key 已加入 dataSourceFields 并在 rows 中提供值。
•
print.dpi 必须与目标打印机实际 DPI 匹配,否则会被等比缩放;• 横向打印机请将
print.rotate90 设为 true,仅做长宽交换,无需重新设计内容。
• 换取 Token 时
serviceType 必须为 Poster;• OCR / ASR / TTS 的 Token 不可用于打印模板接入,需要单独换取;
• Token 仅在调用
wacp.openDesigner 时使用(会同步校验有效性),sendTemplate 和 print 无需 Token。
•
templateId 由三方自行生成和管理,不再由 SDK 自动生成;• 建议使用有意义的命名(如业务主键或模板名称),确保同一模板在 ClientPrinter 中唯一;
• 示例:
EMP_CARD_2026、工号牌_2026版。