设置页面:

<template><view class="pageBg"><u-navbar leftIconColor="#fff" :leftIconSize="28" title="打印设置" bgColor="#3c9cff" :placeholder="true"@leftClick="$navigateBack"></u-navbar><view class="h10"></view><!-- 蓝牙未开启提示 --><u-alert v-if="!isBlue" type="warning" :show-icon="true" title="未开启蓝牙"description="无法连接蓝牙打印机,请先开启蓝牙!"></u-alert><view class="p10">已连接打印机</view><view><!-- <view v-if="connected" class="list">{{ connectedDeviceName }}</view> --><view v-if="connected || connectedDeviceName" class="list">{{ connectedDeviceName }}</view><view v-else class="list center cloc">暂无</view></view><view class="p10">搜索到的蓝牙打印机</view><view><view v-for="d in devices" :key="d.deviceId" class="list"><view @click="connectDevice(d.deviceId)" class="flexBetween"><view>{{ d.name || '未知设备' }}</view><view style="color:#3c9cff;">添加</view></view></view><view v-if="devices.length === 0" class="list center cloc">暂无</view></view><view class="h100"></view><!-- <view class="float-button p10 grid2"><u-button type="primary" text="重新搜索蓝牙打印机" @click="initAndScan()" color="#3c9cff" /><u-button type="primary" text="一键打印" @click="oneClickPrint()" color="#3c9cff" /></view> --><view class="float-button p10 "><u-button type="primary" text="重新搜索蓝牙打印机" @click="initAndScan()" color="#3c9cff" /></view></view>
</template><script>import {initBluetooth,discoverDevices,connect,print,onPrinterStatus,isBluetoothReady} from "@/utils/printer.js"export default {data() {return {devices: [],connected: false,connectedDeviceId: null,connectedDeviceName: "",reconnecting: false,isBlue: true,scanning: false,}},// async onLoad() {// 	await this.checkBluetooth()// 	if (this.isBlue) {// 		await this.autoReconnect()// 		await this.initAndScan()// 	}// },async onLoad() {await this.checkBluetooth()// ✅ 获取上次打印机信息const lastPrinter = uni.getStorageSync("lastPrinter")console.log('lastPrinter:',lastPrinter)if (lastPrinter && lastPrinter.name) {this.connectedDeviceName = lastPrinter.name}if (this.isBlue) {await this.autoReconnect() // 尝试重连await this.initAndScan()    // 扫描设备}},methods: {/** 检查蓝牙是否可用 */async checkBluetooth() {this.isBlue = await isBluetoothReady()},/** 初始化蓝牙并扫描设备 */async initAndScan() {if (!this.isBlue) {uni.showToast({title: "请先开启蓝牙",icon: "none"})return}if (this.scanning) returnthis.scanning = truetry {await initBluetooth()uni.showLoading({title: '加载中',mask: true,})// 扫描设备this.devices = await discoverDevices(3000) // 扫描3秒// ✅ 获取上次打印机const lastPrinter = uni.getStorageSync("lastPrinter")if (lastPrinter) {// 如果扫描列表里没有上次打印机,就加到最前面if (!this.devices.find(d => d.deviceId === lastPrinter.deviceId)) {this.devices.unshift({deviceId: lastPrinter.deviceId,name: lastPrinter.name || '未知设备'})}}uni.hideLoading()console.log('this.devices:', this.devices)if (this.devices.length === 0) {uni.showToast({title: "未找到设备",icon: "none"})}} catch (err) {console.error("搜索设备失败", err)uni.showToast({title: "搜索设备失败",icon: "none"})} finally {this.scanning = false}},/** 手动连接设备 */async connectDevice(deviceId) {try {await connect(deviceId)this.connected = truethis.connectedDeviceId = deviceIdconst dev = this.devices.find(d => d.deviceId === deviceId)this.connectedDeviceName = dev ? dev.name : deviceIduni.showToast({ title: "连接成功" })// ✅ 保存上次打印机(带名字)uni.setStorageSync("lastPrinter", {deviceId,name: this.connectedDeviceName})this.listenPrinterStatus()this.listenDisconnect()} catch (err) {console.error("连接失败", err)uni.showToast({ title: "连接失败", icon: "none" })}},/** 打印机状态监听 */listenPrinterStatus() {// uni.offBLECharacteristicValueChange()onPrinterStatus(data => {if (data[0] & 0x04) uni.showToast({title: "缺纸",icon: "none"})if (data[0] & 0x08) uni.showToast({title: "开盖",icon: "none"})if (data[0] & 0x10) uni.showToast({title: "过热",icon: "none"})})},/** 监听断开并自动重连 */listenDisconnect() {// uni.offBLEConnectionStateChange()uni.onBLEConnectionStateChange(res => {if (!res.connected && res.deviceId === this.connectedDeviceId && !this.reconnecting) {console.warn("打印机断开,开始自动重连")this.connected = falsethis.reconnecting = truethis.autoReconnect()}})},/** 自动重连上次打印机 */async autoReconnect() {try {const lastPrinter = uni.getStorageSync("lastPrinter")if (!lastPrinter || !lastPrinter.deviceId) {this.reconnecting = falsereturn}uni.showLoading({ title:'加载中', mask:true })// ✅ 优先直接用 deviceId 连接try {await connect(lastPrinter.deviceId)this.connected = truethis.connectedDeviceId = lastPrinter.deviceIdthis.connectedDeviceName = lastPrinter.name || lastPrinter.deviceIduni.hideLoading()uni.showToast({ title: "打印机已重连" })this.listenPrinterStatus()this.listenDisconnect()return} catch (err) {console.warn("直接重连失败,尝试扫描", err)}// ❌ 兜底:扫描设备再匹配const devices = await discoverDevices(3000)uni.hideLoading()const dev = devices.find(d => d.deviceId === lastPrinter.deviceId)if (!dev) {console.warn("未找到上次打印机")this.reconnecting = falsereturn}await connect(dev.deviceId)this.connected = truethis.connectedDeviceId = dev.deviceIdthis.connectedDeviceName = dev.nameuni.showToast({ title: "打印机已重连" })// ✅ 更新存储,保证 name 最新uni.setStorageSync("lastPrinter", {deviceId: dev.deviceId,name: dev.name})this.listenPrinterStatus()this.listenDisconnect()} catch (err) {console.error("自动重连失败", err)} finally {this.reconnecting = falseuni.hideLoading()}},/** 一键打印 */async oneClickPrint() {try {if (!this.connected) {await this.autoReconnect()if (!this.connected) {uni.showToast({title: "未连接打印机",icon: "none"})return}}await print("Hello K329", "https://uniapp.dcloud.io")uni.showToast({title: "打印完成"})} catch (err) {console.error("打印失败", err)uni.showToast({title: "打印失败",icon: "none"})}},},onUnload() {// 页面卸载时解绑事件// uni.offBLECharacteristicValueChange()// uni.offBLEConnectionStateChange()}}
</script><style lang="scss" scoped>.list {border-bottom: 2rpx solid #efefef;background: white;padding: 30rpx;font-size: 32rpx;}.list:active {background: #efefef;}.grid2 {display: flex;align-items: center;}.grid2>* {flex: 1;}.grid2>*:first-child {margin-right: 20rpx;}
</style>

printer.js

let deviceId = null
let writeServiceId = null
let writeCharacteristicId = null
let notifyServiceId = null
let notifyCharacteristicId = null/*** 初始化蓝牙*/
export function initBluetooth() {return new Promise((resolve, reject) => {uni.openBluetoothAdapter({success: resolve,fail: reject})})
}/*** 搜索蓝牙设备*/
export function discoverDevices(scanTime = 3000) {return new Promise((resolve, reject) => {const devices = []uni.startBluetoothDevicesDiscovery({allowDuplicatesKey: true, // 可以重复发现success: () => {console.log("开始扫描蓝牙设备...")uni.onBluetoothDeviceFound((res) => {res.devices.forEach((d) => {if (d.name && !devices.find(x => x.deviceId === d.deviceId)) {devices.push(d)}})})// 扫描指定时间后返回结果setTimeout(() => {uni.stopBluetoothDevicesDiscovery({success: () => {console.log("扫描结束,设备列表:", devices)resolve(devices)},fail: (err) => {console.error("停止扫描失败", err)resolve(devices) // 即使失败也返回已收集的}})}, scanTime)},fail: reject})})
}/*** 连接设备并自动发现写入/状态通道*/
export function connect(deviceId_) {return new Promise((resolve, reject) => {uni.createBLEConnection({deviceId: deviceId_,success: () => {deviceId = deviceId_// ✅ 存储上次使用的打印机// uni.setStorageSync("lastPrinter", deviceId_)console.log("已保存 lastPrinter:", deviceId_)uni.getBLEDeviceServices({deviceId,success: (res) => {let foundWrite = falselet foundNotify = falseres.services.forEach(service => {uni.getBLEDeviceCharacteristics({deviceId,serviceId: service.uuid,success: (res2) => {res2.characteristics.forEach(c => {// 找写入通道if ((c.properties.write || c.properties.writeWithoutResponse) && !foundWrite) {writeServiceId =service.uuidwriteCharacteristicId= c.uuidfoundWrite =trueconsole.log("找到写入通道:",service.uuid, c.uuid)}// 找状态通道if ((c.properties.notify || c.properties.read) && !foundNotify) {notifyServiceId= service.uuidnotifyCharacteristicId= c.uuidfoundNotify =trueconsole.log("找到状态通道:",service.uuid, c.uuid)// 开启状态通知uni.notifyBLECharacteristicValueChange({deviceId,serviceId: service.uuid,characteristicId: c.uuid,state: true,success: () =>console.log("已开启状态通知"),fail: (err) =>console.error("开启状态通知失败:",err)})}})if (foundWrite) resolve()},fail: reject})})},fail: reject})},fail: reject})})
}/*** 打印文本和二维码*/
export function print(text, qrcode) {if (!deviceId || !writeServiceId || !writeCharacteristicId) {return Promise.reject("请先连接打印机")}let cmd = `
SIZE 40 mm,30 mm
GAP 2 mm,0 mm
CLS
TEXT 50,50,"3",0,1,1,"${text}"
`if (qrcode) {cmd += `QRCODE 50,150,L,5,A,0,"${qrcode}"\n`}cmd += `PRINT 1\n`const buffer = str2ab(cmd)return new Promise((resolve, reject) => {uni.writeBLECharacteristicValue({deviceId,serviceId: writeServiceId,characteristicId: writeCharacteristicId,value: buffer,success: resolve,fail: reject})})
}/*** 监听打印机状态*/
export function onPrinterStatus(callback) {uni.onBLECharacteristicValueChange((res) => {const data = new Uint8Array(res.value)console.log("打印机状态数据:", data)callback(data)})
}/*** 字符串转 ArrayBuffer*/
function str2ab(str) {let buf = new ArrayBuffer(str.length)let bufView = new Uint8Array(buf)for (let i = 0; i < str.length; i++) {bufView[i] = str.charCodeAt(i)}return buf
}export async function isBluetoothReady() {return new Promise((resolve) => {uni.openBluetoothAdapter({success: () => {// 延迟 500ms 再获取状态,保证 iOS 能拿到真实值setTimeout(() => {uni.getBluetoothAdapterState({success(res) {resolve(res.available)},fail() {resolve(false)}})}, 500)},fail() {resolve(false)}})})
}export async function oneClickPrint2(text = "Hello K329", qrcode = "https://uniapp.dcloud.io") {try {// 1. 蓝牙是否开启const ready = await isBluetoothReady()if (!ready) throw new Error("蓝牙未开启")// 2. 初始化蓝牙适配器await initBluetooth()// 3. 获取上次打印机const lastPrinter = uni.getStorageSync("lastPrinter")// console.log("lastPrinter:", lastPrinter, "打印机上次")if (!lastPrinter) throw new Error("未找到已连接打印机")// 4. 直接尝试连接await connect(lastPrinter)// 5. 打印await print(text, qrcode)return true} catch (err) {console.error("一键打印失败", err)throw err}
}
/** 一键打印 */
export async function oneClickPrint(text = "Hello K329", qrcode = "https://uniapp.dcloud.io") {try {// 1. 蓝牙是否开启const ready = await isBluetoothReady()if (!ready) {uni.showToast({ title: "蓝牙未开启", icon: "none" })return}// 2. 初始化蓝牙await initBluetooth()// 3. 检查是否已连接if (!this.connected) {const lastPrinter = uni.getStorageSync("lastPrinter")if (!lastPrinter || !lastPrinter.deviceId) {uni.showToast({ title: "未连接打印机", icon: "none" })return}// 尝试直接连接上次打印机try {await connect(lastPrinter.deviceId)this.connected = truethis.connectedDeviceId = lastPrinter.deviceIdthis.connectedDeviceName = lastPrinter.name || lastPrinter.deviceIdthis.listenPrinterStatus()this.listenDisconnect()uni.showToast({ title: "打印机已重连" })} catch (err) {console.warn("自动连接上次打印机失败,需要手动连接", err)uni.showToast({ title: "打印机未连接", icon: "none" })return}}// 4. 打印await print(text, qrcode)uni.showToast({ title: "打印完成" })} catch (err) {console.error("一键打印失败", err)uni.showToast({ title: "打印失败", icon: "none" })}
}

一键打印:

<view class="p10"><u-button type="primary" class="lockoutClass" text="打印" :color="bgColor" @click="printNow()" /></view>
async printNow() {try {uni.showLoading({title:'打印中',mask:true,})await this.$printer.oneClickPrint("测试文本", "https://example.com/qrcode")uni.hideLoading()uni.showToast({title: "打印完成"})} catch (err) {console.log('err:',err)uni.showToast({title: "打印失败,请检查蓝牙或者打印机!",icon: "none"})}},

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/921046.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/921046.shtml
英文地址,请注明出处:http://en.pswp.cn/news/921046.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

pikachu之sql注入

目录 XX型注入 insert/update注入 delete注入 "http header"注入 基于boolian的盲注 基于时间的盲注 宽字节注入&#xff08;wide byte注入&#xff09; pikachu靶场的字符型注入中xx or 11#可以得到所有用户的信息。 XX型注入 首先输入1探测一下。 然后返回…

TLS(传输层安全协议)

文章目录一、核心概念二、为什么需要 TLS/SSL&#xff1f;三、工作原理与详细流程握手步骤详解&#xff1a;1.ClientHello & ServerHello&#xff1a;2.服务器认证 (Certificate, ServerKeyExchange)&#xff1a;3.客户端响应 (ClientKeyExchange, Finished)&#xff1a;4.…

【SpringMVC】SSM框架【二】——SpringMVC超详细

SpringMVC 学习目标&#xff1a; 1.SpringMVC简介 1&#xff09;web访问流程1.web服务器通过浏览器访问页面2.前端页面使用异步提交的方式发送请求到后端服务器3.后端服务器采用&#xff1a;表现层—业务层—数据层的架构进行开发4.页面请求由表现层进行接收&#xff0c;获取用…

PostgreSQL表膨胀的危害与解决方案

PostgreSQL 的 表膨胀&#xff08;Table Bloat&#xff09; 是数据库中由于 MVCC&#xff08;多版本并发控制&#xff09;机制导致的一种常见性能问题&#xff0c;表现为物理存储空间远大于实际有效数据量。以下是详细解释及其危害&#xff1a;一、表膨胀的产生原因 1. MVCC 机…

Elasticsearch面试精讲 Day 5:倒排索引原理与实现

【Elasticsearch面试精讲 Day 5】倒排索引原理与实现 在“Elasticsearch面试精讲”系列的第五天&#xff0c;我们将深入探讨搜索引擎最核心的技术基石——倒排索引&#xff08;Inverted Index&#xff09;。作为全文检索系统的灵魂&#xff0c;倒排索引直接决定了Elasticsearc…

【小白笔记】基本的Linux命令来查看服务器的CPU、内存、磁盘和系统信息

一、 核心概念与命令知识点英文名词&#xff08;词源解释&#xff09;作用与命令CPU (中央处理器)Central Processing Unit&#xff1a;<br> - Central&#xff08;中心的&#xff09;&#xff1a;来自拉丁语 centralis&#xff0c;意为“中心的”。<br> - Process…

51c大模型~合集177

自己的原文哦~ https://blog.51cto.com/whaosoft/14154064 #公开V3/R1训练全部细节&#xff01; 刚刚&#xff0c;DeepSeek最新发文&#xff0c;回应国家新规 AI 生成的内容该不该打上“水印”&#xff1f;网信办《合成内容标识方法》正式生效后&#xff0c;De…

CA根证书的层级关系和验证流程

CA根证书的层级关系和验证流程&#xff1a;1. 证书层级结构&#xff08;树状图&#xff09; [根证书 (Root CA)] │ ├── [中间证书 (Intermediate CA 1)] │ │ │ ├── [网站证书 (example.com)] │ └── [邮件证书 (mail.example.com)] │ └── [中间证书 (In…

液态神经网络(LNN)1:LTC改进成CFC思路

从液态时间常数网络&#xff08;Liquid Time-Constant Networks, LTC&#xff09;到其闭式解版本——闭式连续时间网络&#xff08;Closed-form Continuous-time Networks, CfC&#xff09; 的推导过程&#xff0c;可以分为以下几个关键步骤。我们将基于你提供的两篇论文&#…

【图像处理基石】图像预处理方面有哪些经典的算法?

图像预处理是计算机视觉任务&#xff08;如目标检测、图像分割、人脸识别&#xff09;的基础步骤&#xff0c;核心目的是消除图像中的噪声、提升对比度、修正几何畸变等&#xff0c;为后续高阶处理提供高质量输入。以下先系统梳理经典算法&#xff0c;再通过Python实现2个高频应…

MySQL 多表查询方法

MySQL 多表查询方法MySQL 多表查询用于从多个表中检索数据&#xff0c;通常通过关联字段&#xff08;如外键&#xff09;实现。以下是常见的多表查询方式&#xff1a;内连接&#xff08;INNER JOIN&#xff09;内连接返回两个表中匹配的行。语法如下&#xff1a;SELECT 列名 F…

网络断连与业务中断的全链路诊断与解决之道(面试场景题)

目录 1. 网络链路的“命脉”:从物理层到应用层的排查逻辑 物理层:别小看那一根网线 数据链路层:MAC地址和交换机的“恩怨情仇” 工具推荐:抓包初探 2. 网络层的“幕后黑手”:IP冲突与路由迷雾 IP冲突:谁抢了我的地址? 路由问题:数据包的“迷路”之旅 3. 传输层与…

英伟达Newton与OpenTwins如何重构具身智能“伴随式数采”范式

具身智能的“数据饥荒”&#xff1a;行业痛点与技术瓶颈的深度剖析1.1 具身智能的现状与核心挑战Embodied AI的落地之路面临着多重严峻挑战。在算法层面&#xff0c;实现通用智能仍需人类的持续介入&#xff0c;并且从感知到行动的认知映射尚未完全打通。在硬件层面&#xff0c…

STM32HAL 快速入门(十六):UART 协议 —— 异步串行通信的底层逻辑

大家好&#xff0c;这里是 Hello_Embed。在前几篇中&#xff0c;我们通过环形缓冲区解决了按键数据丢失问题&#xff0c;而在嵌入式系统中&#xff0c;设备间的数据交互&#xff08;如单片机与电脑、传感器的通信&#xff09;同样至关重要。UART&#xff08;通用异步收发传输器…

使用 C 模仿 C++ 模板的拙劣方法

如下所示&#xff0c;准备两个宏&#xff0c;一个定义类型&#xff0c;一个定义容器大小。 使用时只要先定义这两个宏&#xff0c;然后再包含容器头文件就能生成不同类型和大小的容器了。但是这种方法只允许在源文件中使用&#xff0c;如果在头文件中使用&#xff0c;定义不同类…

flume接收处理器:构建高可用与高性能的数据链路

flume接收处理器&#xff1a;构建高可用与高性能的数据链路 在大规模数据采集场景中&#xff0c;单点故障和性能瓶颈是两大核心挑战。Flume 通过 Sink Group 接收处理器&#xff08;Processor&#xff09; 机制&#xff0c;提供了强大的故障转移&#xff08;Failover&#xf…

高级Kafka应用之流处理

40 Kafka Streams与其他流处理平台的差异在哪里&#xff1f; 什么是流处理平台&#xff1f; “Streaming Systems”一书是这么定义“流处理平台”的&#xff1a;流处理平台&#xff08;Streaming System&#xff09;是处理无限数据集&#xff08;Unbounded Dataset&#xff09;…

Custom SRP - LOD and Reflections

1 LOD Groups 场景中对象越多,场景就越丰富,但是过多的对象,也会增加 CPU 和 GPU 的负担.同时如果对象最终渲染在屏幕上后覆盖的像素太少,就会产生模糊不清的像素点/噪点.如果能够不渲染这些过小的对象,就能解决噪点问题,同时释放 CPU GPU,去处理更重要的对象. 裁剪掉这些对象…

【Linux篇章】互联网身份密码:解密 Session 与 Cookie 的隐藏玩法和致命漏洞!

本篇摘要 本篇将承接上篇HTTP讲解&#xff08; 戳我查看 &#xff09;遗留的关于Cookie与Session的介绍&#xff0c;在本篇&#xff0c;将会介绍Cookie的由来&#xff0c;作用&#xff0c;以及缺点等&#xff0c;进而引出Session&#xff0c;最后介绍一下它们的性质等&#xf…

Postman接口测试工具:高效管理测试用例与环境变量,支持断言验证及团队协作同步

之前跟你们聊过能搭知识网络的 Obsidian&#xff0c;今天换个偏向接口测试的方向 —— 给你们安利一个 Github 上的「Postman」&#xff0c;它是个接口测试工具&#xff0c;官网能直接下载&#xff08;Postman: The Worlds Leading API Platform | Sign Up for Free&#xff09…