You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

329 lines
8.1 KiB
Vue

<template>
<view class="print-page">
<ch-nav-bar :height="66" title="选择打印机">
<block v-slot:right>
<ch-nav-btn icon="paperplane-filled" iconSize="28" color="#fff" @click="print">
</ch-nav-btn>
<ch-nav-btn icon="paperplane-filled" iconSize="28" color="#fff" @click="printImg">
</ch-nav-btn>
</block>
</ch-nav-bar>
<canvas ref="canvasRef" canvas-id="imgCanvas" id="imgCanvas" :style="`width: ${canvasW}px; height: ${canvasH}px;`"/>
<uni-section title="附近打印机" type="line">
<block v-slot:right>
<button type="primary" size="mini" class="search-btn" :loading="searching" :disabled="searching" @click="searchDevices">{{ searching ? '...' : '' }}</button>
</block>
<uni-list>
<block v-for="item in devices" :key="item.deviceId">
<uni-list-item
showArrow
showExtraIcon
:extraIcon="extraIcon"
:clickable="true"
:title="item.name || '未知设备'"
@click="connectBLE(item.deviceId)">
<block v-slot:footer>
<text>{{ item.deviceId == connectId ? '已连接' : ''}}</text>
</block>
</uni-list-item>
</block>
</uni-list>
</uni-section>
</view>
</template>
<script setup>
import { onBeforeUnmount, onMounted, reactive, toRefs, ref, nextTick } from 'vue';
import Bluetooth from '@/components/xun-bluetoothPrint-t/print/bluetooth.js'
import PrinterJobs from '@/components/xun-bluetoothPrint-t/print/printerjobs.js'
import printerUtil from '@/components/xun-bluetoothPrint-t/print/printerutil.js'
import printerJobs from '@/components/xun-bluetoothPrint-t/print/printerjobs.js';
import { getImgData } from '@/api/common.js';
// #ifdef APP-PLUS
const bluetooth = new Bluetooth();
// #endif
const canvasRef = ref(null);
const data = reactive({
searching: false,
connectId: '',
printUUIDs: ["E7810A71-73AE-499D-8C15-FAA9AEF0C3F2"],
devices: [],
currentDevices: [],
writeId: '',
extraIcon: {
type: 'smallcircle',
color: '#4cd964',
size: '32'
},
questions: [],
canvasH: 300,
canvasW: 150,
imgPixData: null
});
const { devices, currentDevices, writeId, printUUIDs, searching, connectId, extraIcon, questions, canvasH, canvasW, imgPixData } = toRefs(data);
onMounted(() => {
uni.getStorage({
key: 'questions',
success: (res) => {
questions.value = JSON.parse(res.data)
console.log(questions.value)
getImg();
}
})
})
onBeforeUnmount(() => {
// #ifdef APP-PLUS
bluetooth.closeBluetoothAdapter() // 关闭蓝牙模块
// #endif
})
// 搜索打印机
function searchDevices () {
closeConnection()
devices.value = []
searching.value = true;
bluetooth.startBluetoothDevicesDiscovery({
uuids: printUUIDs.value,
isListen: true,
callback: addDevices
})
setTimeout(() => {
stopSearch()
}, 20000)
}
// 停止搜索
function stopSearch () {
bluetooth.stopBluetoothDevicesDiscovery()
searching.value = false;
}
// 添加打印机
function addDevices (newDevices) {
devices.value = newDevices
console.log(devices.value)
}
// 连接打印机
function connectBLE (id) {
console.log(id);
searching && stopSearch();
bluetooth.createBLEConnection(id).then(res => {
connectId.value = id;
setTimeout(() => {
bluetooth.getBLEDeviceServices().then(services => {
return bluetooth.getBLEDeviceCharacteristics(printUUIDs.value[0])
}).then(res => {
res.writeId ? (writeId.value = res.writeId)
: uni.showToast({ title: '该打印机服务无法打印' })
})
}, 1000)
})
}
// 断开连接
function closeConnection () {
if (!connectId) return;
bluetooth.closeBLEConnection()
connectId.value = ''
}
// 发送二进制数据
function print(){
const printerJobs = new PrinterJobs();
const quesArr = questions.value.forEach(ques => {
const quesItem = formatQuestion(ques);
Object.keys(quesItem).forEach(key => {
const item = quesItem[key];
switch (key) {
case 'content':
printTitleContent(printerJobs, item.textArr);
break;
case 'options':
printOption(printerJobs, item);
break;
case 'answer':
printAnswerExplain(printerJobs, item.textArr, 'answer');
break;
case 'explain':
printAnswerExplain(printerJobs, item.textArr, 'explain')
}
})
})
let buffer = printerJobs.buffer();
console.log('buffer>>>',buffer)
printbuffs(buffer)
}
function printbuffs(buffer) {
// 1.并行调用多次会存在写失败的可能性
// 2.建议每次写入不超过20字节
// 分包处理,延时调用
const maxChunk = 20;
const delay = 20;
for (let i = 0, j = 0, length = buffer.byteLength; i < length; i += maxChunk, j++) {
let subPackage = buffer.slice(i, i + maxChunk <= length ? (i + maxChunk) : length);
setTimeout(printbuff, j * delay, subPackage);
}
}
function printbuff(buffer) {
bluetooth.writeBLECharacteristicValue(buffer, writeId.value)
}
function printImg() {
const printerJobs = new PrinterJobs();
console.log(imgPixData.value.data)
printerJobs.printImage(imgPixData.value.data)
}
// 打印题文
function printTitleContent(printerJobs, textArr) {
textArr.forEach(text => printerJobs.print(text))
}
// 打印答案、解析
function printAnswerExplain (printerJobs, textArr, type) {
if (type == 'answer') printerJobs.print('答案:');
if (type == 'explain') printerJobs.print('解析:');
textArr.forEach(text => printerJobs.print(text))
}
// 打印选项
function printOption (printerJobs, options) {
options.forEach(option => {
option.textArr.forEach((text, index) => {
const printText = index == 0 ? `${option.number}. ${text}` : text;
printerJobs.print(printText)
})
})
}
// 格式化试题
function formatQuestion (question) {
// 提取img标签
// 剔除img标签及内容并做标记
// 根据br标签分割内容
// 剔除html标签
const ques = {
content: {
text: question.questioncontent,
textArr: [],
imgs: []
},
options: question.option.map(option => {
return {
number: option.optionnumber,
text: option.optioncontent,
textArr: [],
imgs: []
}
}),
answer: {
text: question.questionanswer,
textArr: [],
imgs: []
},
explain: {
text: question.questionexplain,
textArr: [],
imgs: []
}
}
Object.keys(ques).forEach(key => {
if (key == 'options') {
ques[key].forEach(option => {
const { imgArr, text } = formatImgText(option.text)
option.text = text;
option.textArr = formatTextTag(text);
option.imgs = imgArr;
})
return;
}
const item = ques[key];
const { imgArr, text } = formatImgText(item.text)
item.text = text;
item.textArr = formatTextTag(text)
item.imgs = imgArr;
})
return ques;
}
function formatImgText (text) {
const imgRegx = /<img\s*src=\"([^\"]*?)\"[^>]*>/;
const imgRegxG = /<img\s*src=\"([^\"]*?)\"[^>]*>/g;
const imgArr = text.match(imgRegxG) || [];
let regText = text;
imgArr.forEach((img, index) => {
regText = text.replace(imgRegx, `#{img_${index}}`)
})
return {
imgArr,
text: regText
}
}
function formatTextTag (text) {
const textArr = text.split(/<br\s*\/?>/);
textArr.forEach((item, index) => {
textArr[index] = item.replace(/<[^>]*>/g, '')
})
return textArr;
}
function getImg () {
const imgUrl = 'http://test6.jianxiaopu.com/img/ad.png';
return getImgData(imgUrl)
.then(data => {
return getImageInfo(imgUrl)
})
.then(imgInfo => {
const { width, height, path } = imgInfo;
canvasH.value = height;
canvasW.value = width;
const canvas = uni.createCanvasContext('imgCanvas');
canvas.drawImage(path, 0, 0, width, height)
canvas.draw(false, () => {
uni.canvasGetImageData({
canvasId: 'imgCanvas',
success: (pixData) => {
imgPixData.value = pixData;
}
})
})
})
}
function getImageInfo(path) {
return new Promise((resolve, reject) => {
uni.getImageInfo({
src: path,
success: (imgInfo) => {
resolve(imgInfo)
},
fail: (error) => {
reject(error)
}
})
})
}
</script>
<style lang="scss" scoped>
@import './print.scss';
</style>