更新记录
1.0.0(2026-11-02)
首次发布,支持 Android 平台(最低 SDK 21,需设备支持 USB Host / OTG)。
- USB:设备枚举、打印机枚举、USB 连接(权限申请 + 插拔监听 + 关闭连接回调)
- 画布打印:画线、边框、文字、区域文字、条码、区域条码、二维码、图片、区域图片、提交打印
- TSPL 标签打印:纸宽高、页设置、线、边框、文本、区域文本、条码、二维码、图片、区域图片、提交、翻转、擦除、标签设置、间隙、蜂鸣、原始指令、钱箱(19 个方法)
- 通用打印:文本、图片(压缩 / 浓度 / 灰度 / 针打)、表格、条码二维码、纸宽、页设置、初始化、字体、打印属性、编码集、钱箱、切纸、缺纸 / 纸将尽 / 状态查询、十六进制互转
平台兼容性
uni-app(4.72)
| Vue2 |
Vue2插件版本 |
Vue3 |
Vue3插件版本 |
Chrome |
Safari |
app-vue |
app-vue插件版本 |
app-nvue |
app-nvue插件版本 |
Android |
Android插件版本 |
iOS |
鸿蒙 |
| √ |
1.0.0 |
√ |
1.0.0 |
× |
× |
√ |
1.0.0 |
√ |
1.0.0 |
5.0 |
1.0.0 |
× |
× |
| 微信小程序 |
支付宝小程序 |
抖音小程序 |
百度小程序 |
快手小程序 |
京东小程序 |
鸿蒙元服务 |
QQ小程序 |
飞书小程序 |
小红书小程序 |
快应用-华为 |
快应用-联盟 |
| × |
× |
× |
× |
× |
× |
× |
× |
× |
- |
× |
× |
uni-app x(4.72)
| Chrome |
Safari |
Android |
Android插件版本 |
iOS |
鸿蒙 |
微信小程序 |
| × |
× |
5.0 |
1.0.0 |
× |
× |
× |
zy-usb-print(USB 热敏打印机)
Android 平台 USB 热敏打印机 UTS 原生插件,覆盖 USB 设备枚举与连接、画布打印、TSPL 标签打印、
通用 ESC/POS 打印,所有方法统一 { code, message, data } 回调结构。
功能特性
- USB 方式:枚举所有 USB 设备 / USB 打印机设备、USB 连接(自动申请权限、插拔监听、关闭回调)
- 画布打印:画线、边框、文字、区域文字、条形码、区域条形码、二维码、图片、区域图片、提交打印
- TSPL 标签打印:纸张宽高、页设置、线、边框、文本、区域文本、条码、二维码、图片、区域图片、
提交打印、翻转区域、擦除区域、标签设置、标签间隙、蜂鸣器、原始指令、钱箱(共 19 个方法)
- 通用打印(ESC/POS):文本、图片(压缩 / 浓度 / 灰度 / 针打)、表格、条码二维码、纸张宽度、
页设置、初始化、字体样式、打印属性、编码集、钱箱、切纸、缺纸 / 纸将尽 / 状态 / 打印结果查询、
十六进制与字节数组互转
环境要求
| 项 |
要求 |
| 平台 |
Android(不支持 iOS / Web / 小程序) |
| 最低系统 |
Android 5.0(API 21) |
| 硬件 |
手机支持 USB Host / OTG,USB 热敏打印机 |
| 基座 |
需自定义基座或云打包(含原生插件,标准基座不可用) |
引入
按需引入用到的方法:
import { usbConnect, printText, printTable, cutPaper } from '@/uni_modules/zy-usb-print'
回调结构
所有方法统一通过 callback(res => {}) 返回:
{
code: 0, // 0 成功,其余为错误码
message: '', // 消息提示
data: {} // 业务数据(无数据时为空对象)
}
错误码
| code |
说明 |
| 0 |
成功 |
| -1 |
一般错误 / 参数错误(message 中带具体原因) |
| -2 |
未连接打印机(未调用 usbConnect 或连接已断开) |
| -100 |
未知错误 |
| -101 |
打印机连接失败 |
| -102 |
打印机连接关闭 |
| -103 |
未找到打印机设备 |
| -104 |
没有 USB 权限 / 授权超时(20 秒) |
| -105 |
USB 插入(usbConnect 回调推送) |
| -106 |
USB 拔出(usbConnect 回调推送) |
一、USB 方式
getAllUsbDevices
获取所有 USB 设备。
getAllUsbDevices(res => {
// res.data.list = [{ deviceName, deviceId, productId, productName, vendorId, deviceClass }]
})
| 字段 |
类型 |
说明 |
| deviceName |
String |
设备节点路径,如 /dev/bus/usb/001/003 |
| deviceId |
Number |
系统设备 ID |
| productId |
Number |
产品 ID |
| productName |
String |
产品名称 |
| vendorId |
Number |
厂商 ID |
| deviceClass |
Number |
设备类(7 为打印机类) |
getAllUsbPrinterDevices
获取所有 USB 打印机设备(接口类为 7),返回结构同上。
usbConnect
USB 连接。回调可持续触发:连接结果、USB 插入 / 拔出、关闭连接都通过同一个 callback 推送。
usbConnect({ deviceName: '/dev/bus/usb/001/003' }, res => {
// res.code 0 连接成功 / -101 失败 / -103 未发现设备 / -104 无权限
// -105 usb插入 / -106 usb拔出 / -102 连接关闭
})
| 参数 |
类型 |
必填 |
说明 |
| deviceName |
String |
是 |
设备名称,取 getAllUsbDevices 返回的 deviceName |
首次连接会弹出系统 USB 授权框,授权后自动完成连接(最长等待 20 秒)。
二、画布打印
画布打印分为两步:先用 drawXxx 把内容画到画布,再调用 print 提交给打印机。
drawLine(画线)
| 参数 |
类型 |
必填 |
默认值 |
说明 |
| lineWidth |
Number |
否 |
1 |
线宽 |
| startX |
Number |
是 |
- |
起点 x |
| startY |
Number |
是 |
- |
起点 y |
| endX |
Number |
是 |
- |
终点 x |
| endY |
Number |
是 |
- |
终点 y |
| isSolidLine |
Boolean |
否 |
true |
是否实线 |
drawBorder(画边框)
| 参数 |
类型 |
必填 |
说明 |
| lineWidth |
Number |
是 |
线宽 |
| topLeftX / topLeftY |
Number |
是 |
左上角坐标 |
| bottomRightX / bottomRightY |
Number |
是 |
右下角坐标 |
drawText(画文字)
| 参数 |
类型 |
必填 |
默认值 |
说明 |
| textX / textY |
Number |
是 |
- |
文本起点坐标 |
| text |
String |
是 |
- |
文本内容 |
| fontSize |
String |
否 |
Size_16 |
Size_16 / 24 / 32 / 48 / 64 / 72 / 96 |
| rotate |
String |
否 |
Rotate_0 |
Rotate_0 / 90 / 180 / 270 |
| bold / reverse / underline |
Number |
否 |
0 |
1 开启,0 关闭 |
drawTextForArea(画区域块文字)
| 参数 |
类型 |
必填 |
默认值 |
说明 |
| areaStartX / areaStartY |
Number |
是 |
- |
区域左上角 |
| areaEndX / areaEndY |
Number |
是 |
- |
区域右下角 |
| xAlign / yAlign |
String |
否 |
NONE |
NONE / START / CENTER / END |
| text |
String |
是 |
- |
文本内容 |
| fontSize |
String |
否 |
Size_16 |
字号 |
| bold / reverse / underline / deleteLine |
Number |
否 |
0 |
1 开启,0 关闭 |
| rotate |
String |
否 |
Rotate_0 |
旋转角度 |
drawBarCode(画条形码)
| 参数 |
类型 |
必填 |
默认值 |
说明 |
| startX / startY |
Number |
是 |
- |
起点坐标 |
| text |
String |
是 |
- |
条码内容 |
| barcodeType |
String |
是 |
- |
见下方取值 |
| lineWidth |
Number |
是 |
- |
线宽 |
| height |
Number |
是 |
- |
高度 |
| rotate |
String |
否 |
Rotate_0 |
旋转角度 |
barcodeType 可选值:UPC_E、CODABAR、ITF、CODE39、CODE93、CODE128、EAN8Plus2、
EAN8Plus5、EAN13Plus2、EAN13Plus5、JAN3_EAN13、JAN8_EAN8、JAN128、MSI、Postnet、
QR、UPC_A、UPCAPlus2、UPCAPlus5、UPCEPlus2、UPCEPlus5
drawBarCodeForArea(画区域块条形码)
参数为 drawBarCode 的全部参数 + areaStartX / areaStartY / areaEndX / areaEndY / xAlign / yAlign。
drawQrCode(画二维码)
| 参数 |
类型 |
必填 |
默认值 |
说明 |
| startX / startY |
Number |
是 |
- |
起点坐标 |
| text |
String |
是 |
- |
二维码内容 |
| rotate |
String |
否 |
Rotate_0 |
旋转角度 |
| ver |
Number |
否 |
1 |
版本号 |
| level |
Number |
否 |
1 |
纠错等级 |
drawGraphic(画图)
| 参数 |
类型 |
必填 |
默认值 |
说明 |
| type |
String |
是 |
- |
path 本地绝对路径 / drawable drawable 名 / assets assets 路径 |
| path |
String |
是 |
- |
图片路径或名称 |
| startX / startY |
Number |
是 |
- |
起点坐标 |
| zoomImage |
Boolean |
否 |
false |
是否缩放 |
| newWidth |
Number |
否 |
- |
缩放后宽度(zoomImage 为 true 时必填) |
drawGraphicForArea(区域画图)
额外参数:areaStartX / areaStartY / areaEndX / areaEndY / bmpSizeX / bmpSizeY / xAlign / yAlign。
print(提交打印)
| 参数 |
类型 |
必填 |
默认值 |
说明 |
| rotate |
String |
否 |
Rotate_0 |
整体旋转角度 |
| skip |
Number |
否 |
0 |
打印后走纸行数 |
drawText({ textX: 10, textY: 10, text: '画布打印示例', fontSize: 'Size_32' }, () => {})
drawQrCode({ startX: 10, startY: 60, text: 'https://uniapp.dcloud.net.cn' }, () => {})
print({ rotate: 'Rotate_0', skip: 3 }, res => console.log(res))
三、TSPL 标签打印
TSPL 指令用于标签打印机(如 100mm×150mm 标签),同样是先设置/绘制,最后 printTSPL 提交。
| 方法 |
说明 |
主要参数 |
| getPaperWidthAndHeightTSPL |
获取纸张宽高 |
回调 data.width / data.height |
| setPaperWidthAndHeightTSPL |
设置纸张宽高 |
width / height |
| pageSetupTSPL |
页设置 |
paperSizeType(SIZE_58mm / SIZE_80mm / SIZE_108mm)、pageWidth、pageHeight |
| drawLineTSPL |
打印线 |
startX / startY / lineLength / lineHeight |
| drawBorderTSPL |
打印边框 |
lineWidth / topLeftX / topLeftY / bottomRightX / bottomRightY |
| drawTextTSPL |
打印文本 |
startX / startY / text / isSimplifiedChinese / xMultiplication / yMultiplication / rotate |
| drawTextTSPLForArea |
区域打印文本 |
上者 + areaStartX / areaStartY / areaEndX / areaEndY / xAlign / yAlign |
| drawBarCodeTSPL |
打印条码 |
startX / startY / barcodeType / height / isReadable / narrowWidth / wideWidth / rotate / text |
| draw2DBarCodeTSPL |
打印二维码 |
startX / startY / barcodeType(QR / DMATRIX / PDF417)/ level / width / rotate / text |
| drawBitmapTSPL |
打印图片 |
type / path / startX / startY / zoomImage / newWidth / mode |
| drawBitmapTSPLForArea |
区域打印图片 |
上者 + areaStartX / areaStartY / areaEndX / areaEndY / xAlign / yAlign |
| printTSPL |
提交打印 |
numbers(打印份数,默认 1) |
| reverseAreaTSPL |
翻转区域 |
startX / startY / width / height |
| eraseAreaTSPL |
擦除区域 |
startX / startY / width / height |
| setPrinterTSPL |
标签设置 |
command(DIRECTION / FEED / REPRINT / SPEED / DENSITY / OFFSET / SHIFT / FORMFEED / HOME / PRINTKEY / KEY1 / KEY2 / PEEL)、value |
| setGAPTSPL |
标签间隙 |
value(间隙宽度)、offset(偏移) |
| beepTSPL |
蜂鸣器 |
level(等级)、interval(时长,毫秒) |
| sendStrToPrinterTSPL |
发送原始 TSPL 指令 |
content |
| openCashBoxTSPL |
开启钱箱 |
t1 / t2(0~255) |
setPaperWidthAndHeightTSPL({ width: 440, height: 1500 }, () => {})
pageSetupTSPL({ paperSizeType: 'SIZE_108mm', pageWidth: 440, pageHeight: 1500 }, () => {})
drawTextTSPL({ startX: 20, startY: 20, text: '标签标题', xMultiplication: 2, yMultiplication: 2 }, () => {})
drawBarCodeTSPL({ startX: 20, startY: 80, barcodeType: 'CODE128', height: 60,
narrowWidth: 2, wideWidth: 4, isReadable: true, text: '1234567890' }, () => {})
draw2DBarCodeTSPL({ startX: 20, startY: 160, barcodeType: 'QR', level: 4, width: 6, text: 'zy-usb-print' }, () => {})
printTSPL({ numbers: 1 }, res => console.log(res))
四、通用方法(ESC/POS)
close(关闭连接)
close(res => console.log(res))
sendBytesData(发送字节数据)
| 参数 |
类型 |
必填 |
说明 |
| content |
Number[] |
是 |
字节数据(0~255) |
printText(打印文本)
| 参数 |
类型 |
必填 |
说明 |
| content |
String |
是 |
打印内容,\n 换行 |
printImageWithCompressed(打印图片·压缩)
| 参数 |
类型 |
必填 |
默认值 |
说明 |
| type / path |
String |
是 |
- |
图片来源(path / drawable / assets) |
| alignType |
String |
否 |
NONE |
NONE / START / CENTER / END |
| left |
Number |
否 |
0 |
左边距 |
| isCompressed |
Boolean |
否 |
false |
是否压缩打印 |
printImageWidthLevel(打印图片·浓度)
参数同上,isCompressed 换成 level(浓度等级,默认 0)。
printColorImg2Gray(打印灰色图片)
| 参数 |
类型 |
必填 |
默认值 |
| type / path |
String |
是 |
- |
| alignType |
String |
否 |
NONE |
| left |
Number |
否 |
0 |
| isCompressed |
Boolean |
否 |
false |
printImageStylus(针打图形)
| 参数 |
类型 |
必填 |
默认值 |
说明 |
| type / path |
String |
是 |
- |
图片来源 |
| multiple |
Number |
否 |
0 |
0 倍高倍宽,1 只倍高 |
| pointNum |
Number |
否 |
0 |
针点数量 |
printTable(打印表格)
| 参数 |
类型 |
必填 |
默认值 |
说明 |
| column |
String |
是 |
- |
表头,如 品名;数量;单价;金额 |
| regular |
String |
否 |
; |
列分隔符 |
| columnWidth |
Number[] |
是 |
- |
每列宽度(字符数) |
| rows |
String[] |
是 |
- |
行内容,使用同样的分隔符 |
printTable({
column: '品名;数量;单价;金额',
regular: ';',
columnWidth: [14, 6, 6, 6],
rows: ['保鲜袋;1;10.00;10.00', '铁丝挂钩;2;5.00;10.00', '雨伞;3;5.00;15.00']
}, res => console.log(res))
printBarCode(打印条码和二维码)
| 参数 |
类型 |
必填 |
说明 |
| type |
String |
是 |
CODE39 / CODE128 / CODE93 / ITF / UPC_E / CODABAR / DATAMATRIX / JAN8 / JAN13 / JAN128 / PDF417 / QRCODE / UPC_A |
| width |
Number |
是 |
宽度 |
| height |
Number |
是 |
高度 |
| level |
Number |
是 |
等级 |
| content |
String |
是 |
内容 |
getPaperWidth(获取纸张宽度)
回调 data.width。
setPaperWidth(设置纸张宽度)
pageSetup(页设置·画布)
| 参数 |
类型 |
必填 |
说明 |
| paperType |
String |
是 |
Size_58mm / Size_80mm / Size_100mm |
| pageWidth |
Number |
是 |
纸张宽度 |
| pageHeight |
Number |
是 |
纸张高度 |
initPrinter(初始化打印机)
setFont(设置字体样式)
| 参数 |
类型 |
必填 |
默认值 |
说明 |
| characterType |
Number |
否 |
0 |
编码类型 [0,1] |
| width |
Number |
否 |
0 |
宽度倍数 [0,7] |
| height |
Number |
否 |
0 |
高度倍数 [0,7] |
| bold |
Number |
否 |
0 |
是否加粗 |
| underline |
Number |
否 |
0 |
是否下划线 |
setPrinter(设置打印属性)
| 参数 |
类型 |
必填 |
说明 |
| command |
String |
是 |
PRINT_AND_WAKE_PAPER_BY_LNCH / PRINT_AND_WAKE_PAPER_BY_LINE / PRINT_AND_ENTER / CHARACTER_RIGHT_MARGIN / ALIGN |
| value |
Number |
否 |
ALIGN 时 0 左对齐 / 1 居中 / 2 右对齐 |
getCharsetName(获取编码集)
回调 data.charsetName(默认 GBK)。
setCharsetName(设置编码集)
| 参数 |
类型 |
必填 |
| charsetName |
String |
是 |
openCashBox(打开钱箱)
| 参数 |
类型 |
必填 |
默认值 |
| cashbox1 |
Boolean |
否 |
false |
| cashbox2 |
Boolean |
否 |
false |
cutPaper(切纸)
| 参数 |
类型 |
必填 |
默认值 |
说明 |
| isHalf |
Boolean |
否 |
false |
true 半切 / false 全切 |
isPaperOut(是否缺纸)
回调 data.isPaperOut。
isPaperWillOut(纸张是否将用尽)
回调 data.isPaperWillOut。
getCurrentStatus(获取打印机状态)
回调 data.status:0 正常 / -1 写入数据失败 / -2 缺纸 / -3 纸将尽 / -4 开盖 / -5 读数据失败。
getPrintingStatus(获取打印结果状态)
| 参数 |
类型 |
必填 |
默认值 |
说明 |
| timeout |
Number |
否 |
100 |
超时时间(毫秒) |
回调 data.status:0 打印完成 / -1 未知异常 / -2 正在打印中 / -3 缺纸 / -4 纸舱盖开盖 /
-5 与打印机通信失败 / -6 通信异常 / -7 接收数据格式不正确。
hexStringToByteArray(十六进制字符串转字节数组)
| 参数 |
类型 |
必填 |
说明 |
| content |
String |
是 |
十六进制字符串,如 1B 40 1B 61 01 |
回调 data.bytes(Number[])。
stringToHex(字符串转 16 进制)
| 参数 |
类型 |
必填 |
说明 |
| content |
String |
是 |
待转换字符串(GBK 编码) |
回调 data.value(小写十六进制字符串)。
五、完整示例
import {
getAllUsbPrinterDevices,
usbConnect,
initPrinter,
setPrinter,
setFont,
printText,
printTable,
printBarCode,
cutPaper
} from '@/uni_modules/zy-usb-print'
// 1. 查找打印机并连接
getAllUsbPrinterDevices(res => {
if (res.code !== 0 || res.data.list.length === 0) {
return console.log('未发现打印机')
}
const deviceName = res.data.list[0].deviceName
usbConnect({ deviceName }, conn => {
console.log('连接回调', conn.code, conn.message)
if (conn.code === 0) {
printReceipt()
}
})
})
// 2. 打印小票(ESC/POS)
function printReceipt() {
initPrinter(() => {})
setPrinter({ command: 'ALIGN', value: 1 }, () => {})
setFont({ width: 1, height: 1, bold: 1 }, () => {})
printText({ content: '欢迎光临\n' }, () => {})
setFont({ width: 0, height: 0, bold: 0 }, () => {})
printText({ content: '订单号:20261031001\n' }, () => {})
printTable({
column: '品名;数量;金额',
columnWidth: [16, 6, 8],
rows: ['美式咖啡;1;18.00', '芝士蛋糕;2;36.00']
}, () => {})
printBarCode({ type: 'QRCODE', width: 4, height: 4, level: 4, content: 'https://example.com/order/20261031001' }, () => {})
printText({ content: '合计:54.00 元\n\n\n' }, () => {})
cutPaper({ isHalf: false }, res => console.log('切纸', res.code))
}
注意事项
- 必须使用自定义基座:插件包含原生代码,标准基座不包含该插件。
- USB 授权:首次连接会在系统弹窗中请求 USB 权限,用户拒绝或 20 秒未操作返回
-104;
部分机型需在系统设置中为 App 打开「USB 权限」。
- text 参数换行:
printText 使用 \n 换行;TSPL 原始指令使用 \r\n。
- 图片来源:
path:手机本地文件路径,推荐用 uni.chooseImage 取临时路径(插件已兼容 file:// 前缀),
也支持 /sdcard/xxx.png 这类绝对路径(Android 10+ 访问公共目录需存储权限或使用应用私有目录)
drawable:宿主 App 的 drawable 资源名(插件自身不携带图片资源,需宿主项目提供)
assets:宿主 App 的 assets 内路径
- 画布打印:
drawXxx 只是把内容绘制到画布,必须调用 print 才会真正打印;
打印前建议先 initPrinter 清空画布。
- TSPL 与 ESC/POS 是两套指令集:小票机用
printText 等通用方法,标签机用 drawXxxTSPL + printTSPL。
实现说明
| 项 |
说明 |
| 依赖 |
打印能力由插件内置,无 maven 依赖,本地即可编译调试 |
| USB 连接 |
权限申请使用可变 PendingIntent + 包名限定,并用轮询复核授权结果,兼容 Android 12+ / 14 的广播与 PendingIntent 限制 |
| 线程 |
所有 USB 写操作串行投递到单线程池执行,回调切回主线程,避免主线程阻塞与并发写导致的数据交错 |
| hexStringToByteArray |
按实际内容返回对应长度的字节数组,不做固定长度补位 |
| 示例资源 |
插件不内置示例图片,示例页用 uni.chooseImage 选择本地图片后以 type: 'path' 打印 |
| 参数兼容 |
drawTextForArea 同时接受 deleteLine 与 deleteline;pageSetupTSPL 同时接受 paperSizeType 与 paperSize |