Skip to main content

Printer 打印能力聚合

@rydeen/printer 是通用打印机聚合组件,屏蔽 网络、蓝牙、串口、USB、虚拟蓝牙、海石 POS 等不同打印方式的差异,对外提供统一的 PrinterDriver 接口,方便在门店终端、收银机、一体机等场景中按配置切换打印机类型。

设计定位​

  • 统一抽象:所有打印机类型都实现同一套接口(initialize / scan / connect / print / disconnect / isConnected / isClosed ...)。
  • 按需依赖:本组件只聚合接口,具体驱动通过 peerDependencies 交给业务项目按需安装。
  • 多终端形态:兼容 Android、Electron(网络/串口/USB)以及特定厂商 POS 设备(如商米虚拟蓝牙、海石 POS)。

安装与依赖​

npm install @rydeen/printer

@rydeen/printer 不直接内置具体驱动实现,而是通过 peerDependencies 依赖以下驱动插件。
业务工程需要根据实际使用的打印类型(PrinterType)按需安装对应依赖,未安装的驱动在运行时会报模块未找到错误。

按需安装的驱动插件(示例):

  • 网络打印:@capacitor-rydeen/network-printer
  • 蓝牙打印:@capacitor-community/bluetooth-le(以及你的蓝牙打印实现)
  • 虚拟蓝牙打印(商米等):@capacitor-rydeen/sunmi-virtual-printer
  • 海石 POS:@capacitor-rydeen/histonepos
  • USB 打印:@capacitor-rydeen/usb-printer
  • 串口打印(Electron/Node):serialport

例如,只需要「网络 + USB 打印」:

npm install @rydeen/printer \
@capacitor-rydeen/network-printer \
@capacitor-rydeen/usb-printer

支持的打印类型​

打印类型 (PrinterType)依赖插件/库典型平台说明
NETWORK@capacitor-rydeen/network-printerAndroid / iOS / Electron通过 TCP/IP 直连网络打印机(如 9100 端口)。
BLUETOOTH@capacitor-community/bluetooth-leAndroid / iOS / Web*低功耗蓝牙打印机。
SERIALserialportElectron / Node串口打印机(COM / /dev/tty*)。
USB@capacitor-rydeen/usb-printerAndroid / ElectronUSB 打印机(iOS 暂不支持实际打印)。
VIRTUAL_BLUETOOTH@capacitor-rydeen/sunmi-virtual-printerAndroid POS商米等虚拟蓝牙打印。
HISTONEPOS@capacitor-rydeen/histoneposAndroid POS海石 POS 内置打印机。

Web*:蓝牙打印在 Web 端依赖浏览器对 Web Bluetooth 的支持。

核心概念与接口​

PrinterFactory 与 PrinterDriver​

import { PrinterFactory, PrinterType, type NetworkPrinterOptions } from '@rydeen/printer'

const factory = PrinterFactory.getInstance()
const driver = factory.create(PrinterType.NETWORK)
  • PrinterFactory:单例工厂,根据 PrinterType 创建具体驱动。
  • PrinterDriver<T extends PrinterOptions>:所有具体驱动实现的统一接口,方法签名见下文「完整类型定义」。

API 与完整类型定义​

以下接口与类型与 @rydeen/printer 源码中的 src/types/definitions.d.ts 保持一致。

PrinterDriver 接口​

export interface PrinterDriver<T extends PrinterOptions> {
/**
* 初始化打印机服务(可能会申请权限,建议在需要蓝牙的页面再调用,避免送审被拒)
* @returns 初始化结果
*/
initialize(options: T): Promise<PrinterResult>

/** 停止扫描 */
stopScan(): Promise<void>

/**
* 扫描打印机
* @returns 扫描是否执行成功
*/
scan(): Promise<PrinterResult>

/**
* 连接打印机
* @returns 连接是否完成
*/
connect(): Promise<PrinterResult>

/**
* 断开打印机
* @returns 断开是否完成
*/
disconnect(): Promise<PrinterResult>

/** 检查打印机是否已连接 */
isConnected(): Promise<boolean>

/** 检查打印机是否已关闭 */
isClosed(): Promise<boolean>

/**
* 打印数据(字节数组,如 ESC/POS 指令)
* @param data 打印数据
* @returns 是否打印成功
*/
print(data: number[]): Promise<PrinterResult>

/**
* 获取/绑定信号强度监听(如蓝牙 RSSI)
* @returns 监听器绑定结果
*/
signal(): Promise<PrinterResult>

/** 获取设备详细信息 */
getDeviceInfo(): Promise<DeviceInfo>

/** 根据设备名称获取设备(部分驱动支持) */
getDeviceByName(): Promise<PrinterResult>
}

结果与设备信息类型​

export interface PrinterResult {
/** 执行是否成功 */
success: boolean
/** 错误或提示信息 */
message: string
/** 蓝牙相关:服务列表 */
bleServices?: BleService[]
/** 蓝牙相关:设备信息 */
bleDevice?: BleDevice
}

export interface DeviceInfo {
/** 本结果是否有效 */
valid: boolean
/** 设备 ID */
deviceId: string
/** 设备名称 */
deviceName: string
/** 设备序列号 */
serialNumber: string
/** 设备软件版本 */
softwareVersion: string
/** 设备硬件版本 */
hardwareVersion: string
/** 设备芯片供应商 */
manufacturer: string
/** 设备芯片型号 */
modelNumber: string
/** 固件版本 */
firmwareVersion: string
}

BleService、BleDevice 来自 @capacitor-community/bluetooth-le。

打印机选项类型(PrinterOptions 及派生)​

export interface PrinterOptions {
/** 打印机类型(PrinterType 枚举) */
type: PrinterType
}

export interface NetworkPrinterOptions extends PrinterOptions {
/** 打印机 IP 地址 */
ip: string
/** 打印机端口(常见 9100) */
port: number
}

export enum WriteMode {
/** 标准写:单次一致性,效率中等【默认】 */
WRITE = 'write',
/** 无应答写:效率最高,不保证接收 */
WRITE_WITHOUT_RESPONSE = 'writeWithoutResponse',
/** 可靠写:整体一致性,效率最低 */
RELIABLE_WRITE = 'reliableWrite'
}

export interface BluetoothPrinterOptions extends PrinterOptions {
/** 查找用的蓝牙服务 UUID 列表 */
scanServiceUUIDs?: string[]
/** 蓝牙设备扫描结果回调 */
scanNotification?: (scanResult: ScanResult) => void
/** 连接用的蓝牙服务 UUID */
serviceUUID?: string
/** 写方式(默认 WriteMode.WRITE) */
writeMode?: WriteMode
/** 写特征 UUID */
characteristicUUID?: string
/** 设备 ID(Android 多为 MAC,iOS 为设备 UUID) */
deviceId?: string
/** 设备名称 */
deviceName?: string
/** 按服务 UUID 过滤设备(128 位 UUID 字符串) */
services?: string[]
/** 按设备名称过滤 */
name?: string
/** 按设备名称前缀过滤 */
namePrefix?: string
/** Web 平台需声明将使用的服务列表 */
optionalServices?: string[]
/** 是否允许重复广播(仅 requestLEScan) */
allowDuplicates?: boolean
/** Android 扫描模式(默认 ScanMode.SCAN_MODE_BALANCED) */
scanMode?: ScanMode
/** 扫描总时长(秒,默认 60) */
scanDuration?: number
/** 连接超时时长(秒,默认 10) */
connectTimeout?: number
/**
* 是否需要响应(默认 false)
* true:等打印机响应再发下一包,完整但较慢
* false:直接发送,快但信号差时可能丢包
*/
withResponse?: boolean
/** 打印机连接状态/信号强度监听器 */
listener?: BleSignalMonitorListener
}

export interface SerialPortPrinterOptions extends PrinterOptions {
/** 串口设备路径(如 /dev/ttyS0、COM1) */
comPath: string
/** 串口波特率(如 9600、115200) */
baudRate: number
}

export interface UsbPrinterOptions extends PrinterOptions {
/** USB 供应商 ID(Vendor ID) */
vendorId: number
/** USB 产品 ID(Product ID) */
productId: number
}

ScanMode、ScanResult 来自 @capacitor-community/bluetooth-le。

监听器类型​

import { SignalStrength } from '../enums/signal-strength'

/**
* 蓝牙信号强度监听器
* @param deviceId 设备 ID
* @param rssi 原始信号强度(dBm)
* @param strength 归一化后的信号强度枚举
*/
export type BleSignalMonitorListener = (
deviceId: string,
rssi: number,
strength: SignalStrength
) => void

方法列表摘要​

方法签名说明
initializeinitialize(options: T): Promise<PrinterResult>初始化打印机服务,可能触发权限申请。
stopScanstopScan(): Promise<void>停止扫描。
scanscan(): Promise<PrinterResult>扫描打印机。
connectconnect(): Promise<PrinterResult>连接打印机。
disconnectdisconnect(): Promise<PrinterResult>断开打印机。
isConnectedisConnected(): Promise<boolean>是否已连接。
isClosedisClosed(): Promise<boolean>是否已关闭。
printprint(data: number[]): Promise<PrinterResult>发送打印数据(字节数组)。
signalsignal(): Promise<PrinterResult>获取/绑定信号强度监听。
getDeviceInfogetDeviceInfo(): Promise<DeviceInfo>获取设备详情。
getDeviceByNamegetDeviceByName(): Promise<PrinterResult>按设备名称获取设备。

各类型打印机 API 方法可用性​

以下表格表示各 PrinterType 对上述方法的支持情况:✅ 支持(有实际能力)、⚠️ 占位(可调用但无实质能力或返回固定/无效结果)、❌ 不支持(调用会抛错或明确返回失败)。

方法NETWORKBLUETOOTHSERIALUSBVIRTUAL_BLUETOOTHHISTONEPOS
initialize✅✅✅✅✅✅
stopScan⚠️ 空实现✅⚠️ 空实现⚠️ 空实现✅ 继承蓝牙⚠️ 空实现
scan✅✅ 扫描周边设备✅ 枚举串口✅ 枚举 USB 设备✅ 继承蓝牙⚠️ 占位(无需扫描)
connect✅✅✅✅✅✅
disconnect✅✅✅✅✅✅
isConnected✅✅✅✅✅✅
isClosed✅✅✅✅✅✅
print✅✅✅✅✅✅
signal⚠️ 可调用无实质✅ 真实 RSSI 监听❌ 不支持❌ 不支持⚠️ 返回成功无监测❌ 未实现(抛错)
getDeviceInfo✅✅✅✅✅ 固定信息⚠️ 返回 valid: false
getDeviceByName✅✅✅✅✅⚠️ 返回成功但提示不支持

实际行为以各驱动源码为准;选型时若依赖 signal 或按名称取设备,请优先选用 BLUETOOTH / NETWORK 等标注为支持的类型。

快速开始​

网络打印机示例​

import {
PrinterFactory,
PrinterType,
type NetworkPrinterOptions,
} from '@rydeen/printer'

async function printByNetwork() {
const factory = PrinterFactory.getInstance()
const driver = factory.create(PrinterType.NETWORK)

const options: NetworkPrinterOptions = {
type: PrinterType.NETWORK,
ip: '192.168.1.100',
port: 9100,
}

await driver.initialize(options)
await driver.scan()

const conn = await driver.connect()
if (!conn.success) {
console.error('网络打印机连接失败:', conn.message)
return
}

// 假设 data 是 ESC/POS 指令字节数组
const data: number[] = [0x1b, 0x40 /* ... */]
const result = await driver.print(data)
console.log('打印结果:', result.success, result.message)

await driver.disconnect()
}

USB 打印机示例​

import {
PrinterFactory,
PrinterType,
type UsbPrinterOptions,
} from '@rydeen/printer'

async function printByUsb() {
const factory = PrinterFactory.getInstance()
const driver = factory.create(PrinterType.USB)

const options: UsbPrinterOptions = {
type: PrinterType.USB,
vendorId: 0x1234,
productId: 0x5678,
}

await driver.initialize(options)

const scanResult = await driver.scan()
if (!scanResult.success) {
console.error('USB 扫描失败:', scanResult.message)
return
}

const conn = await driver.connect()
if (!conn.success) {
console.error('USB 连接失败:', conn.message)
return
}

const data: number[] = [0x1b, 0x40 /* ... */]
const result = await driver.print(data)
console.log('USB 打印结果:', result.success, result.message)

await driver.disconnect()
}

实战示例:门店多打印方式回退​

场景:门店既有网络打印机又有 USB 打印机,当网络打印失败时自动回退到 USB 打印,避免影响收银。

import { PrinterFactory, PrinterType } from '@rydeen/printer'

const factory = PrinterFactory.getInstance()

export async function safePrintTicket(data: number[]) {
// 1. 优先网络打印
const network = factory.create(PrinterType.NETWORK)
try {
// 省略 options 注入
const res = await network.print(data)
if (res.success) return
} catch (_) {
// 忽略异常,进入回退逻辑
}

// 2. 回退到 USB 打印
const usb = factory.create(PrinterType.USB)
const res2 = await usb.print(data)
if (!res2.success) {
throw new Error(`票据打印失败: ${res2.message}`)
}
}

使用建议​

  • 驱动按需安装:未安装的插件在运行时会抛出模块找不到错误,需要在业务中做好检测与提示。
  • 调用顺序:典型流程为 initialize → scan → connect → print → disconnect,推荐在错误时也调用一次 disconnect() 清理资源。
  • 数据格式统一:print(data: number[]) 接收字节数组,建议在业务层封装「模板 → ESC/POS 字节」的转换逻辑。