更新记录

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.createBLEConnectionuni.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[] 状态查询指令

注意buildTextFromBytesbuildImage 为 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.createBLEConnectionuni.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 插件返回 ArrayBufferUint8Array 时,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)

注意:该打印机特征值仅支持 writeNoResponsewriteType 固定为 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),热更新即可生效。

隐私、权限声明

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

android.permission.BLUETOOTH

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

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

暂无用户评论。