Appearance
使用方法
介绍
安卓蓝牙操作UTS原生插件集成了常用的蓝牙开关,设备配对,已配对的设备列表,搜索设备,连接设备,发送数据,蓝牙文件传输等多项功能,插件支持uniapp和uniapp x
插件说明
- 如果您在使用插件的过程中有任何问题,可以联系作者,作者将全力协助您只用插件
- 本文档同时提供了uniapp的用法示例和uniappx的用法示例,插件市场导入的示例项目仅为uniapp的用法示例,如果您需要uniappx的示例项目可以通过下方的链接下载示例
联系作者

关注微信公众号可联系作者
插件地址
https://ext.dcloud.net.cn/plugin?id=27995
权限
- android.permission.BLUETOOTH
- android.permission.BLUETOOTH_ADMIN
- android.permission.ACCESS_FINE_LOCATION
- android.permission.ACCESS_COARSE_LOCATION
- android.permission.POST_NOTIFICATIONS
- android.permission.READ_EXTERNAL_STORAGE
- android.permission.WRITE_EXTERNAL_STORAGE
- android.permission.MANAGE_EXTERNAL_STORAGE
- android.permission.BLUETOOTH_CONNECT
- android.permission.BLUETOOTH_SCAN
- android.permission.BLUETOOTH_ADVERTISE
用法
在需要使用插件的页面加载以下代码
js
import * as module from "@/uni_modules/leven-uts-bluetooth"js
import * as module from "@/uni_modules/leven-uts-bluetooth"页面内容
vue
<template>
<view class="content">
<!-- 按钮组 -->
<view>
<view style="display: flex; flex-direction: row; margin-bottom: 8px;">
<button style="flex: 1; margin-right: 8px;" type="primary" size="mini" @click="enableBluetooth">开启蓝牙</button>
<button style="flex: 1; margin-right: 8px;" type="primary" size="mini" @click="disableBluetooth">关闭蓝牙</button>
</view>
<view style="display: flex; flex-direction: row; margin-bottom: 8px;">
<button style="flex: 1;" type="primary" size="mini" @click="getPairedDevices">获取已配对设备</button>
</view>
<view style="display: flex; flex-direction: row; margin-bottom: 8px;">
<button style="flex: 1; margin-right: 8px;" type="primary" size="mini" @click="startDiscovery">搜索设备</button>
<button style="flex: 1; margin-right: 8px;" type="primary" size="mini" @click="stopDiscovery">停止搜索</button>
</view>
<view style="display: flex; flex-direction: row; margin-bottom: 8px;">
<button style="flex: 1; margin-right: 8px;" type="primary" size="mini" @click="sendBytes">发送字节</button>
<button style="flex: 2; margin-right: 8px;" type="primary" size="mini" @click="sendHexString">发送十六进制</button>
<button style="flex: 1; margin-right: 8px;" type="primary" size="mini" @click="sendText">发送文本</button>
</view>
<view style="display: flex; flex-direction: row; margin-bottom: 8px;">
<button style="flex: 1; margin-right: 8px;" type="primary" size="mini" @click="sendSelectFile">选择文件并发送</button>
<button style="flex: 1;" type="primary" size="mini" @click="openBluetoothSettings">跳转到蓝牙设置界面</button>
</view>
</view>
<!-- 设备列表 -->
<view style="display: flex; flex-direction: column; border: 1px solid #F2F2F2;">
<view style="height: 40px; display: flex; align-items: center; background-color: #F2F2F2; padding: 0 8px;">
<text style="font-size: 16px;">设备列表</text>
</view>
<scroll-view class="scroll-Y" scroll-y="true" direction="vertical">
<view v-for="(item, index) in devices" :key="index"
style="display: flex; flex-direction: row; justify-content: space-between; align-items: center; padding: 8px; font-size: 12px;">
<view>
<view>{{item.name}}</view>
<view>{{item.address}}</view>
</view>
<view v-if="item.bondState == 12"><button type="primary" size="mini" @click="openDialog(item)">连接</button>
</view>
<view v-if="item.bondState == 10"><button type="primary" size="mini" @click="openDialog(item)">配对</button>
</view>
</view>
</scroll-view>
</view>
<!-- 日志 -->
<view style="display: flex; flex-direction: column; border: 1px solid #F2F2F2;">
<view style="height: 40px; display: flex; align-items: center; background-color: #F2F2F2; padding: 0 8px;">
<text style="font-size: 16px;">日志</text>
</view>
<scroll-view class="scroll-Y" scroll-y="true" direction="vertical">
<view v-for="(item, index) in logs" :key="index" style="padding: 8px; font-size: 12px;">{{item}}</view>
</scroll-view>
</view>
<!-- 配对或连接弹出层 -->
<uni-popup ref="refConnectPop" background-color="#fff" border-radius="8px">
<view style=" width: 600rpx;">
<!-- 标题 -->
<view
style="height: 40px; border-bottom: 1px solid #F2F2F2; display: flex; flex-direction: row; justify-content: center; align-items: center;">
{{device.name || ""}}
</view>
<view style="padding: 8px;">
<view>地址:{{device.address}}</view>
<view>状态:{{device.bondState == 12 ? "已配对" : "未配对"}}</view>
</view>
<!-- 按钮 -->
<view
style="display: flex; flex-direction: row; justify-content: space-between; align-items: center; padding: 8px 8px 16px 8px;">
<view style="flex: 1;">
<button type="primary" size="mini" v-if="device.bondState == 12" @click="unpairDevice">取消配对</button>
</view>
<view
style="flex: 1; display: flex; flex-direction: row; justify-content: space-between; align-items: center;">
<button type="primary" size="mini" @click="closeDialog">取消</button>
<button type="primary" size="mini" @click="connectOrPair">{{device.bondState == 12 ? "连接" : "配对"}}</button>
</view>
</view>
</view>
</uni-popup>
<!-- 接收文件请求弹窗 -->
<uni-popup ref="refFileRequestPop" background-color="#fff" border-radius="8px">
<view style=" width: 600rpx;">
<!-- 标题 -->
<view
style="height: 40px; border-bottom: 1px solid #F2F2F2; display: flex; flex-direction: row; justify-content: center; align-items: center;">
接收文件请求</view>
<!-- 内容 -->
<view style="padding: 8px;">
<view>发送设备:{{sendFileInfo.deviceName}}</view>
<view>文件名:{{sendFileInfo.fileName}}</view>
<view>文件大小:{{getFormatFileSize(sendFileInfo.fileSize)}}</view>
</view>
<!-- 按钮 -->
<view
style="display: flex; flex-direction: row; justify-content: space-between; align-items: center; padding: 8px 8px 16px 8px;">
<button type="primary" size="mini" @click="confirmFileReceive(false)">拒绝</button>
<button type="primary" size="mini" @click="confirmFileReceive(true)">接受</button>
</view>
</view>
</uni-popup>
<!-- 文件发送或接收进度弹窗 -->
<uni-popup ref="refFileProgressPop" background-color="#fff" border-radius="8px" :isMaskClick="false">
<view style="width: 600rpx;">
<!-- 标题 -->
<view
style="height: 40px; border-bottom: 1px solid #F2F2F2; display: flex; flex-direction: row; justify-content: center; align-items: center; margin-bottom: 16px;">
{{filePop.title}}
</view>
<!-- 进度条 -->
<view style="padding: 8px;">
<view style="border-radius: 10px; overflow: hidden;">
<progress :percent="filePop.percent" :stroke-width="20"></progress>
</view>
</view>
<!-- 进度信息展示 -->
<view
style="display: flex; flex-direction: row; justify-content: space-between; align-items: center; padding: 8px;">
<view style="font-size: 14px;">{{getFormatFileSize(filePop.size)}} / {{getFormatFileSize(filePop.totalSize)}}
</view>
<view style="font-size: 14px;">{{filePop.percent}}%</view>
</view>
</view>
</uni-popup>
</view>
</template>
<script>
// const module = uni.requireNativePlugin("leven-bluetooth-ToothModule");
import * as module from "@/uni_modules/leven-uts-bluetooth"
import {
formatFileSize
} from "@/utils/index.js"
export default {
data() {
return {
//设备列表
devices: [],
//日志
logs: [],
//连接或配对的设备信息
device: {},
//当前连接的设备编码
connectDeviceAddress: "",
//收到文件发送请求数据
sendFileInfo: {},
//发送或接收文件
filePop: {
//标题
title: "文件接收中...",
//进度
percent: 30.25,
//已接收/发送的大小
size: 17924222,
//文件总大小
totalSize: 36526417
},
//文件弹窗是否展示
filePopIsOpen: false
}
},
onUnload() {
module.release({
complete: (res) => {
console.log("释放资源" + JSON.stringify(res))
}
})
},
mounted() {
this.$nextTick(() => {
this.requestPermissions();
})
},
methods: {
//显示消息提示
showMessage(res) {
if (res.code != 0) {
uni.showToast({
title: res.message,
icon: "none"
})
}
},
//跳转到蓝牙设置界面
openBluetoothSettings() {
module.openBluetoothSettings({
success: (res) => {
console.log(res)
}
})
},
getFormatFileSize(size) {
return formatFileSize(size)
},
//拒绝或接受文件接收
confirmFileReceive(accept) {
module.confirmFileReceive({
params: {
accept: accept
},
complete: (res) => {
this.$refs.refFileRequestPop.close();
}
})
},
//选择文件并发送
sendSelectFile() {
// this.$refs.refFileProgressPop.open();
module.sendSelectFile({
complete: (res) => {
this.showMessage(res)
}
})
},
//发送文本
sendText() {
module.sendText({
params: {
content: "这是发送的文本数据",
},
complete: (res) => {
this.showMessage(res)
}
})
},
//发送十六进制数据
sendHexString() {
module.sendHexString({
params: {
content: "48 65 6C 6C 6F",
},
complete: (res) => {
this.showMessage(res)
}
})
},
//发送字节数据
sendBytes() {
module.sendBytes({
params: {
content: "这是发送的字节数据",
},
complete: (res) => {
this.showMessage(res)
}
})
},
//连接或配对设备
connectOrPair() {
if (this.device.bondState == 12) {
//已配对开始连接设备
module.connect({
params: {
address: this.device.address
},
complete: (res) => {
this.showMessage(res)
}
})
} else {
//配对
module.pairDevice({
params: {
address: this.device.address
},
complete: (res) => {
this.showMessage(res)
}
})
}
},
//取消配对
unpairDevice() {
module.unpairDevice({
params: {
address: this.device.address
},
complete: (res) => {
this.showMessage(res)
}
})
},
//关闭弹窗
closeDialog() {
this.$refs.refConnectPop.close()
},
//打开配对或连接弹窗
openDialog(data) {
this.device = data;
this.$refs.refConnectPop.open()
},
//停止搜索设备
stopDiscovery() {
module.stopDiscovery({})
},
//搜索设备
startDiscovery() {
this.devices = [];
module.startDiscovery({
complete: (res) => {
console.log("搜索设备:" + JSON.stringify(res))
}
})
},
//获取已配对设备
getPairedDevices() {
this.devices = [];
module.getPairedDevices({
complete: (res) => {
if (res.code == 0) {
let data = res.data;
this.devices = data.list;
}
}
})
},
//关闭蓝牙
disableBluetooth() {
module.disableBluetooth({})
},
//开启蓝牙
enableBluetooth() {
module.enableBluetooth({
complete: (res) => {
console.log(res)
}
})
},
//初始化蓝牙
init() {
module.init({
complete: (res) => {
console.log(res)
let data = res.data || {};
let type = data.type;
if (type == "onDeviceFound") {
//发现新设备
let device = data.device;
this.devices.push(device);
this.logs.push("发现新设备:" + (device.name || "未知名称") + ":" + device.address)
} else if (type == "onDiscoveryStopped") {
//停止扫描
this.logs.push("停止扫描")
} else if (type == "onPairingCompleted") {
//配对或取消配对完成
this.getPairedDevices();
this.closeDialog();
} else if (type == "onConnectionStateChanged") {
//连接状态更新
this.closeDialog();
let device = data.device;
let state = data.state;
let stateStr = "";
switch (state) {
case 0:
stateStr = "已断开";
break
case 1:
stateStr = "连接中";
break
case 2:
stateStr = "连接监听中";
break
case 3:
stateStr = "已连接";
break
default:
stateStr = "未知";
}
this.logs.push("连接状态: " + stateStr)
this.logs.push("设备: " + device.name)
if (state == 3 && device) {
this.connectDeviceAddress = device.address
}
} else if (type == "onDataReceived") {
//收到数据
let dataString = data.dataString || ""
this.logs.push("接收到数据:" + dataString)
} else if (type == "onFileReceiveRequest") {
//收到发送文件的请求
this.sendFileInfo = data;
this.$refs.refFileRequestPop.open()
} else if (type == "onFileSendProgress") {
this.filePop.title = "文件发送中";
if (!this.filePopIsOpen) {
this.$refs.refFileProgressPop.open();
this.filePopIsOpen = true;
}
this.filePop.percent = ((data.sentSize / data.totalSize) * 100).toFixed(2);
this.filePop.size = data.sentSize;
this.filePop.totalSize = data.totalSize;
} else if (type == "onFileReceiveProgress") {
this.filePop.title = "文件接收中";
if (!this.filePopIsOpen) {
this.$refs.refFileProgressPop.open();
this.filePopIsOpen = true;
}
this.filePop.percent = ((data.receivedSize / data.totalSize) * 100).toFixed(2);
this.filePop.size = data.receivedSize;
this.filePop.totalSize = data.totalSize;
} else if (type == "onFileReceiveComplete" || type == "onFileSendComplete") {
this.$refs.refFileProgressPop.close();
this.filePopIsOpen = false;
if (type == "onFileReceiveComplete") {
uni.showToast({
title: "文件接收完毕",
icon: "none"
})
} else {
uni.showToast({
title: "文件发送完毕",
icon: "none"
})
}
}
}
})
},
requestPermissions() {
//申请权限
module.requestPermissions({
params: {
permissions: [
'android.permission.BLUETOOTH_CONNECT',
'android.permission.BLUETOOTH_SCAN',
'android.permission.ACCESS_FINE_LOCATION'
]
},
complete: (res) => {
if (res.code == 0) {
//初始化蓝牙
this.init();
}
}
})
}
}
}
</script>
<style>
.content {
padding: 16px;
}
.scroll-Y {
height: 200px;
}
</style>vue
<template>
<!-- #ifdef APP -->
<scroll-view style="flex:1; padding: 8px;">
<!-- #endif -->
<!-- 按钮组 -->
<view>
<view style="flex-direction: row; margin-bottom: 8px;">
<button style="flex: 1; margin-right: 8px;" type="primary" size="mini" @click="enableBluetooth">开启蓝牙</button>
<button style="flex: 1; margin-right: 8px;" type="primary" size="mini" @click="disableBluetooth">关闭蓝牙</button>
</view>
<view style="flex-direction: row; margin-bottom: 8px;">
<button style="flex: 1;" type="primary" size="mini" @click="getPairedDevices">获取已配对设备</button>
</view>
<view style="flex-direction: row; margin-bottom: 8px;">
<button style="flex: 1; margin-right: 8px;" type="primary" size="mini" @click="startDiscovery">搜索设备</button>
<button style="flex: 1; margin-right: 8px;" type="primary" size="mini" @click="stopDiscovery">停止搜索</button>
</view>
<view style="display: flex; flex-direction: row; margin-bottom: 8px;">
<button style="flex: 1; margin-right: 8px;" type="primary" size="mini" @click="sendBytes">发送字节</button>
<button style="flex: 2; margin-right: 8px;" type="primary" size="mini" @click="sendHexString">发送十六进制</button>
<button style="flex: 1; margin-right: 8px;" type="primary" size="mini" @click="sendText">发送文本</button>
</view>
<view style="display: flex; flex-direction: row; margin-bottom: 8px;">
<button style="flex: 1; margin-right: 8px;" type="primary" size="mini" @click="sendSelectFile">选择文件并发送</button>
<button style="flex: 1;" type="primary" size="mini" @click="openBluetoothSettings">跳转到蓝牙设置界面</button>
</view>
</view>
<!-- 设备列表 -->
<view style="flex-direction: column; border: 1px solid #F2F2F2;">
<view style="height: 40px; justify-content: center; background-color: #F2F2F2; padding: 0 8px;">
<text style="font-size: 16px;">设备列表</text>
</view>
<scroll-view direction="vertical" class="scroll-Y" :show-scrollbar="false">
<view v-for="(item, index) in devices" :key="index"
style="flex-direction: row; justify-content: space-between; align-items: center; padding: 8px;">
<view>
<view><text class="text-style">{{item.name}}</text></view>
<view><text class="text-style">{{item.address}}</text></view>
</view>
<view v-if="item.bondState == 12"><button type="primary" size="mini" @click="openDialog(item)">连接</button>
</view>
<view v-else-if="item.bondState == 10"><button type="primary" size="mini"
@click="openDialog(item)">配对</button></view>
</view>
</scroll-view>
</view>
<!-- 日志 -->
<view style="flex-direction: column; border: 1px solid #F2F2F2;">
<view style="height: 40px; justify-content: center; background-color: #F2F2F2; padding: 0 8px;">
<text style="font-size: 16px;">日志</text>
</view>
<scroll-view class="scroll-Y" direction="vertical" :show-scrollbar="false">
<view v-for="(item, index) in logs" :key="index" style="padding: 8px;">
<text class="text-style">{{item}}</text>
</view>
</scroll-view>
</view>
<!-- #ifdef APP -->
</scroll-view>
<!-- #endif -->
<!-- 配对或连接弹出层 -->
<rice-popup v-model:show="connectPopShow" position="center" :closeable="false" bgColor="#FFFFFF" radius="8px">
<view class="popup-center">
<!-- 标题 -->
<view
style="height: 40px; border-bottom: 1px solid #F2F2F2; flex-direction: row; justify-content: center; align-items: center;">
<text class="text-title-style">{{device.name ?? ""}}</text>
</view>
<view style="padding: 8px;">
<view><text class="text-style">地址:{{device.address}}</text></view>
<view><text class="text-style">状态:{{device.bondState == 12 ? "已配对" : "未配对"}}</text></view>
</view>
<!-- 按钮 -->
<view
style="flex-direction: row; justify-content: space-between; align-items: center; padding: 8px 8px 16px 8px;">
<view style="flex: 1;">
<button type="primary" size="mini" v-if="device.bondState == 12" @click="unpairDevice">取消配对</button>
</view>
<view style="flex: 1; display: flex; flex-direction: row; justify-content: space-between; align-items: center;">
<button type="primary" size="mini" @click="connectPopShow = false">取消</button>
<button type="primary" size="mini" @click="connectOrPair">{{device.bondState == 12 ? "连接" : "配对"}}</button>
</view>
</view>
</view>
</rice-popup>
<!-- 接收文件请求弹窗 -->
<rice-popup v-model:show="fileRequestPopShow" position="center" :closeable="false" bgColor="#FFFFFF" radius="8px"
:closeOnClickOverlay="false">
<view class="popup-center">
<!-- 标题 -->
<view
style="height: 40px; border-bottom: 1px solid #F2F2F2; flex-direction: row; justify-content: center; align-items: center;">
<text class="text-title-style">接收文件请求</text>
</view>
<!-- 内容 -->
<view style="padding: 8px;">
<view><text class="text-style">发送设备:{{sendFileInfo.deviceName}}</text></view>
<view><text class="text-style">文件名:{{sendFileInfo.fileName}}</text></view>
<view><text class="text-style">文件大小:{{getFormatFileSize(sendFileInfo.fileSize)}}</text></view>
</view>
<!-- 按钮 -->
<view
style="flex-direction: row; justify-content: space-between; align-items: center; padding: 8px 8px 16px 8px;">
<button type="primary" size="mini" @click="confirmFileReceive(false)">拒绝</button>
<button type="primary" size="mini" @click="confirmFileReceive(true)">接受</button>
</view>
</view>
</rice-popup>
<!-- 文件发送或接收进度弹窗 -->
<rice-popup v-model:show="fileProgressPopShow" position="center" :closeable="false" bgColor="#FFFFFF" radius="8px"
:closeOnClickOverlay="false">
<view class="popup-center">
<!-- 标题 -->
<view
style="height: 40px; border-bottom: 1px solid #F2F2F2; flex-direction: row; justify-content: center; align-items: center; margin-bottom: 16px;">
{{filePop.title}}
</view>
<!-- 进度条 -->
<view style="padding: 8px;">
<rice-progress :percentage="filePop.percent" :show-text="false" stroke-width="16px"></rice-progress>
</view>
<!-- 进度信息展示 -->
<view style="flex-direction: row; justify-content: space-between; align-items: center; padding: 8px;">
<view>
<text class="text-style">{{getFormatFileSize(filePop.size)}} / {{getFormatFileSize(filePop.totalSize)}}</text>
</view>
<view><text class="text-style">{{filePop.percent}}%</text></view>
</view>
</view>
</rice-popup>
</template>
<script setup>
import * as module from "@/uni_modules/leven-uts-bluetooth"
import { formatFileSize } from "@/utils/bluetooth"
import { LevenResult } from "@/uni_modules/leven-uts-bluetooth"
//设备列表属性
type deviceItem = {
//设备名称
name ?: string,
//设备地址
address ?: string,
//设备配对状态
bondState ?: number
}
//文件属性
type fileItem = {
//设备名称
deviceName ?: string,
//文件名称
fileName ?: string,
//文件大小
fileSize ?: number
};
//发送或接收文件属性
type filePopItem = {
//标题
title : string,
//进度
percent : number,
//已接收/发送的大小
size : number,
//文件总大小
totalSize : number
}
//配对或连接弹出层
const connectPopShow = ref(false)
//接收文件请求弹窗
const fileRequestPopShow = ref(false)
//文件发送或接收进度弹窗
const fileProgressPopShow = ref(false)
//设备列表
const devices = ref<deviceItem[]>([])
//连接或配对的设备信息
const device = ref<deviceItem>({})
//日志列表
const logs = ref<string[]>([])
//收到文件发送请求数据
const sendFileInfo = ref<fileItem>({})
//发送或接收文件
const filePop = ref<filePopItem>({
title: "文件接收中...",
percent: 0,
size: 0,
totalSize: 0
})
//当前连接的设备编码
const connectDeviceAddress = ref("")
//转换文件大小
function getFormatFileSize(size : number | null) {
return formatFileSize(size)
}
//显示消息提示
function showMessage(res : LevenResult) {
if (res.code != 0) {
uni.showToast({
title: res.message,
icon: "none"
})
}
}
//跳转到蓝牙设置界面
function openBluetoothSettings() {
module.openBluetoothSettings({
success: (res) => {
console.log(res)
}
})
}
//拒绝或接受文件接收
function confirmFileReceive(accept : boolean) {
module.confirmFileReceive({
params: {
accept: accept
},
complete: (res) => {
fileRequestPopShow.value = false
showMessage(res)
}
})
}
//选择文件并发送
function sendSelectFile() {
module.sendSelectFile({
complete: (res) => {
showMessage(res)
}
})
}
//发送文本
function sendText() {
module.sendText({
params: {
content: "这是发送的文本数据",
},
complete: (res) => {
showMessage(res)
}
})
}
//发送十六进制数据
function sendHexString() {
module.sendHexString({
params: {
content: "48 65 6C 6C 6F",
},
complete: (res) => {
showMessage(res)
}
})
}
//发送字节数据
function sendBytes() {
module.sendBytes({
params: {
content: "这是发送的字节数据",
},
complete: (res) => {
showMessage(res)
}
})
}
//连接或配对设备
function connectOrPair() {
if (device.value?.bondState == 12) {
//已配对开始连接设备
module.connect({
params: {
address: device.value?.address
},
complete: (res) => {
showMessage(res)
}
})
} else {
//配对
module.pairDevice({
params: {
address: device.value?.address
},
complete: (res) => {
showMessage(res)
}
})
}
}
//取消配对
function unpairDevice() {
module.unpairDevice({
params: {
address: device.value?.address
},
complete: (res) => {
showMessage(res)
}
})
}
//打开连接或配对弹窗
function openDialog(data : deviceItem) {
device.value = data
connectPopShow.value = true
}
//停止搜索
function stopDiscovery() {
module.stopDiscovery({
complete: (res) => {
console.log("停止搜索:" + JSON.stringify(res))
}
})
}
//搜索设备
function startDiscovery() {
devices.value = [];
module.startDiscovery({
complete: (res) => {
console.log("搜索设备:" + JSON.stringify(res))
}
})
}
//获取已配对设备
function getPairedDevices() {
devices.value = [];
module.getPairedDevices({
complete: (res) => {
if (res.code == 0) {
let data = res.data;
let list : UTSJSONObject[] = data.getArray("list") as UTSJSONObject[]
if (list.length > 0) {
list.forEach((item : UTSJSONObject | null) => {
let deviceData : deviceItem = {
name: item?.getString("name"),
address: item?.getString("address"),
bondState: item?.getNumber("bondState")
}
devices.value.push(deviceData)
})
}
}
}
})
}
//关闭蓝牙
function disableBluetooth() {
module.disableBluetooth({
complete: (res) => {
console.log(res)
}
})
}
//开启蓝牙
function enableBluetooth() {
module.enableBluetooth({
complete: (res) => {
console.log(res)
}
})
}
//初始化蓝牙
function init() {
module.init({
complete: (res) => {
console.log(res)
let data = res.data;
let type = data.getString("type");
if (type == "onInit") {
if (res.code == 0) {
logs.value.push("初始化成功")
} else {
logs.value.push("初始化失败")
}
} else if (type == "onDeviceFound") {
//发现新设备
let device = data.getJSON("device");
devices.value.push({
name: device?.getString("name"),
address: device?.getString("address"),
bondState: device?.getNumber("bondState")
} as deviceItem)
logs.value.push("发现新设备:" + (device?.getString("name") ?? "未知名称") + ":" + device?.getString("address"))
} else if (type == "onDiscoveryStopped") {
//停止扫描
logs.value.push("停止扫描")
} else if (type == "onPairingCompleted") {
//配对或取消配对完成
getPairedDevices()
connectPopShow.value = false;
} else if (type == "onConnectionStateChanged") {
//连接状态更新
connectPopShow.value = false;
let deviceInfo = data.getJSON("device");
let state = data.getNumber("state");
let stateStr = "";
switch (state) {
case 0:
stateStr = "已断开";
break
case 1:
stateStr = "连接中";
break
case 2:
stateStr = "连接监听中";
break
case 3:
stateStr = "已连接";
break
default:
stateStr = "未知";
}
logs.value.push("连接状态: " + stateStr)
logs.value.push("设备: " + deviceInfo?.getString("name"))
if (state == 3 && deviceInfo != null) {
connectDeviceAddress.value = deviceInfo?.getString("address") ?? ""
}
} else if (type == "onDataReceived") {
//收到数据
let dataString = data.getString("dataString")
logs.value.push("接收到数据:" + dataString)
} else if (type == "onFileReceiveRequest") {
//收到发送文件的请求
sendFileInfo.value = {
fileName: data.getString("fileName"),
deviceName: data.getString("deviceName"),
fileSize: data.getNumber("fileSize")
} as fileItem;
fileRequestPopShow.value = true
} else if (type == "onFileSendProgress") {
filePop.value.title = "文件发送中";
if (!fileProgressPopShow.value) {
fileProgressPopShow.value = true
}
let sentSize : number = data.getNumber("sentSize")!
let totalSize : number = data.getNumber("totalSize")!
filePop.value.percent = parseFloat(((sentSize / totalSize) * 100).toFixed(2));
filePop.value.size = sentSize;
filePop.value.totalSize = totalSize;
} else if (type == "onFileReceiveProgress") {
filePop.value.title = "文件接收中";
if (!fileProgressPopShow.value) {
fileProgressPopShow.value = true
}
let receivedSize : number = data.getNumber("receivedSize")!;
let totalSize : number = data.getNumber("totalSize")!
filePop.value.percent = parseFloat(((receivedSize / totalSize) * 100).toFixed(2));
filePop.value.size = receivedSize;
filePop.value.totalSize = totalSize;
} else if (type == "onFileReceiveComplete" || type == "onFileSendComplete") {
fileProgressPopShow.value = false;
if (type == "onFileReceiveComplete") {
uni.showToast({
title: "文件接收完毕",
icon: "none"
})
} else {
uni.showToast({
title: "文件发送完毕",
icon: "none"
})
}
}
}
})
}
//申请权限
function requestPermissions() {
module.requestPermissions({
params: {
permissions: [
'android.permission.BLUETOOTH_CONNECT',
'android.permission.BLUETOOTH_SCAN',
'android.permission.ACCESS_FINE_LOCATION'
]
},
complete: (res) => {
if (res.code == 0) {
//初始化蓝牙
init();
}
}
})
}
//页面加载
onMounted(() => {
//申请权限
requestPermissions();
})
//页面卸载
onUnload(() => {
module.release({
complete: (res) => {
console.log("释放资源" + JSON.stringify(res))
}
})
})
</script>
<style>
.scroll-Y {
height: 200px;
}
.text-title-style {
font-size: 16px;
}
.text-style {
font-size: 12px;
}
.popup-center {
width: 600rpx;
}
</style>