基于Native.js 实现的连接蓝牙打印机
- 打印效果图
- 核心代码
- 测试代码
- 运行设备及环境
- PS:
- PPS:
- Demo
打印效果图
核心代码
/**
* @Description: 蓝牙打印类 基于h5+ Native.js
* @Author: EricLee
* @Date: 2020-10-14 13:53:23
* @Param: mod
* @Return: $
*/
export const Bluetooth = function () {
// 全局 变量
let main = null,
BluetoothAdapter = null,
UUID = null,
uuid = null,
BAdapter = null,
device = null,
bluetoothSocket = null,
outputStream = null,
OutputStreamWriter = null,
writer = null
this.status = 0 // 和设备的连接状态: 0 未连接 1 连接中 2 已连接 (可以打印) 注: ** 此状态不是 手机蓝牙和设备的配对状态 **
// 初始化
this.initState = function () {
main = plus.android.runtimeMainActivity()
BluetoothAdapter = plus.android.importClass('android.bluetooth.BluetoothAdapter')
BAdapter = BluetoothAdapter.getDefaultAdapter()
UUID = plus.android.importClass('java.util.UUID')
uuid = UUID.fromString('00001101-0000-1000-8000-00805F9B34FB')
this.status = 1
this.queryBindDevice()
}
// 获取配对设备 mac地址
this.queryBindDevice = function () {
var lists = BAdapter.getBondedDevices()
plus.android.importClass(lists)
// var resultDiv = document.getElementById('bluetooth_list')
var iterator = lists.iterator()
plus.android.importClass(iterator)
console.log('==> 设备列表长度', lists.size())
if (lists.size() == 0) {
mui.toast('连接失败!未检测到已配对成功的设备!')
return
}
if (lists.size() > 1) {
mui.toast('连接失败!检测到多个配对成功设备!')
return
}
while (iterator.hasNext()) {
var d = iterator.next()
plus.android.importClass(d)
console.log(d.getAddress())
console.log(d.getName())
this.createConnect(d.getAddress()) // 创建连接
}
}
// 建立连接
this.createConnect = function (mac) {
if (!mac) {
mui.toast('连接失败!未能获取设备MAC地址!')
this.status = 0
return
}
device = BAdapter.getRemoteDevice(mac) // 连接打印机
plus.android.importClass(device)
// 只需建立一次连接,多次调用不能正常打印 !!!
bluetoothSocket = device.createInsecureRfcommSocketToServiceRecord(uuid)
plus.android.importClass(bluetoothSocket)
if (!bluetoothSocket.isConnected()) {
console.log('断开了,需要重新连接,连接中')
bluetoothSocket.connect()
}
mui.toast('打印机已准备就绪,可以打印!')
this.status = 2
// 注册打印类
outputStream = bluetoothSocket.getOutputStream()
plus.android.importClass(outputStream)
OutputStreamWriter = plus.android.importClass('java.io.OutputStreamWriter')
writer = new OutputStreamWriter(outputStream, 'GBK')
plus.android.importClass(writer)
}
// 关闭IO 关闭连接
// 关闭页面时必需调用该方法,要不下次不能正常连接设备 !!!
this.closeConnect = function () {
bluetoothSocket.close()
outputStream.close()
OutputStreamWriter.close()
bluetoothSocket = null
outputStream = null
OutputStreamWriter = null
device = null
this.status = 0
}
// 走纸 n = 点行数
this.feedPoint = function (n) {
const point = n || 8
writer.write(0x1B)
writer.write(0x4A)
writer.write(point) // 点行 8 点 = 1mm
writer.flush()
}
// 走纸 n = 行数
this.feedLine = function (n) {
const line = n || 1
writer.write(0x1B)
writer.write(0x64)
writer.write(line) // 行数
writer.flush()
}
// 设置左边距
this.setLeftMargin = function (n, m) {
writer.write(0x1D)
writer.write(0x4C)
writer.write(n) // 行数
writer.write(m) // 行数
writer.flush()
}
// 打印空行 linNum 行数
this.printLine = function (lineNum) {
for (let i = 0; i < lineNum; i++) {
writer.write('\n')
}
writer.flush()
}
// 设置打印 位置 // 0 右 1 中 2 右
this.setPrintPosition = function (n) {
let m = n || 1
writer.write(0x1B)
writer.write(0x61)
writer.write(m) // 0 右 1 中 2 右
writer.flush()
}
// 设置绝对打印位置
this.setPrintLocation = function (light, weight) {
writer.write(0x1B)
writer.write(0x24)
writer.write(light) // 0≤ light ≤ 255
writer.write(weight) // 0≤ weight ≤ 2
writer.flush()
}
// 打印空白(一个Tab的位置,约4个汉字)
this.printTabSpace = function (n) {
for (let i = 0; i < n; i++) {
writer.write('\t')
}
writer.flush()
}
// 设置/解除字符旋转模式
// 0解除旋转模式 1设置90°顺时针旋转模式 2设置180°顺时针旋转模式 3设置270°顺时针旋转模式
this.setPrintRotate = function (n) {
writer.write(0x1B)
writer.write(0x56)
writer.write(n)
writer.flush()
}
// 打印位图 todo
this.printBitmap = function (m, data) {
writer.write(0x1B)
writer.write(0x2A)
writer.write(m)
writer.write(data)
}
// 字符缩放
this.setCharacterScale = function (n) {
// 打印倍宽高
if (n == 1) {
writer.write(0x1B)
writer.write(0x21)
writer.write(16)
writer.flush()
writer.write(0x1B)
writer.write(0x21)
writer.write(32)
writer.flush()
} else {
writer.write(0x1B)
writer.write(0x21)
writer.write(0)
writer.flush()
}
}
// 打印初始化 每次打印前必须调用!!!
this.initPrinter = function () {
writer.write(0x1B)
writer.write(0x40)
writer.flush()
}
// 打印文字 并换行
this.printTextNewLine = function (byteStr) {
if (!main) {
mui.toast('设备未进行配对!')
return
}
var bytes = plus.android.invoke(byteStr, 'getBytes', 'gbk')
console.log(bytes)
outputStream.write(bytes)
outputStream.flush()
// 换行
writer.write('\n')
writer.flush()
console.log('print ')
}
// 打印字符串方法 byteStr 只能是字符串
this.printText = function (byteStr, l, w) {
if (!main) {
mui.toast('设备未进行配对!')
return
}
var bytes = plus.android.invoke(byteStr, 'getBytes', 'gbk')
console.log(bytes)
outputStream.write(bytes)
outputStream.flush()
console.log('print ')
// device = null
}
/**
* @Description: 二维码打印
* @Author: EricLee
* @Date: 2020-10-15 15:16:10
* @Param: byteStr {String} 要打印的内容
* @Return: void
*/
this.printQrcode = function (byteStr) {
if (!main) {
mui.toast('设备未进行配对!')
return
}
// init
var moduleSize = 8
var bytes = plus.android.invoke(byteStr, 'getBytes', 'gbk')
var length = bytes.length
console.log(length)
// 缓存二维码数据
writer.write(0x1D)// init
writer.write('(k')// adjust height of barcode
writer.write(length + 3) // pl
writer.write(0) // ph
writer.write(49) // cn
writer.write(80) // fn
writer.write(48) //
writer.write(byteStr)
// 二维码纠错等级
writer.write(0x1D)
writer.write('(k')
writer.write(3)
writer.write(0)
writer.write(49)
writer.write(69)
writer.write(48)
// 设置二维码块大小
writer.write(0x1D)
writer.write('(k')
writer.write(3)
writer.write(0)
writer.write(49)
writer.write(67)
writer.write(moduleSize)
// 打印已缓存的数据二维码
writer.write(0x1D)
writer.write('(k')
writer.write(3) // pl
writer.write(0) // ph
writer.write(49) // cn
writer.write(81) // fn
writer.write(48) // m
writer.flush()
// 二维码打印 结束
console.log('print Qrcode')
}
}
测试代码
<template>
<div>
<div>
<Button @click="_initBluetooth">{{ statusList[status] }}</Button>
</div>
<div>
<!-- <Button @click="_printText('DC:0D:30:9B:AC:99')">打印</Button>-->
<Button @click="_printTest(msg)">打印</Button>
<br/>
<Button @click="_printQrcode(code)">打印二维码</Button>
</div>
<div>
<br/>
<Input v-model="line" placeholder="走纸行数" />
<Button @click="feed(line)">走纸</Button>
</div>
<div>
<br/>
<Input v-model="marginNum" placeholder="定位" />
<Button @click="_setPrintPosition(marginNum)">定位</Button>
</div>
<div>
<br/>
<Button @click="_setCharacterScale(1)">放大</Button>
<Button @click="_setCharacterScale(0)">缩小</Button>
</div>
<div>
<br/>
<Input v-model="light" placeholder="light" />
<Input v-model="weight" placeholder="weight" />
<Button @click="_setPrintLocation(light,weight)">绝对位置</Button>
</div>
<div>
<br/>
<Input v-model="rotateNum" placeholder="旋转" />
<Button @click="_setPrintRotate(rotateNum)">旋转</Button>
</div>
<div>
<br/>
<Button @click="_closeConnect()">关闭连接</Button>
</div>
</div>
</template>
<script>
import {Bluetooth} from '../lib/bluetooth'
export default {
name: 'printTest',
data () {
return {
msg: '样品内容\n' + '101013Q73898\n' + '2020-10-10 09:33:33\n' + '张三三\n',
code: '191013Q7398',
mac: '',
line: 8,
light: 0,
weight: 0,
rotateNum: 0,
marginNum: 1,
initFlag: false,
bluetoothPrinter: null,
text: '配对',
status: 0,
statusList: [
'待连接',
'连接中',
'已连接'
]
}
},
mounted () {
this._initBluetooth()
},
destroyed () {
this._closeConnect()
},
watch: {
status () {
console.log('status==>', this.status)
if (this.status == 2) {
this._loading(false)
}
}
},
methods: {
_loading (flag) {
if (flag) {
this.$vux.loading.show({
text: 'Loading'
})
setTimeout(() => {
this.$vux.loading.hide()
}, 5000)
} else {
this.$vux.loading.hide()
}
},
// 初始化并配对设备
_initBluetooth () {
if (!this.bluetoothPrinter) {
this.bluetoothPrinter = new Bluetooth()
this._loading(true)
console.log(this.bluetoothPrinter.status)
}
this.bluetoothPrinter.initState()
this.status = this.bluetoothPrinter.status
},
_printTest (msg) {
this.bluetoothPrinter.initPrinter()
this.bluetoothPrinter.setPrintPosition(1) // 居中 打印
this.bluetoothPrinter.printQrcode(msg)
this.bluetoothPrinter.feedPoint(20)
this.bluetoothPrinter.printTextNewLine('样品111')
this.bluetoothPrinter.printTextNewLine('101013Q73898')
this.bluetoothPrinter.printTextNewLine('2020-10-10 09:33:33')
this.bluetoothPrinter.printTextNewLine('张三三')
this.bluetoothPrinter.printLine(3)
},
_printQrcode (msg) {
this.bluetoothPrinter.initPrinter()
this.bluetoothPrinter.printTabSpace(10)
// this.bluetoothPrinter.setPrintPosition(2)
this.bluetoothPrinter.printQrcode(msg)
},
feed (n) {
this.bluetoothPrinter.feedPoint(n)
},
_closeConnect () {
if (this.bluetoothPrinter) {
this.bluetoothPrinter.closeConnect()
}
},
_setPrintLocation (l, w) {
this.bluetoothPrinter.setPrintLocation(l, w)
},
_setPrintRotate (l) {
this.bluetoothPrinter.setPrintRotate(l)
},
_setCharacterScale (l) {
this.bluetoothPrinter.setCharacterScale(l)
},
_setPrintPosition (l) {
this.bluetoothPrinter.setPrintPosition(l)
}
}
}
</script>
<style scoped>
</style>
运行设备及环境
IDE:Hbuilder X 2.8.13
测试机型:红米 note4 android6.0
测试打印机:科密PB8001
打印指令类型:ESC/POS ESC/POS指令参考文档
PS:
本文未提供连接蓝牙设备的方法,如有需要请点传送门 —» H5+连接蓝牙打印机
PPS:
只初始化一次即可持续打印实现
可以把 new Bluetooth() 的实例的初始化放到store中进行管理,在需要的组件注入实例。代码如下:
// store
const printer = {
namespaced: true,
state: {
bluetoothPrinter: null
},
mutations: {
setBluetoothPrinter (state, payload) {
state.bluetoothPrinter = payload
}
}
}
// component
export default {
data () {
return {
pagination: {
page: 0,
rows: 15
},
bottomList: [
{name: '添加', color: '#0D55A7'},
{name: '提交', color: '#11C827'},
{name: '标签打印', color: '#F7AC0C'}
],
records: [],
recordsList: [],
printData: [],
refresh: false, // 上拉刷新
loading: false, // 下拉加载
finished: false, // 是否获取到了所有数据
submitStatus: false,
status: 0
}
},
computed: {
...mapState('printer', {
bluetoothPrinter: 'bluetoothPrinter'
})
},
watch: {
status () {
console.log('status==>', this.status)
if (this.status == 2) {
this._loading(false)
}
}
},
mounted () {
this._initBluetooth()
},
destroyed () {
console.log('destroyed!')
// this._closeConnect()
},
methods: {
_add () {
this.$store.commit('steel/removeInsSteelItem')
this.$jump('typeA-insScrapSteelContractEdit', {sampleCode: '添加/编辑基本信息'})
},
// 全选
_allCheck () {
if (this.records.length === this.recordsList.length) {
this.records = []
return
}
this.records = this.recordsList.map(item => item.id)
},
// 提交
async _submitById () {
if (!this.records.length) {
this.$toast('至少选择一个')
return
}
if (this.submitStatus) return
this.submitStatus = true
this.$vux.loading.show({
text: '正在提交中...'
})
const result = await waitInsScrapSteelSubmit(this.records)
if (result) {
this._resultChange('提交成功!')
this.$vux.loading.hide()
} else {
this._resultChange('网络问题,请重新提交!')
this.$vux.loading.hide()
}
},
_resultChange (msg) {
this.$toast(msg)
this._onRefresh()
},
// 上拉刷新
async _onRefresh () {
this.pagination.page = 0
this.recordsList = []
this.printData = []
await this._onLoad()
this.refresh = false
},
// 下拉加载
async _onLoad () {
this.pagination.page++
await this._waitInsScrapSteel()
this.loading = false
return true
},
_loading (flag) {
if (flag) {
this.$vux.loading.show({
text: '设备配对中'
})
setTimeout(() => {
this.$vux.loading.hide()
}, 5000)
} else {
this.$vux.loading.hide()
}
},
// 初始化并配对设备
_initBluetooth () {
if (!this.bluetoothPrinter) {
const obj = new Bluetooth()
this._loading(true)
this.$store.commit('printer/setBluetoothPrinter', obj)
console.log(this.bluetoothPrinter.status)
this.bluetoothPrinter.initState()
this.status = this.bluetoothPrinter.status
}
},
_closeConnect () {
if (this.bluetoothPrinter) {
this.bluetoothPrinter.closeConnect()
}
},
// 待打印数据
_selectData () {
if (!this.records.length) {
this.$toast('至少选择一个')
return false
} else {
const {records, recordsList} = this
this.printData = []
let data = []
for (let i = 0; i < records.length; i++) {
for (let j = 0; j < recordsList.length; j++) {
if (records[i] === recordsList[j].id) {
data.push(recordsList[j])
}
}
}
this.printData = [...data]
}
},
// 打印方法
_print (data) {
this.bluetoothPrinter.initPrinter()
this.bluetoothPrinter.setPrintPosition(1) // 居中 打印
this.bluetoothPrinter.printQrcode(data.sampleCode) // 二维码 样品编号
this.bluetoothPrinter.feedPoint(20)
this.bluetoothPrinter.printTextNewLine(data.name) // 物料名称
this.bluetoothPrinter.printTextNewLine(data.sampleCode) // 二维码 样品编号
this.bluetoothPrinter.printTextNewLine(this.$getTime(data.obtainSampleTime, true)) // 取样时间
this.bluetoothPrinter.printTextNewLine(data.obtainSampler) // 取样人
this.bluetoothPrinter.feedLine(4)
},
// 打印标签
_printLabel () {
this._selectData()
const {printData} = this
const len = printData.length
for (let i = 0; i < len; i++) {
this._print(printData[i])
}
},
_dataBack (msg) {
switch (msg) {
case '添加':
this._add()
break
case '提交':
this._submitById()
break
case '标签打印':
this._printLabel()
break
}
}
}
}
Demo
github地址:H5-bluetooth