更新记录

1.0.0(2026-10-14) 下载此版本

  • 首次发布
  • 提供商米内置扫码头、内置打印机、设备序列号适配
  • 依赖 pro-device-core

平台兼容性

uni-app(5.06)

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

pro-device-adp-sunmi — 商米硬件适配层

商米(Sunmi)设备硬件适配层 UTS 插件,封装三类原生能力:

能力 说明
设备序列号 读取商米设备 SN
内置扫码头 注册/注销红外扫码广播回调
内置打印机 连接、检查、原始打印原语(供业务层组合标签)

依赖:pro-device-core、pro-device-print(仅类型依赖)
平台:APP-PLUS(Android,minSdkVersion 21)
AAR:printerx-1.0.17.aar(Sunmi PrinterX SDK)


架构定位

pro-device-adp-sunmi (UTS 原生层)
    ↓ 被项目层 import
src/devices/scanner/sunmi.ts       → SunmiScanner
src/devices/terminal/sunmi.ts      → SunmiTerminal
src/devices/printer/sunmi/built-in.ts → SunmiBuiltInPrinter (SDKPrinterBase<LineDocument>)
    ↓
src/devices/printer/documents/       → 业务版式(按 PrintJob;商米用 LineDocument)

本插件是原生能力提供层,不包含业务逻辑。业务标签实现对应画布接口,放在 src/devices/printer/documents/。


目录结构

pro-device-adp-sunmi/
  utssdk/
    interface.uts             — 公共 Options 类型
    unierror.uts              — UniErrorSubject / MyApiFailImpl
    app-android/
      AndroidManifest.xml     — 声明 Sunmi 打印机 AIDL Service 查询(Android 11+)
      config.json             — minSdkVersion: 21
      index.uts               — 统一入口,export * 全部公共函数
      device/
        sn.uts                — getDeviceSN()
      printer/
        built-in.uts          — BuiltInPrinter 单例 + 生命周期 + 打印原语
      scancode/
        infrared.uts          — InfraredScanCode BroadcastReceiver 单例
        interface.uts         — ScanResult / InfraredScanCodeOptions 类型
      libs/
        printerx-1.0.17.aar

API

设备序列号

import { getDeviceSN } from '@/uni_modules/pro-device-adp-sunmi'

// 读取商米设备 SN(通过 SystemProperties / Build 反射)
// 非商米设备返回空字符串
const sn = getDeviceSN()   // 例:'SM-2026XXXXXX'

内置扫码头

registerInfraredScanCode(options)

注册红外扫码广播回调。每次扫码触发 onData。

import {
  registerInfraredScanCode,
  unRegisterInfraredScanCode,
} from '@/uni_modules/pro-device-adp-sunmi'

// ── 注册 ──────────────────────────────────────────────────────
// #ifdef APP-PLUS
registerInfraredScanCode({
  onData: (result) => {
    console.log('扫码结果:', result.data)        // 条码/二维码内容字符串
    console.log('原始字节:', result.sourceByte)  // 十六进制字符串
  },
  success: () => console.log('扫码头注册成功'),
  fail: (err) => console.error('注册失败', err.errMsg),
})
// #endif

// ── 注销 ──────────────────────────────────────────────────────
// #ifdef APP-PLUS
unRegisterInfraredScanCode({
  success: () => console.log('扫码头已注销'),
})
// #endif

回调类型:

type ScanResult = {
  data       : string   // 扫码内容(条码/二维码文本)
  sourceByte : string   // 原始字节(大写十六进制字符串)
}

onData 在 Android 主线程回调,避免在回调中执行耗时操作。


内置打印机

生命周期

import {
  initPrinter,
  checkPrinter,
  destroyPrinter,
  printerBeginLabel,
  printerAddLine,
  printerAddLineCenter,
  printerAddQrCode,
  printerAddAutoOut,
  printerCommitPrint,
} from '@/uni_modules/pro-device-adp-sunmi'
函数 说明
initPrinter(options) 连接打印机,获取 PrinterX SDK 实例
checkPrinter(options) 检查打印机就绪状态(不重连)
destroyPrinter(options) 释放打印机资源
// ── 初始化 ──────────────────────────────────────────────────
// #ifdef APP-PLUS
await new Promise<void>((resolve, reject) =>
  initPrinter({ success: resolve, fail: (e) => reject(e.errMsg) })
)
// #endif

// ── 打印前检查 ────────────────────────────────────────────────
await new Promise<void>((resolve, reject) =>
  checkPrinter({ success: resolve, fail: (e) => reject(e.errMsg) })
)

// ── 释放资源(页面 onUnmounted) ──────────────────────────────
destroyPrinter({ success: () => {}, fail: () => {} })

原始打印原语

重要: printerBeginLabel() 到 printerCommitPrint() 之间的所有调用必须在同一个 JS 微任务中同步顺序执行,不能跨 await 分散(PrinterX 事务模型限制)。

// ✅ 正确:全部同步执行后再 await commit
printerBeginLabel()
printerAddLineCenter('危险废物转运凭证')
printerAddQrCode('HW08-2026-001', 150, 150)
printerAddLineCenter('HW08-2026-001')
printerAddLine('废物类型:废矿物油(HW08)')
printerAddLine('重量:3.2 KG')
printerAddLine('收集人:张三')
printerAddLine('日期:2026-05-12')
printerAddAutoOut()
await new Promise<void>((resolve, reject) =>
  printerCommitPrint({ success: resolve, fail: (e) => reject(e.errMsg) })
)

// ❌ 错误:中间有 await,会导致事务断裂
printerBeginLabel()
printerAddLine('第一行')
await somethingAsync()   // ← 禁止
printerAddLine('第二行')

完整打印流程示例

// src/devices/printer/sunmi/built-in.ts 的 _sdkPrint() 实现参考
async function printLabel(doc: LineDocument): Promise<void> {
  // 1. 检查打印机状态
  await new Promise<void>((resolve, reject) =>
    checkPrinter({ success: resolve, fail: (e) => reject(new Error(e.errMsg)) })
  )

  // 2. 同步执行打印事务
  const canvas = new SunmiPrinterCanvas()   // 实现 LineCanvas,缓存调用
  doc.render(canvas)                        // 业务层填充内容

  printerBeginLabel()
  for (const op of canvas.operations) op() // 回放所有 printerAdd* 调用
  printerAddAutoOut()

  // 3. 提交打印
  await new Promise<void>((resolve, reject) =>
    printerCommitPrint({ success: resolve, fail: (e) => reject(new Error(e.errMsg)) })
  )
}

在项目中使用

商米设备能力通过 app-device Store 统一管理,通常无需在业务组件中直接调用本包。

检查当前设备是否为商米终端

import { detectTerminalConfig } from '@/devices'

const config = detectTerminalConfig()
// config 非 null 且 config.brand === 'sunmi' → 商米终端
if (config?.brand === 'sunmi') {
  console.log('当前为商米设备,内置外设可用')
}

通过 Store 获取内置打印机

内置商米打印机实现 IPrintable<LineDocument> + IPrintJobSupport。 业务标签须先声明对应 PrintJob 并构造 LineDocument;危废预印标签不属于商米 Job。

import { useAppDeviceStore } from '@/plugins/store/app-device'

const appDeviceStore = useAppDeviceStore()

async function printTest() {
  const printer = appDeviceStore.printer
  if (!printer) {
    uni.showToast({ title: '打印机未初始化', icon: 'none' })
    return
  }
  try {
    await printer.printTest()
    uni.showToast({ title: '打印成功', icon: 'success' })
  } catch (e: any) {
    uni.showToast({ title: e?.message ?? '打印失败', icon: 'none' })
  }
}

通过 Store 获取内置扫码头

import { useAppDeviceStore } from '@/plugins/store/app-device'

const appDeviceStore = useAppDeviceStore()
let unsubScan: (() => void) | null = null

onMounted(() => {
  const scannerEntry = appDeviceStore.devices.find(d => d.type === 'scanner')
  const scanner = scannerEntry?.device
  if (!scanner) return

  unsubScan = (scanner as any).onScan?.((code: string) => {
    form.value.code = code
    uni.showToast({ title: `已扫:${code}`, icon: 'none' })
  })
})

onUnmounted(() => unsubScan?.())

注意事项

  1. 事务模型限制:printerBeginLabel 到 printerCommitPrint 之间不能有 await,所有原语调用必须同步完成。
  2. Android 11+ 权限:AndroidManifest.xml 中的 <queries> 声明了 woyou.aidlservice.jiuiv5,Android 11+ 必须有此声明才能绑定商米 AIDL 服务。
  3. checkPrinter 不重连:只检查连接状态。若返回失败,应重新调用 initPrinter。
  4. 主线程回调:registerInfraredScanCode.onData 在 Android 主线程触发,不要在回调中执行网络请求或重度计算。
  5. 非商米设备防御:在非商米设备上调用任何打印/扫码函数均会触发 fail,务必处理 fail 回调。

平台支持

平台 支持
APP-PLUS(Android 5.0+ / minSdk 21) √
APP-PLUS(iOS) ×
微信/支付宝小程序 ×
H5 ×

原生依赖

文件 说明
utssdk/app-android/libs/printerx-1.0.17.aar Sunmi PrinterX SDK,封装商米 AIDL 打印服务

此 AAR 已内置在插件目录中,HBuilderX 打包时自动引入,无需额外配置。

隐私、权限声明

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

商米打印、扫码硬件访问

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

插件不采集任何数据

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

无

许可协议

MIT协议

暂无用户评论。