天天看點

uni-app H5+ 連接配接藍牙列印機列印文字及二維碼

基于Native.js 實作的連接配接藍牙列印機

    • 列印效果圖
    • 核心代碼
    • 測試代碼
    • 運作裝置及環境
    • PS:
    • PPS:
    • Demo

列印效果圖

uni-app H5+ 連接配接藍牙列印機列印文字及二維碼

核心代碼

/**
 * @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

繼續閱讀