更新记录

1.0.0(2026-09-24)

  • 首次发布,支持 uni-app Android 5.0+ 和 iOS 12.0+ App 真机运行。
  • 支持 Android 经典蓝牙、Wi-Fi(RAW TCP)和 USB Host(OTG)连接;支持 iOS BLE 蓝牙和 Wi-Fi(RAW TCP)连接。
  • 支持 ESC/POS、TSPL、CPCL、ZPL 打印协议,以及文本、图片、一维码、二维码、PDF417 和线框等常用打印内容。
  • 支持连接状态、原始数据、打印机状态、设备信息和打印结果回调,并提供常用打印参数设置接口。
  • 提供蓝牙扫描、设备连接及各打印协议的完整 Demo 和接入文档。

平台兼容性

uni-app(4.87)

Vue2 Vue3 Chrome Safari app-vue app-nvue Android iOS 鸿蒙
√ √ × × √ - 5.0 12 ×
微信小程序 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 小红书小程序 快应用-华为 快应用-联盟
× × × × × × × × × - × ×

uni-app x(4.87)

Chrome Safari Android iOS 鸿蒙 微信小程序
× × - - × ×

其他

多语言 暗黑模式 宽屏模式
× √ √

xl-hanyinPrinter

汉印 UxPrint 打印机 UTS 插件,面向 uni-app 的 App 端,提供打印机连接、指令生成、数据发送和状态查询能力。

  • Android:经典蓝牙、Wi-Fi(RAW TCP)和 USB Host(OTG)
  • iOS:BLE 蓝牙和 Wi-Fi(RAW TCP)
  • 打印协议:ESC/POS、TSPL、CPCL、ZPL
  • 打印内容:文本、图片、一维码、二维码、PDF417、线框等
  • 设备能力:状态查询、设备信息、打印结果和常用参数设置

插件包含原生 SDK,只支持 App 真机运行,不支持 H5 和各类小程序。USB 连接目前仅支持 Android。

运行要求

项目 要求
HBuilderX 3.6.8 及以上
项目类型 uni-app App 项目;本 Demo 使用 Vue 3
Android Android 5.0(API 21)及以上
iOS iOS 12 及以上
调试设备 真实 Android/iOS 设备及支持相应协议的打印机

首次运行带原生插件的项目时,请使用包含本插件的自定义调试基座,或直接云打包安装到真机。普通浏览器和小程序开发工具无法验证打印功能。

先运行 Demo

  1. 用 HBuilderX 打开本项目。
  2. 在 manifest.json 中确认 App 标识、Android 权限和 iOS 隐私描述。
  3. 制作包含 xl-hanyinPrinter 的自定义调试基座,并运行到真机。
  4. 打开打印机,并根据需要准备蓝牙、局域网或 OTG 数据线。
  5. 在首页选择连接方式。连接成功后进入 ESC/POS、TSPL、CPCL、ZPL 或“通用”页面测试。

Demo 已包含以下完整流程,可直接作为业务代码参考:

  • pages/index/index.vue:连接、断开和连接状态监听
  • pages/bluetooth/bluetooth.vue:蓝牙扫描与设备选择
  • pages/esc/esc.vue:票据、图片、条码、页模式和设备查询
  • pages/tspl/tspl.vue:标签、内置字体、文本块和状态查询
  • pages/cpcl/cpcl.vue:便携标签和打印结果查询
  • pages/zpl/zpl.vue:工业标签打印
  • pages/common/common.vue:设备信息查询和通用参数设置

接入自己的项目

将插件目录放到项目的 uni_modules/xl-hanyinPrinter 下,然后从模块路径按需导入:

import {
    connectBlue,
    connectWifi,
    connectUsb,
    disconnect,
    isConnect,
    onConnectStateChange
} from '@/uni_modules/xl-hanyinPrinter'

权限配置

Android 端需要蓝牙扫描/连接、旧系统定位兼容和网络权限。插件的 AndroidManifest.xml 已声明必要权限,扫描时也会按系统版本申请运行时权限。若主项目自行维护权限列表,请参考本 Demo 的 manifest.json,至少保留:

  • Android 12 及以上:BLUETOOTH_SCAN、BLUETOOTH_CONNECT
  • Android 11 及以下:BLUETOOTH、BLUETOOTH_ADMIN、ACCESS_FINE_LOCATION
  • Wi-Fi:INTERNET、ACCESS_NETWORK_STATE

iOS 主项目需要在 manifest.json 的“App 权限配置”中填写蓝牙和本地网络用途说明:

{
    "NSBluetoothAlwaysUsageDescription": "用于搜索并连接蓝牙打印机",
    "NSBluetoothPeripheralUsageDescription": "用于搜索并连接蓝牙打印机",
    "NSLocalNetworkUsageDescription": "用于通过局域网连接并使用 WiFi 打印机"
}

发布前请根据应用实际用途调整文案。用户拒绝权限后,蓝牙扫描或局域网连接会失败。

连接打印机

所有连接方式都通过 onConnectStateChange() 异步返回结果。建议在发起连接前注册监听,并根据 state 更新页面状态。

import {
    onConnectStateChange,
    disconnect,
    isConnect
} from '@/uni_modules/xl-hanyinPrinter'

onConnectStateChange((result) => {
    if (result.state === 'connectSuccess') {
        console.log('连接成功', result.device)
    } else if (result.state === 'connectFail') {
        console.error('连接失败', result.message)
    } else if (result.state === 'disconnect') {
        console.log('连接已断开', result.message)
    }
})

console.log('当前是否已连接:', isConnect())

// 页面销毁或不再使用打印机时主动断开
disconnect()

result.state 的值为:

值 含义
connectSuccess 连接成功,可以发送打印或查询指令
connectFail 连接失败,原因见 message
disconnect 主动断开、连接丢失或设备被拔出

蓝牙连接

Android 扫描经典蓝牙打印机,iOS 扫描 BLE 打印机。发现设备后,把回调中的 deviceId 原样传给 connectBlue()。

import {
    scanBlue,
    stopScanBlue,
    connectBlue
} from '@/uni_modules/xl-hanyinPrinter'

scanBlue((device) => {
    console.log(device.name, device.deviceId, device.paired)
    // 用户选中设备后:
    // stopScanBlue()
    // connectBlue(device.deviceId)
})

// 页面退出或扫描超时后必须停止扫描
setTimeout(() => stopScanBlue(), 12000)

注意:

  • 连接前请确认系统蓝牙已开启,打印机处于可发现状态。
  • Android 低版本的蓝牙扫描依赖定位权限;部分系统还要求开启定位服务。
  • 不要写死 deviceId。iOS 返回的是系统生成的标识,应使用本次扫描结果。

Wi-Fi 连接

手机和打印机需要处于可互通的局域网中。RAW 打印端口通常为 9100,也可按打印机配置传入其他端口。

import { connectWifi } from '@/uni_modules/xl-hanyinPrinter'

connectWifi('192.168.1.100')

// 指定端口
connectWifi('192.168.1.100', 9100)

如果连接失败,请先确认手机能访问打印机 IP、打印机已启用 RAW TCP 服务,并检查 AP 隔离、防火墙和端口配置。

USB 连接(仅 Android)

通过 OTG 接入 USB 打印机后调用 connectUsb()。插件会自动选择第一个包含 USB Printer Class 接口的设备,并在首次使用时弹出系统授权。

import { connectUsb } from '@/uni_modules/xl-hanyinPrinter'

connectUsb()

USB 打印机需提供 Printer Class 接口及 Bulk IN/OUT 端点。若提示未找到设备,请检查手机是否支持 USB Host、OTG 是否开启,以及数据线是否支持数据传输。

第一次打印

先理解指令缓冲区

大多数打印和设置方法只会把指令加入内存缓冲区,并不会立即发给打印机。标准流程是:

  1. 用 clearCommands() 清理上一次未发送的内容。
  2. 按顺序调用协议方法生成一批指令。
  3. 调用一次 writeData() 发送整批指令。

writeData() 完成后会清空缓冲区。连接断开时不要继续生成或发送指令。

import {
    clearCommands,
    escInitializePrinter,
    escText,
    escPrintAndFeedLines,
    writeData
} from '@/uni_modules/xl-hanyinPrinter'

clearCommands()
escInitializePrinter()
escText('Hello, UxPrint!', {
    align: 'center',
    bold: true,
    fontSize: 32,
    encoding: 'GB18030'
})
escPrintAndFeedLines(3)

writeData((result) => {
    if (result.complete) {
        console.log('指令发送成功')
    } else {
        console.error('发送失败:', result.message)
    }
})

writeData().complete 表示数据是否完整写入连接通道,不等同于纸张已经打印完成。需要确认物理打印结果时,请使用打印结果相关接口,并确保打印机型号支持结果上报。

TSPL 标签

import {
    clearCommands,
    tsplSize,
    tsplCls,
    tsplText,
    tsplQRCode,
    tsplPrint,
    writeData
} from '@/uni_modules/xl-hanyinPrinter'

clearCommands()
tsplSize({ width: 60, height: 40 }) // TSPL 通常使用毫米
tsplCls()
tsplText({ content: '商品标签', x: 20, y: 20, fontSize: 24 })
tsplQRCode({ content: 'SKU-10001', x: 20, y: 70, size: 5, errorLevel: 'M' })
tsplPrint(1)
writeData((result) => console.log(result))

CPCL 标签

import {
    clearCommands,
    cpclSize,
    cpclText,
    cpclBarCode,
    cpclPrint,
    writeData
} from '@/uni_modules/xl-hanyinPrinter'

clearCommands()
cpclSize({ width: 576, height: 400, copies: 1 })
cpclText({ content: '订单号:A10001', x: 20, y: 30, fontSize: 24 })
cpclBarCode({ content: 'A10001', x: 20, y: 100, width: 2, height: 80, type: '128' })
cpclPrint()
writeData((result) => console.log(result))

ZPL 标签

import {
    clearCommands,
    zplStart,
    zplPrintWidth,
    zplLabelLength,
    zplText,
    zplQRCode,
    zplPrint,
    writeData
} from '@/uni_modules/xl-hanyinPrinter'

clearCommands()
zplStart()
zplPrintWidth(576)
zplLabelLength(360)
zplText({ content: 'UxPrint ZPL', x: 40, y: 30, fontName: '0', fontSize: 40 })
zplQRCode({ content: 'https://example.com', x: 40, y: 100, size: 6 })
zplPrint(1)
writeData((result) => console.log(result))

查询打印机

状态、设备信息、通用参数和打印结果属于查询操作。这些方法会直接发送请求并读取响应,不需要调用 writeData()。

import { escGetStatus, escGetDeviceInfo } from '@/uni_modules/xl-hanyinPrinter'

escGetStatus((result) => {
    if (!result.complete || !result.status) {
        console.error(result.message)
        return
    }

    console.log('在线:', result.status.isOnline)
    console.log('缺纸:', result.status.isPaperOut)
    console.log('开盖:', result.status.isCoverOpen)
    console.log('过热:', result.status.isOverheating)
})

escGetDeviceInfo((result) => {
    if (result.complete && result.info) {
        console.log(result.info.model, result.info.firmware, result.info.serialNumber)
    }
})

同一时间只允许一个查询等待打印机响应。请等待当前查询回调后再发起下一次查询;超时、解析失败或断开连接时,回调中的 complete 为 false,具体原因见 message。

打印图片

四种协议的图片接口都接收 ImageCommand:

{
    base64: 'data:image/png;base64,...', // 也可以只传纯 Base64
    x: 0,
    y: 0,
    width: 384,       // 打印宽度,单位 dot
    height: 200,      // 打印高度,单位 dot
    dithering: true,  // 扩散抖动转黑白图
    compress: false   // LZO 压缩,需要打印机支持
}

请在传入前按打印机有效宽度缩放图片。203 dpi 机型常见纸宽对应关系约为:58 mm 纸使用 384 dot,80 mm 纸使用 576 dot,实际可打印宽度以机型为准。图片过大时会增加处理时间和传输失败概率。

API 速查

分类 常用接口
连接与监听 scanBlue、stopScanBlue、connectBlue、connectWifi、connectUsb、disconnect、isConnect、onConnectStateChange、onBlueStateChange、onDataReceive
缓冲与发送 clearCommands、writeData
ESC/POS escText、escImage、escBarCode、escQRCode、escPDF417、escPrintTableRow、escGetStatus、escGetDeviceInfo、escGetPrintResult
TSPL tsplSize、tsplCls、tsplText、tsplTextWithFont、tsplTextBlock、tsplImage、tsplBarCode、tsplQRCode、tsplPrint、tsplGetStatus
CPCL cpclSize、cpclText、cpclImage、cpclBarCode、cpclQRCode、cpclForm、cpclPrint、cpclGetStatus、cpclGetPrintResult
ZPL zplStart、zplText、zplImage、zplBarCode、zplQRCode、zplPrint、zplGetStatus
通用查询与设置 getPrinterModel、getSerialNumber、getFirmwareVersion、getBatteryLevel、getPaperType、setPaperType、getPrintWidth、setPrintWidth、setPrintDensity、resetPrinter
原始指令 escStringCommand / escBytesCommand,以及对应的 tspl*、cpcl*、zpl* 方法

完整方法签名、参数类型、默认值和字段说明见 utssdk/interface.uts。

哪些方法需要 writeData()

需要调用 writeData():

  • 文本、图片、条码、二维码、走纸、切纸、自检页等打印和设备控制方法
  • setPaperType()、setPrintWidth()、setPrintDensity() 等通用设置方法
  • *StringCommand() 和 *BytesCommand() 原始指令方法

不需要调用 writeData():

  • escGetStatus()、escGetDeviceInfo()、tsplGetStatus()、cpclGetStatus()、zplGetStatus()
  • getPrinterModel()、getSerialNumber()、getFirmwareVersion() 等通用查询
  • escGetPrintResult()、cpclGetPrintResult()、cpclGetPrintResultWithoutId() 等结果查询

打印结果查询有一个例外:打印任务本身仍需调用 writeData() 发送,结果查询接口负责等待该任务的打印机响应。完整时序请参考 Demo 中的 ESC/POS 和 CPCL 页面。

常见问题

扫描不到蓝牙打印机

  • 确认打印机已开机、蓝牙可发现,且没有被其他手机占用。
  • Android 请授予“附近的设备”权限;Android 11 及以下同时检查定位权限和定位服务。
  • iOS 请在系统设置中允许应用使用蓝牙。
  • 停止旧扫描后重新调用 scanBlue(),不要同时启动多次扫描。

已连接但没有出纸

  • 确认使用的协议与打印机当前仿真模式一致。
  • 检查生成指令后是否调用了 writeData()。
  • 查看 writeData() 的 complete 和 message,并用对应协议的 *GetStatus() 检查缺纸、开盖或过热。
  • 标签协议通常还需要结束指令:TSPL 使用 tsplPrint(),CPCL 使用 cpclPrint(),ZPL 使用 zplPrint()。

中文乱码

  • ESC/POS 文本默认使用 GB18030,也可以通过 encoding 显式指定。
  • 确认打印机固件支持相同字符编码、代码页或中文字体。
  • TSPL/ZPL 内置字体不一定包含中文;需要时可使用 SDK 栅格化文字接口或改为图片打印。

Wi-Fi 连接超时

  • 确认手机和打印机处于同一局域网,且网段可互通。
  • 检查打印机 IP 是否变化、RAW TCP 端口是否为 9100。
  • 关闭路由器的客户端/AP 隔离,检查防火墙是否拦截端口。
  • iOS 首次使用时允许“本地网络”权限。

查询一直超时

  • 并非所有机型都支持所有状态和设备信息指令,请先核对型号协议手册。
  • 同一时刻只执行一个查询,并等待回调后再发起下一个。
  • 确保当前没有其他页面或业务同时读写打印机。

隐私、权限声明

1. 本插件需要申请的系统权限列表:

Android:蓝牙扫描与连接、兼容 Android 11 及以下的定位权限、网络访问;iOS:蓝牙、本地网络;Android USB Host 连接时请求 USB 设备访问授权。

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不采集、不存储或上传用户数据。打印内容和设备连接信息仅在本地用于与用户指定的打印机通信。

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

无