uniApp开发微信小程序-连接蓝牙连接打印机上岸!

news2025/4/18 6:14:28

历经波折三次成功上岸!

三次经历简单絮叨一下:使用uniApp+vue开发的微信小程序,使用蓝牙连接打印机,蓝牙所有的接口都是插件中封装的,用的插件市场中的这个: dothan-lpapi-ble ;所以,开发中,主要是看插件文档中使用的API。

首次上线

情况说明: 首次上线微信小程序:直接初始化实例,调用的插件API,测试环境成功连接打印,就上线了。

结果

客户手机一直搜索不到打印机,根据经验发现没执行实例的代码;
想到授权蓝牙也没自动弹框提示,小程序设置中也没有蓝牙开启设置;

问题定位:

那就问 deepseeek 开干呗!
1. 可能是用户手机 鸿蒙系统 问题:在系统设置中找到应用管理,找到微信,然后打开微信的权限管理,查看 附近设备的权限 - 开启(反复开关)。

插件市场遇到的特殊情况
2. 显示授权:必要的权限声明
3. 检查manifest.json配置
4. 微信小程序发版后台 - 设置 - 基本设置 - 服务内容声明 - 用户隐私保护指引 - 添加(蓝牙、微信信息收集)

二次上线

情况说明: 二次上线微信小程序:提交审核是其他同事在做,正常提交

结果

客户手机一直搜索不到打印机,根据经验发现没执行实例的代码;
线上环境用户还是没有触发授权蓝牙、位置信息提示;
审核没通过

问题定位:

1. 提交审核时勾选了:未采集用户隐私
2. 亲自提审、加图片、视频辅佐

三次上线

情况说明:审核通过

结果

线上成功授权、成功搜索打印机!

成功版本代码

manifest.json配置

"mp-weixin": {
		"appid": "...",
		//  添加...
		"permission": {
			"scope.bluetooth": {
				"desc": "用于连接蓝牙设备"
			},
			"scope.userLocation": {
				"desc": "你的位置信息将用于蓝牙设备连接"
			}
		},
		"requiredPrivateInfos": ["getLocation"]
	},
	

授权提示 - deepseek友情提供

// 使用-获取蓝牙权限
await this.bringBluetoothRight();

// 方法 methods: 
bringBluetoothRight() {
  let _this = this;

  return new Promise((resolve, reject) => {
    uni.getSetting({
      success: async (res) => {
        try {
          console.log(res, 'res')
          // 1. 同时检查蓝牙和位置权限
          const needBluetooth = !res.authSetting['scope.bluetooth'];
          const needLocation = !res.authSetting['scope.userLocation'];

          if (!needBluetooth && !needLocation) {
            // 已有全部权限
            return resolve(await _this.wechatBluetoothAuthUtils());
          }

          // 2. 显示预授权提示(合并说明)
          await _this.showPreAuthorizeModal(
            '需要蓝牙和位置权限',
            '连接打印机需要以下权限:\n\n1. 蓝牙权限 - 用于设备配对\n2. 位置权限 - 用于搜索设备'
          );

          // 3. 按顺序请求权限
          if (needBluetooth) {
            await _this.requestPermission('scope.bluetooth', '蓝牙');
          }

          if (needLocation) {
            await _this.requestPermission('scope.userLocation', '位置');
          }

          // 4. 验证权限获取结果
          const currentSettings = await _this.getSettingWithRetry();

          console.log(currentSettings, 'currentSettings')
          if ((!needBluetooth || currentSettings['scope.bluetooth']) &&
              (!needLocation || currentSettings['scope.userLocation'])) {
            console.log('.......')
            resolve(await _this.wechatBluetoothAuthUtils());
          } else {
            console.log('1111111')
            // 5. 有权限未被授予,引导去设置页
            await _this.showGoSettingModal();
            const finalSettings = await _this.getSettingWithRetry();

            if ((!needBluetooth || finalSettings['scope.bluetooth']) &&
                (!needLocation || finalSettings['scope.userLocation'])) {
              resolve(await _this.wechatBluetoothAuthUtils());
            } else {
              reject(new Error('权限未完全授予'));
            }
          }
        } catch (error) {
          reject(error);
        }
      },
      fail: reject
    });
  });
},
// 新增:带重试的获取设置
getSettingWithRetry(retry = 2) {
  return new Promise((resolve, reject) => {
    const tryGet = (attempt) => {
      uni.getSetting({
        success: resolve,
        fail: attempt > 0 ?
          () => setTimeout(() => tryGet(attempt - 1), 500) : reject
      });
    };
    tryGet(retry);
  });
},

// 改进:通用权限请求方法
requestPermission(scope, permissionName) {
  let _this = this;
  // 添加最大重试次数控制
  const MAX_RETRY = 1; // 最多重试1次(即总共2次机会)

  return new Promise((resolve, reject) => {
    const tryAuthorize = (retryCount = 0) => {
      uni.authorize({
        scope,
        success: resolve,
        fail: async () => {
          // 第一次拒绝时显示提示,后续直接引导去设置页
          if (retryCount < MAX_RETRY) {
            const confirmed = await _this.showPreAuthorizeModal(
              `${permissionName}权限被拒绝`,
              `需要${permissionName}权限才能连接打印机,是否重新授权?`,
              '重新授权',
              '取消'
            );

            if (confirmed) {
              tryAuthorize(retryCount + 1); // 增加重试计数
              return;
            }
          }

          // 达到最大重试或用户取消,引导去设置页
          const goSetting = await _this.showPreAuthorizeModal(
            '权限不足',
            `需要${permissionName}权限才能使用打印机功能,是否去设置页开启?`,
            '去设置',
            '暂不开启'
          );

          if (goSetting) {
            uni.openSetting({
              success: (res) => {
                res.authSetting[scope] ? resolve() :
                  reject(new Error(
                    `用户未授权${permissionName}权限`));
              },
              fail: () => reject(new Error('打开设置页失败'))
            });
          } else {
            reject(new Error(`用户取消${permissionName}权限授权`));
          }
        }
      });
    };

    tryAuthorize(); // 开始首次授权尝试
  });
},
// 改进:预授权弹窗(返回Promise<boolean>)
showPreAuthorizeModal(title, content) {
  return new Promise((resolve) => {
    uni.showModal({
      title,
      content,
      confirmText: '继续',
      cancelText: '取消',
      success: (res) => {
        resolve(!!res.confirm);
      },
      fail: () => resolve(false)
    });
  });
},

// 改进:去设置页弹窗
showGoSettingModal() {
  return this.showPreAuthorizeModal(
    '需要权限',
    '需要前往设置页手动开启权限,是否现在去设置?'
  ).then((confirm) => {
    if (confirm) {
      return new Promise((resolve) => {
        uni.openSetting({
          success: resolve,
          fail: resolve
        });
      });
    }
    return Promise.reject(new Error('用户取消设置'));
  });
},


wechatBluetoothAuthUtils() {
  let _this = this;
  const {
    bluetoothEnabled,
    platform
  } = uni.getSystemInfoSync();
  console.log(bluetoothEnabled,
              platform)
  // 设备为IOS时,微信蓝牙是否开启
  if (platform === 'ios') {
    return new Promise((resolve, reject) => {
      // 初始化蓝牙模块(用openBluetoothAdapter 方法解决部分ios设备,授权蓝牙失败的问题)
      uni.openBluetoothAdapter({
        success: () => {
          // 开启蓝牙功能 =》 初始化拾果sdk
          resolve(true);
        },
        fail: (openBlueFail) => {
          if (openBlueFail.state === 3) {
            // 说明微信应用蓝牙未授权
            uni.showModal({
              content: '检测到您未允许微信访问手机蓝牙权限,是否打开系统设置?',
              showCancel: false,
              confirmText: '前往设置',
              success: () => {
                // 跳转微信应用权限
                uni.openAppAuthorizeSetting();
              },
            });
          } else if (openBlueFail.state === 4) {
            // 说明系统蓝牙未开启
            uni.showModal({
              content: '蓝牙设置 - 小程序需要通过蓝牙搜索和连接设备,请确认手机蓝牙功能是否已开启?',
              showCancel: false,
              confirmText: '我已开启',
              success: async () => {
                const {
                  bluetoothEnabled,
                  platform
                } = uni.getSystemInfoSync();
                if (bluetoothEnabled) {
                  // 开启蓝牙功能
                  resolve(true);
                } else {
                  uni.showToast({
                    icon: 'none',
                    title: '手机蓝牙未开启'
                  })
                }
              },
            });
          }
        },
      });
    });
  } else {
    return new Promise(function(resolve, reject) {
      // andriod
      if (!bluetoothEnabled) {
        // 说明系统蓝牙未开启
        uni.showModal({
          content: '蓝牙设置 - 小程序需要通过蓝牙搜索和连接设备,请确认手机蓝牙功能是否已开启?',
          showCancel: false,
          confirmText: '我已开启',
          success: async () => {
            const {
              bluetoothEnabled,
              platform
            } = uni.getSystemInfoSync();
            if (bluetoothEnabled) {
              // 开启蓝牙功能
              resolve(true);
            } else {
              toast({
                title: '手机蓝牙未开启'
              });
            }
          },
        });
      } else {
        // 开启蓝牙功能
        resolve(true);
      }
    });
  }
},

额外关闭提示:

<button class="search-btn" type="default" @click="toggleLocationTracking">{{ isTrackingLocation ? '关闭位置追踪' : '开启位置追踪' }}</button>

// data
isTrackingLocation:false

// methods
toggleLocationTracking() {
				if (this.isTrackingLocation) {
					this.stopLocationTracking();
				} else {
					this.startLocationTracking();
				}
			},
startLocationTracking() {
				uni.authorize({
					scope: 'scope.userLocation',
					success: () => {
						this.isTrackingLocation = true;
						uni.showToast({
							title: '已开启位置服务',
							icon: 'success'
						});
					},
					fail: () => {
						uni.showModal({
							title: '提示',
							content: '需要位置权限才能搜索蓝牙设备',
							confirmText: '去设置',
							success: (res) => {
								if (res.confirm) uni.openSetting();
							}
						});
					}
				});
			},
stopLocationTracking() {
				uni.getSetting({
					success: (res) => {
						if (res.authSetting['scope.userLocation']) {
							uni.showModal({
								title: '确认',
								content: '确定要关闭位置服务吗?关闭后将无法搜索新设备',
								success: (res) => {
									if (res.confirm) {
										// 实际微信小程序无法直接关闭权限,引导用户去设置页
										uni.openSetting({
											success: () => {
												this.isTrackingLocation = false;
											}
										});
									}
								}
							});
						}
					}
				});
			},
								

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【特权FPGA】之按键消抖

完整代码如下所示&#xff1a; timescale 1ns / 1ps// Company: // Engineer: 特权 // // Create Date: // Design Name: // Module Name: // Project Name: // Target Device: // Tool versions: // Description: // // Dependencies: // // Revision: // …

P1331 洛谷 海战

题目描述 思路 这个题需要读懂题意&#xff0c;即“什么样的形式表示两只船相撞&#xff1f;” ----> 上下相邻或左右相邻 如果图是不和法的&#xff0c;一定存在如下结构&#xff1a; # # . # 或 # # # . 或 # . # # 或 . # # #即四个格子里有三个#&#xff0c;一个"…

网络安全·第二天·ARP协议安全分析

今天我们来考虑考虑计算机网络中的一类很重要的协议-------ARP协议&#xff0c;介绍他用途的同时&#xff0c;分析分析ARP协议存在的一些漏洞及其相关的协议问题。 一、物理地址与IP地址 1、举例 在计算机网络中&#xff0c;有两类地址十分关键&#xff0c;一类称为物理地址&a…

华为手机或平板与电脑实现文件共享

1.手机或平板与电脑在同一个网络 2.打开手机或平板端&#xff0c;设置---更多连接----快分享或华为分享打开此功能-----开启共享至电脑 3.打开电脑&#xff0c;网络中就可看到手机端分享的用户名称 4. 登陆就可访问手机 5.常见问题 5.1 电脑未发现本机 5.2 修改了访问密码后再…

幻兽帕鲁(Palworld)在线工具集:让游戏体验更轻松!

幻兽帕鲁(Palworld)在线工具集&#xff1a;让游戏体验更轻松&#xff01; &#x1f3ae; 工具介绍 为了帮助广大幻兽帕鲁玩家更好地享受游戏&#xff0c;我开发了这个全面的在线工具集。无需下载安装&#xff0c;打开网页即可使用&#xff0c;完全免费&#xff01; &#x1…

学习51单片机Day02---实验:点亮一个LED灯

目录 1.先看原理图 2.思考一下&#xff08;sbit的使用&#xff09;&#xff1a; 3.给0是要让这个LED亮&#xff08;LED端口设置为低电平&#xff09; 4.完成的代码 1.先看原理图 比如我们要让LED3亮起来&#xff0c;对应的是P2^2。 2.思考一下&#xff08;sbit的使用&…

如何使用通义灵码学习JavaScript和DOM

如果你看到了本手册的页面数量&#xff0c;你就会发现JavaScript的API真的非常丰富&#xff0c;在MDN上专门有一大分类用于介绍JavaScript的API&#xff0c;但软件工程行业有一个著名法则叫2-8法则&#xff0c;意思是只有20%的内容会经常使用到&#xff0c;而80%的内容只在一些…

基于labview的多功能数据采集系统

基于labview的多功能数据采集系统&#xff08;可定制功能&#xff09; 包含基于NI温度采集卡。电流采集卡。电压采集卡的数据采集功能 数据存储 报表存储 数据处理与分析 生产者消费者架构 有需要可联系

SpringMVC基础一(SpringMVC运行原理)

先了解MVC&#xff0c;在JavaWeb基础五中。 回忆servlet&#xff0c;在javaweb基础二中。 创建一个web项目&#xff1a; 1、新建maven项目&#xff0c;导入依赖。&#xff08;junit、springmvc、spring-webmvc、servlet-api、jsp-api、jstl&#xff09; <groupId>org…

蓝桥杯刷题--宝石组合

在一个神秘的森林里&#xff0c;住着一个小精灵名叫小蓝。有一天&#xff0c;他偶然发现了一个隐藏在树洞里的宝藏&#xff0c;里面装满了闪烁着美丽光芒的宝石。这些宝石都有着不同的颜色和形状&#xff0c;但最引人注目的是它们各自独特的 “闪亮度” 属性。每颗宝石都有一个…

红宝书第三十一讲:通俗易懂的包管理器指南:npm 与 Yarn

红宝书第三十一讲&#xff1a;通俗易懂的包管理器指南&#xff1a;npm 与 Yarn 资料取自《JavaScript高级程序设计&#xff08;第5版&#xff09;》。 查看总目录&#xff1a;红宝书学习大纲 一、基础概念 包管理器&#xff1a;帮你自动下载和管理第三方代码库&#xff08;如…

进程状态的转换

进程处于运行态时&#xff0c;它必须已获得所需的资源&#xff0c;在运行结束后就撤销。只有在时间片到或出现了比现在进程优先级更高的进程时才转变成就绪态。 就绪 → 运行​​ ​​触发条件​​&#xff1a;进程被​​调度器选中​​&#xff08;如时间片轮转或优先级调度&…

SpringAOP新链浅析

前言 在复现CCSSSC软件攻防赛的时候发现需要打SpringAOP链子&#xff0c;于是跟着前人的文章自己动手调试了一下 参考了大佬的文章 https://gsbp0.github.io/post/springaop/#%E6%B5%81%E7%A8%8B https://mp.weixin.qq.com/s/oQ1mFohc332v8U1yA7RaMQ 正文 依赖于Spring-AO…

【动手学深度学习】现代卷积神经网络:ALexNet

【动手学深度学习】现代卷积神经网络&#xff1a;ALexNet 1&#xff0c;ALexNet简介2&#xff0c;AlexNet和LeNet的对比3&#xff0c; AlexNet模型详细设计4&#xff0c;AlexNet采用ReLU激活函数4.1&#xff0c;ReLU激活函数4.2&#xff0c;sigmoid激活函数4.3&#xff0c;为什…

PyTorch深度学习框架60天进阶学习计划 - 第37天:元学习框架

PyTorch深度学习框架60天进阶学习计划 - 第37天&#xff1a;元学习框架 嘿&#xff0c;朋友们&#xff01;欢迎来到我们PyTorch进阶之旅的第37天。今天我们将深入探索一个非常有趣且强大的领域——元学习(Meta-Learning)&#xff0c;也被称为"学会学习"(Learning to…

【中检在线-注册安全分析报告】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 1. 暴力破解密码&#xff0c;造成用户信息泄露 2. 短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉 3. 带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造…

UE5 运行时动态将玩家手部模型设置为相机的子物体

在编辑器里&#xff0c;我们虽然可以手动添加相机&#xff0c;但是无法将网格体设置为相机的子物体&#xff0c;只能将相机设置为网格体的子物体 但是为了使用方便&#xff0c;我们希望将网格体设置为相机的子物体&#xff0c;这样我们直接旋转相机就可以旋转网格体&#xff0…

EasyExcel-一款好用的excel生成工具

EasyExcel是一款处理excel的工具类&#xff0c;主要特点如下&#xff08;官方&#xff09;&#xff1a; 特点 高性能读写&#xff1a;FastExcel 专注于性能优化&#xff0c;能够高效处理大规模的 Excel 数据。相比一些传统的 Excel 处理库&#xff0c;它能显著降低内存占用。…

WEB攻防-Java安全JNDIRMILDAP五大不安全组件RCE执行不出网不回显

目录 1. RCE执行-5大类函数调用 1.1 Runtime方式 1.2 Groovy执行命令 1.3 脚本引擎代码注入 1.4 ProcessImpl 1.5 ProcessBuilder 2. JNDI注入(RCE)-RMI&LDAP&高版本 2.1 RMI服务中的JNDI注入场景 2.2 LDAP服务中的JNDI注入场景 攻击路径示例&#…

DrissionPage移动端自动化:从H5到原生App的跨界测试

一、移动端自动化测试的挑战与机遇 移动端测试面临多维度挑战&#xff1a; 设备碎片化&#xff1a;Android/iOS版本、屏幕分辨率差异 混合应用架构&#xff1a;H5页面与原生组件的深度耦合 交互复杂性&#xff1a;多点触控、手势操作、传感器模拟 性能监控&#xff1a;内存…