更新记录
2.1.0(2026-06-20)
UTS插件,支持安卓与iOS系统。 使用了uniapp自带蓝牙组件扫描与调用蓝牙设备,再通过本插件操控打印机。 支持打印文字、二维码、条码、图片。 详细看DEMO例子
平台兼容性
uni-app(5.07)
| Vue2 | Vue3 | Chrome | Safari | app-vue | app-nvue | Android | iOS | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|
| × | × | × | × | × | × | 8.0 | 15 | × |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| × | × | × | × | × | × | × | × | × | × | × | × |
uni-app x(5.07)
| Chrome | Safari | Android | iOS | 鸿蒙 | 微信小程序 |
|---|---|---|---|---|---|
| × | × | 8.0 | 15 | × | × |
其他
| 多语言 | 暗黑模式 | 宽屏模式 |
|---|---|---|
| × | × | √ |
fxsup-Print 打印机模块 API 说明文档
ESC/POS 蓝牙热敏打印机模块(UTS 指令生成 + JS 打印控制) 版本:2.1.0 | 适用:uni-app(App-Android / App-iOS)
目录
简介
本模块适用于基于 ESC/POS 指令集的蓝牙热敏打印机(如汉印 HPRT MPT-III 系列便携式打印机)。
核心特点:
- 职责分离:UTS 插件负责 ESC/POS 指令生成(高性能原生编译),JS 层负责蓝牙通信(BLE API 仅在 JS 层可用)
- 极简调用:业务层只需导入
printController单例即可完成完整的打印机操控 - 两种使用模式:
printController(推荐):完整蓝牙通信 + 打印控制,一行调用搞定EscPosBuilder/ 便捷函数:仅生成指令字节,自行处理蓝牙通信
为什么蓝牙通信放在 JS 层?
UTS(uni-app x)不支持蓝牙 BLE API(uni.createBLEConnection、uni.getBLEDeviceServices 等),这些 API 仅在传统 uni-app 的 JS/Vue 层可用。因此蓝牙通信必须在 JS 层实现,UTS 插件只负责 ESC/POS 指令生成。
架构说明
┌─────────────────────────────────────────────┐
│ 业务层(index.vue) │
│ printController.printText('hello') │
└──────────────────────┬──────────────────────┘
│ 导入
┌──────────────────────▼──────────────────────┐
│ services/print-controller.js(JS 层) │
│ ┌─────────────────────────────────────┐ │
│ │ BluetoothService(蓝牙通信) │ │
│ │ - 蓝牙初始化/扫描/连接/断开 │ │
│ │ - 服务发现/特征值发现 │ │
│ │ - 数据分包发送(writeNoResponse) │ │
│ └─────────────────────────────────────┘ │
│ ┌─────────────────────────────────────┐ │
│ │ PrintController(打印编排) │ │
│ │ - 调用 UTS 生成指令 │ │
│ │ - GBK 编码(中文支持) │ │
│ │ - 调用蓝牙发送数据 │ │
│ └─────────────────────────────────────┘ │
└──────────────────────┬──────────────────────┘
│ 调用指令生成
┌──────────────────────▼──────────────────────┐
│ fxsup-PrintUTSModule(UTS 插件,原生编译) │
│ ┌─────────────────────────────────────┐ │
│ │ EscPosBuilder(指令构建器) │ │
│ │ - ESC/POS 指令生成(纯逻辑) │ │
│ │ - 返回 number[] │ │
│ └─────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
集成方式
文件结构
fxsup-Print/
├── services/
│ └── print-controller.js # JS 打印控制器(蓝牙通信 + 打印编排)
└── uni_modules/
└── fxsup-PrintUTSModule/ # UTS 插件(ESC/POS 指令生成)
├── index.uts # 插件主入口(重新导出)
├── package.json
├── PrintAPI.md # 本文档
└── utssdk/
└── index.uts # UTS 核心实现(指令生成)
在页面/组件中引入
// #ifdef APP-PLUS
// 导入 JS 打印控制器(蓝牙通信 + 打印控制)
import printController from '@/services/print-controller.js'
// 导入 UTS 插件常量(条码类型等)
import { BARCODE_CODE128, BARCODE_CODE39, BARCODE_EAN13, BARCODE_UPC_A } from '@/uni_modules/fxsup-PrintUTSModule'
// #endif
直接调用
// #ifdef APP-PLUS
await printController.initBluetooth()
await printController.startScan()
await printController.connect(deviceId, deviceName)
await printController.createPrintComponent(deviceId, deviceName)
await printController.printText('Hello World')
// #endif
常量定义
以下常量由 UTS 插件导出,
print-controller.js内部也会使用。
对齐方式常量
| 常量名 | 值 | 说明 |
|---|---|---|
ALIGN_LEFT |
0 | 左对齐 |
ALIGN_CENTER |
1 | 居中对齐 |
ALIGN_RIGHT |
2 | 右对齐 |
条码类型常量
| 常量名 | 值 | 说明 |
|---|---|---|
BARCODE_UPC_A |
0 | UPC-A |
BARCODE_UPC_E |
1 | UPC-E |
BARCODE_EAN13 |
2 | EAN-13 |
BARCODE_EAN8 |
3 | EAN-8 |
BARCODE_CODE39 |
4 | CODE39 |
BARCODE_ITF |
5 | ITF |
BARCODE_CODABAR |
6 | CODABAR |
BARCODE_CODE93 |
72 | CODE93 |
BARCODE_CODE128 |
73 | CODE128(默认) |
导入方式:
// #ifdef APP-PLUS
import { ALIGN_CENTER, BARCODE_CODE128 } from '@/uni_modules/fxsup-PrintUTSModule'
// #endif
API 参考
printController(JS 打印控制器,推荐)
打印机控制器,整合蓝牙通信与 ESC/POS 打印指令。通过 services/print-controller.js 导出单例。
导入:
// #ifdef APP-PLUS
import printController from '@/services/print-controller.js'
// #endif
onDeviceFound(callback): void
注册设备发现回调,扫描到新设备时触发。
| 参数名 | 类型 | 说明 |
|---|---|---|
| callback | (devices: Array) => void | 设备发现回调,devices 为 { deviceId, name, RSSI }[] |
printController.onDeviceFound((devices) => {
console.log('发现设备:', devices)
})
onDisconnected(callback): void
注册连接断开回调,蓝牙连接异常断开时触发。
| 参数名 | 类型 | 说明 |
|---|---|---|
| callback | (reason: string) => void | 断开回调 |
printController.onDisconnected((reason) => {
console.log('连接断开:', reason)
})
initBluetooth(): Promise<boolean>
初始化蓝牙适配器。
await printController.initBluetooth()
startScan(): Promise<boolean>
开始扫描蓝牙设备。扫描结果通过 onDeviceFound 回调通知。30秒后自动停止。
printController.onDeviceFound((devices) => {
this.devices = devices
})
await printController.startScan()
stopScan(): Promise<boolean>
停止扫描。
await printController.stopScan()
getDevices(): Array
获取已发现的设备列表。
const devices = printController.getDevices()
connect(deviceId, deviceName?): Promise<boolean>
连接蓝牙设备,并自动发现服务和特征值。
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| deviceId | string | 是 | 设备 ID |
| deviceName | string | 否 | 设备名称 |
await printController.connect('66:32:F8:96:C8:80', 'MPT-III')
disconnect(): Promise<boolean>
断开蓝牙连接。
await printController.disconnect()
createPrintComponent(deviceId, deviceName?): Promise<boolean>
创建打印组件(确保蓝牙连接 + 服务发现完成)。调用打印方法前必须先调用此方法。
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| deviceId | string | 是 | 设备 ID |
| deviceName | string | 否 | 设备名称 |
await printController.createPrintComponent(deviceId, deviceName)
closePrintComponent(): Promise<boolean>
关闭打印组件并断开连接。
await printController.closePrintComponent()
printText(text, align?, bold?, widthScale?, heightScale?, feedLines?): Promise<boolean>
打印文本(自动初始化打印机 + 发送指令)。
中文编码说明:优先使用 GBK 编码(通过 Android 原生
java.lang.String.getBytes('GBK')),大多数中文热敏打印机使用 GBK/GB2312 编码。GBK 编码不可用时回退到 UTF-8。
| 参数名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| text | string | - | 文本内容(支持中文) |
| align | number | 0 | 对齐 0左 1中 2右 |
| bold | boolean | false | 是否加粗 |
| widthScale | number | 0 | 宽度倍数 0-7 |
| heightScale | number | 0 | 高度倍数 0-7 |
| feedLines | number | 3 | 走纸行数 |
// 基本打印
await printController.printText('Hello World')
// 居中加粗放大
await printController.printText('标题', 1, true, 2, 2, 5)
printImageData(width, height, bitmapData, feedLines?): Promise<boolean>
打印光栅位图图片(居中,含初始化与走纸)。
位图格式:1bit/像素,每 8 个像素打包为 1 字节,MSB 优先,1=黑点。 传输说明:使用 20 字节分包 + 5ms 间隔发送(
writeNoResponse模式),图片越大传输时间越长。
| 参数名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| width | number | - | 图片宽度(像素点数,须为 8 的倍数) |
| height | number | - | 图片高度(像素点数) |
| bitmapData | number[] | - | 位图数据(1bit/像素打包) |
| feedLines | number | 3 | 走纸行数 |
// 打印 576x533 的位图(58mm 纸宽)
await printController.printImageData(576, 533, bitmapData)
printQRCode(data, size?, feedLines?): Promise<boolean>
打印二维码(居中)。
| 参数名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| data | string | - | 二维码内容 |
| size | number | 6 | 尺寸 1-16 |
| feedLines | number | 3 | 走纸行数 |
await printController.printQRCode('https://www.example.com', 8)
printBarcode(data, type?, width?, height?, feedLines?): Promise<boolean>
打印条形码(居中)。
| 参数名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| data | string | - | 条码数据 |
| type | number | BARCODE_CODE128 | 条码类型(BARCODE_* 常量) |
| width | number | 3 | 宽度 2-6 |
| height | number | 80 | 高度 1-255 |
| feedLines | number | 3 | 走纸行数 |
// #ifdef APP-PLUS
import { BARCODE_CODE128 } from '@/uni_modules/fxsup-PrintUTSModule'
// #endif
await printController.printBarcode('1234567890', BARCODE_CODE128)
feedPaper(lines?): Promise<boolean>
走纸。
| 参数名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| lines | number | 1 | 走纸行数 |
await printController.feedPaper(3)
cutPaper(): Promise<boolean>
切纸(先走纸3行再切纸,仅支持带切刀的打印机)。
await printController.cutPaper()
checkStatus(): Promise<boolean>
查询打印机状态(发送 ESC v)。
await printController.checkStatus()
testPrint(): Promise<boolean>
测试打印(居中、放大2倍的测试文本)。
await printController.testPrint()
getStatus(): Object
获取当前打印机状态。
返回值:
| 属性 | 类型 | 说明 |
|---|---|---|
| isInitialized | boolean | 蓝牙适配器是否已初始化 |
| isScanning | boolean | 是否正在扫描 |
| isConnected | boolean | 是否已连接设备 |
| isComponentCreated | boolean | 打印组件是否已创建 |
| deviceId | string | 当前连接的设备 ID |
| deviceName | string | 当前连接的设备名称 |
| writeServiceId | string | 可写服务 UUID |
| writeCharacteristicId | string | 可写特征值 UUID |
const status = printController.getStatus()
console.log(status.isConnected, status.writeServiceId)
EscPosBuilder 类(UTS 指令构建器)
ESC/POS 指令构建器,支持链式调用。适合需要自行处理蓝牙通信或组装复杂打印任务的场景。
导入:
// #ifdef APP-PLUS
import { EscPosBuilder } from '@/uni_modules/fxsup-PrintUTSModule'
// #endif
| 方法 | 返回值 | 说明 |
|---|---|---|
init() |
EscPosBuilder | 初始化打印机(ESC @) |
setAlign(align) |
EscPosBuilder | 设置对齐(0左 1中 2右) |
setBold(bold) |
EscPosBuilder | 设置加粗 |
setTextSize(width, height) |
EscPosBuilder | 设置字号(宽高倍数 0-7) |
addText(text) |
EscPosBuilder | 添加文本 + 换行(UTF-8 编码) |
addRawText(bytes) |
EscPosBuilder | 添加预编码字节数据 + 换行(用于 GBK 等) |
addImage(width, height, data) |
EscPosBuilder | 添加光栅位图(GS v 0 命令) |
addFeedLines(lines) |
EscPosBuilder | 走纸 n 行 |
addFeedDots(dots) |
EscPosBuilder | 走纸 n 点 |
addCut() |
EscPosBuilder | 切纸(GS V 0) |
addQRCode(data, size, eccLevel?) |
EscPosBuilder | 添加二维码(size 1-16,eccLevel 0-3) |
addBarcode(data, type, width?, height?) |
EscPosBuilder | 添加条码 |
addStatusRequest() |
EscPosBuilder | 添加状态查询指令 |
build() |
number[] | 构建返回字节数组 |
clear() |
void | 清空已添加指令 |
size() |
number | 获取当前字节数 |
便捷函数(UTS 指令生成)
仅生成指令字节(number[]),不含蓝牙通信。适合自行管理蓝牙连接的场景。
导入:
// #ifdef APP-PLUS
import { buildText, buildTextFromBytes, buildQRCode, buildBarcode, buildImage, buildFeed, buildCut, buildInit, buildStatusRequest } from '@/uni_modules/fxsup-PrintUTSModule'
// #endif
| 函数 | 返回值 | 说明 |
|---|---|---|
buildInit() |
number[] | 初始化指令(ESC @) |
buildText(text, align?, bold?, w?, h?, feed?) |
number[] | 文本打印指令(UTF-8 编码) |
buildTextFromBytes(textBytes, align?, bold?, w?, h?, feed?) |
number[] | 文本打印指令(预编码字节数组,用于 GBK 等) |
buildQRCode(data, size?, feed?) |
number[] | 二维码打印指令 |
buildBarcode(data, type?, w?, h?, feed?) |
number[] | 条码打印指令 |
buildImage(width, height, data, feed?) |
number[] | 光栅位图打印指令(GS v 0) |
buildFeed(lines) |
number[] | 走纸指令 |
buildCut() |
number[] | 切纸指令(含走纸3行) |
buildStatusRequest() |
number[] | 状态查询指令 |
注意:
buildTextFromBytes和buildImage为 UTS 插件新增函数,使用前需重新编译 UTS 插件(热更新不会编译 UTS)。printController.printText内部已通过 JS 层buildTextBytesJS实现 GBK 支持,无需依赖buildTextFromBytes。
JS 层工具函数
以下函数定义在
services/print-controller.js中,为内部使用,不对外导出。
| 函数 | 返回值 | 说明 |
|---|---|---|
toUint8Array(bytes) |
Uint8Array | 将 number[] 转为 Uint8Array |
buildTextBytesJS(textBytes, align, bold, w, h, feed) |
number[] | JS 层拼接文本打印指令(支持预编码字节,不依赖 UTS) |
encodeToGBK(str) |
number[] | null | 将字符串编码为 GBK 字节数组(通过 Android 原生 API) |
initPrinter(bt) |
Promise | 发送 ESC @ 初始化打印机 |
encodeToGBK 说明:
通过 plus.android.importClass('java.lang.String') 调用 Android 原生 String.getBytes('GBK') 方法。仅 App-Android 平台可用,其他平台返回 null。
工具函数(UTS)
| 函数 | 返回值 | 说明 |
|---|---|---|
encodeToUTF8(str) |
number[] | 字符串转 UTF-8 字节数组 |
mergeBytes(arrays) |
number[] | 合并多个 number[] |
调用示例
示例 1:完整打印流程(推荐)
// #ifdef APP-PLUS
import printController from '@/services/print-controller.js'
import { BARCODE_CODE128 } from '@/uni_modules/fxsup-PrintUTSModule'
// #endif
// 1. 初始化蓝牙
await printController.initBluetooth()
// 2. 注册设备发现回调
printController.onDeviceFound((devices) => {
console.log('发现设备:', devices)
})
// 3. 扫描设备
await printController.startScan()
// 4. 连接设备
await printController.connect(deviceId, 'MPT-III')
// 5. 创建打印组件
await printController.createPrintComponent(deviceId, 'MPT-III')
// 6. 打印
await printController.printText('Hello 世界')
await printController.printQRCode('https://www.example.com')
await printController.printBarcode('1234567890', BARCODE_CODE128)
await printController.feedPaper(3)
await printController.cutPaper()
示例 2:打印图片
// #ifdef APP-PLUS
import printController from '@/services/print-controller.js'
// #endif
// 1. 选择相册图片
uni.chooseImage({
count: 1,
sizeType: ['compressed'],
sourceType: ['album'],
success: (res) => {
const imagePath = res.tempFilePaths[0]
// 2. 获取图片信息
uni.getImageInfo({
src: imagePath,
success: (info) => {
const targetWidth = 576 // 58mm 纸宽 = 576 点
const targetHeight = Math.round(info.height * targetWidth / info.width)
// 3. 转为 ESC/POS 位图(需自行实现,可参考 index.vue 中的 processImageToBitmap)
// bitmapData 为 number[],1bit/像素打包,1=黑点
const bitmapData = /* ... */
// 4. 打印
printController.printImageData(targetWidth, targetHeight, bitmapData)
}
})
}
})
示例 3:在 Vue 页面中使用
<script>
// #ifdef APP-PLUS
import printController from '@/services/print-controller.js'
import { BARCODE_CODE128 } from '@/uni_modules/fxsup-PrintUTSModule'
// #endif
export default {
data() {
return {
isConnected: false,
isComponentCreated: false,
devices: [],
textContent: 'Hello World'
}
},
onLoad() {
// #ifdef APP-PLUS
// 注册断开回调
printController.onDisconnected((reason) => {
this.isConnected = false
this.isComponentCreated = false
uni.showToast({ title: '连接断开: ' + reason, icon: 'none' })
})
// 注册设备发现回调
printController.onDeviceFound((devices) => {
this.devices = devices
})
// #endif
},
methods: {
async onInitBluetooth() {
// #ifdef APP-PLUS
await printController.initBluetooth()
// #endif
},
async onScan() {
// #ifdef APP-PLUS
await printController.startScan()
// #endif
},
async onConnect(device) {
// #ifdef APP-PLUS
await printController.connect(device.deviceId, device.name)
this.isConnected = true
// #endif
},
async onCreateComponent() {
// #ifdef APP-PLUS
const status = printController.getStatus()
await printController.createPrintComponent(status.deviceId, status.deviceName)
this.isComponentCreated = true
// #endif
},
async onPrintText() {
// #ifdef APP-PLUS
await printController.printText(this.textContent)
// #endif
},
async onPrintQRCode() {
// #ifdef APP-PLUS
await printController.printQRCode('https://www.example.com')
// #endif
},
async onPrintBarcode() {
// #ifdef APP-PLUS
await printController.printBarcode('1234567890', BARCODE_CODE128)
// #endif
},
async onDisconnect() {
// #ifdef APP-PLUS
await printController.disconnect()
this.isConnected = false
this.isComponentCreated = false
// #endif
}
}
}
</script>
示例 4:复杂打印任务(EscPosBuilder)
// #ifdef APP-PLUS
import { EscPosBuilder, ALIGN_CENTER, ALIGN_LEFT } from '@/uni_modules/fxsup-PrintUTSModule'
// #endif
// #ifdef APP-PLUS
const builder = new EscPosBuilder()
builder.init()
.setAlign(ALIGN_CENTER)
.setBold(true)
.setTextSize(2, 2)
.addText('购物小票')
.setTextSize(0, 0)
.setBold(false)
.addText('------------------------------')
.setAlign(ALIGN_LEFT)
.addText('商品A ¥10.00')
.addText('商品B ¥25.00')
.addText('------------------------------')
.setAlign(2)
.setBold(true)
.addText('合计: ¥35.00')
.setBold(false)
.setAlign(ALIGN_CENTER)
.addQRCode('https://www.example.com', 5)
.addFeedLines(3)
.addCut()
const bytes = builder.build()
// 自行发送 bytes...
// #endif
常见问题
Q1:为什么蓝牙通信放在 JS 层而不是 UTS 插件里?
UTS(uni-app x)不支持蓝牙 BLE API(uni.createBLEConnection、uni.getBLEDeviceServices 等),这些 API 仅在传统 uni-app 的 JS/Vue 层可用。因此蓝牙通信必须在 JS 层实现,UTS 插件只负责 ESC/POS 指令生成。
Q2:printController 和 EscPosBuilder 有什么区别?
printController(JS 层):全功能控制器,包含蓝牙通信 + 打印指令生成 + 数据发送。业务层只需调用printController.printText()即可完成打印。EscPosBuilder(UTS 插件):纯指令构建器,只生成指令字节序列(number[]),不涉及蓝牙通信。适合需要自行管理蓝牙连接的场景。
Q3:为什么指令函数返回 number[] 而不是 Uint8Array?
在 uni-app 中,UTS 插件返回 ArrayBuffer 或 Uint8Array 时,JS 层可能无法正确识别其类型。number[] 跨层传递稳定可靠,print-controller.js 内部自动转换为 Uint8Array 发送。
Q4:插件支持哪些打印机?
支持所有兼容 ESC/POS 指令集的蓝牙打印机,包括但不限于:
- 汉印 HPRT MPT-III 系列(MPT-III/BL 蓝牙版)
- 爱普生 Epson TM 系列
- 其他兼容 ESC/POS 的热敏打印机
Q5:为什么打印前要先 createPrintComponent?
createPrintComponent 确保蓝牙连接已建立、服务和特征值已发现。没有可写特征值,打印数据无法发送。
Q6:中文打印乱码怎么办?
printController.printText 内部已自动处理中文编码:
- 优先:使用 GBK 编码(通过 Android 原生
java.lang.String.getBytes('GBK')),适用于大多数中文热敏打印机 - 回退:GBK 不可用时使用 UTF-8 编码
如仍乱码,请确认打印机固件支持的字符集(GBK/GB2312/UTF-8)。
Q7:蓝牙分包大小如何设置?
writeData 使用固定 20 字节分包(BLE 默认 MTU=23,最大载荷 20 字节)+ writeNoResponse 写入模式。发送间隔:
- 普通指令(文字/二维码/条码):30ms
- 图片数据:5ms(
writeNoResponse不需要等对端 ACK)
注意:该打印机特征值仅支持
writeNoResponse,writeType固定为 1。分包大小超过 20 字节会报property not support (code:10007)错误。
Q8:图片打印速度慢怎么办?
图片数据量大(如 576×533 ≈ 38KB),20 字节分包需发送约 1920 包。优化措施:
- 图片发送间隔为 5ms(普通指令为 30ms)
- 优先使用
sizeType: ['compressed']选择压缩后的图片 - 降低图片分辨率(减小宽度和高度)
- 图片二值化后大部分白色区域会编码为 0x00,但传输字节数不变
Q9:非 App 平台(H5/小程序)能用吗?
蓝牙通信功能仅支持 App 平台(#ifdef APP-PLUS)。UTS 插件的指令生成功能可在所有平台使用,但需要自行实现蓝牙通信。
Q10:UTS 插件新增函数如何生效?
UTS 插件新增/修改函数后,需要重新编译运行(Run),热更新不会重新编译 UTS 插件。如仅修改 JS 文件(print-controller.js),热更新即可生效。

收藏人数:
购买普通授权版(
试用
使用 HBuilderX 导入示例项目
赞赏(0)
下载 53
赞赏 0
下载 12289366
赞赏 1922
赞赏
京公网安备:11010802035340号