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-printer | Android / iOS / Electron | 通过 TCP/IP 直连网络打印机(如 9100 端口)。 |
BLUETOOTH | @capacitor-community/bluetooth-le | Android / iOS / Web* | 低功耗蓝牙打印机。 |
SERIAL | serialport | Electron / Node | 串口打印机(COM / /dev/tty*)。 |
USB | @capacitor-rydeen/usb-printer | Android / Electron | USB 打印机(iOS 暂不支持实际打印)。 |
VIRTUAL_BLUETOOTH | @capacitor-rydeen/sunmi-virtual-printer | Android POS | 商米等虚拟蓝牙打印。 |
HISTONEPOS | @capacitor-rydeen/histonepos | Android 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
方法列表摘要
| 方法 | 签名 | 说明 |
|---|---|---|
| initialize | initialize(options: T): Promise<PrinterResult> | 初始化打印机服务,可能触发权限申请。 |
| stopScan | stopScan(): Promise<void> | 停止扫描。 |
| scan | scan(): Promise<PrinterResult> | 扫描打印机。 |
| connect | connect(): Promise<PrinterResult> | 连接打印机。 |
| disconnect | disconnect(): Promise<PrinterResult> | 断开打印机。 |
| isConnected | isConnected(): Promise<boolean> | 是否已连接。 |
| isClosed | isClosed(): Promise<boolean> | 是否已关闭。 |
print(data: number[]): Promise<PrinterResult> | 发送打印数据(字节数组)。 | |
| signal | signal(): Promise<PrinterResult> | 获取/绑定信号强度监听。 |
| getDeviceInfo | getDeviceInfo(): Promise<DeviceInfo> | 获取设备详情。 |
| getDeviceByName | getDeviceByName(): Promise<PrinterResult> | 按设备名称获取设备。 |
各类型打印机 API 方法可用性
以下表格表示各 PrinterType 对上述方法的支持情况:✅ 支持(有实际能力)、⚠️ 占位(可调用但无实质能力或返回固定/无效结果)、❌ 不支持(调用会抛错或明确返回失败)。
| 方法 | NETWORK | BLUETOOTH | SERIAL | USB | VIRTUAL_BLUETOOTH | HISTONEPOS |
|---|---|---|---|---|---|---|
| initialize | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| stopScan | ⚠️ 空实现 | ✅ | ⚠️ 空实现 | ⚠️ 空实现 | ✅ 继承蓝牙 | ⚠️ 空实现 |
| scan | ✅ | ✅ 扫描周边设备 | ✅ 枚举串口 | ✅ 枚举 USB 设备 | ✅ 继承蓝牙 | ⚠️ 占位(无需扫描) |
| connect | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| disconnect | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| isConnected | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| isClosed | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | |
| 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 字节」的转换逻辑。