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.
337 lines
8.3 KiB
Vue
337 lines
8.3 KiB
Vue
<template>
|
|
<view class="print-page">
|
|
<ch-nav-bar :height="66" title="选择打印机" :rightWidth="140">
|
|
<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(){
|
|
if (!writeId.value) {
|
|
uni.showToast({
|
|
icon: 'none',
|
|
title: '请连接打印机'
|
|
})
|
|
return;
|
|
}
|
|
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')
|
|
}
|
|
})
|
|
printerJobs.print('')
|
|
})
|
|
|
|
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>
|