解决小程序上传时出现的问题。

This commit is contained in:
cst61
2026-02-01 10:55:35 +08:00
parent 802ffbb2f3
commit eef85dce4a
22 changed files with 735 additions and 645 deletions

View File

@@ -119,7 +119,7 @@
</template>
<script>
import bluetoothRecorder from '@/common/bluetooth.js'
import bluetoothRecorder from './utils/bluetooth.js'
// #ifdef APP
import statusBar from '@/uni_modules/uni-nav-bar/components/uni-nav-bar/uni-status-bar'
// #endif

View File

@@ -0,0 +1,516 @@
/**
* 蓝牙录音设备管理工具类
* 支持蓝牙设备搜索、连接、数据接收等功能
*/
class BluetoothRecorder {
constructor() {
this.adapterState = false // 蓝牙适配器状态
this.isScanning = false // 是否正在扫描
this.connectedDeviceId = null // 已连接的设备ID
this.deviceList = [] // 发现的设备列表
this.services = [] // 蓝牙服务列表
this.characteristics = [] // 特征值列表
this.notifyCharacteristicId = null // 用于接收数据的特征值ID
this.writeCharacteristicId = null // 用于发送数据的特征值ID
// 音频数据缓冲区
this.audioBuffer = []
this.isReceiving = false
// 回调函数
this.onDeviceFound = null // 发现设备回调
this.onDeviceConnected = null // 设备连接回调
this.onDeviceDisconnected = null // 设备断开回调
this.onAudioDataReceived = null // 音频数据接收回调
this.onError = null // 错误回调
}
/**
* 初始化蓝牙适配器
*/
async initBluetoothAdapter() {
return new Promise((resolve, reject) => {
uni.openBluetoothAdapter({
success: (res) => {
console.log('蓝牙适配器初始化成功', res)
this.adapterState = true
this._listenAdapterState()
resolve(res)
},
fail: (err) => {
console.error('蓝牙适配器初始化失败', err)
this.adapterState = false
this._handleError('蓝牙适配器初始化失败', err)
reject(err)
}
})
})
}
/**
* 监听蓝牙适配器状态变化
*/
_listenAdapterState() {
uni.onBluetoothAdapterStateChange((res) => {
console.log('蓝牙适配器状态变化', res)
this.adapterState = res.available
if (!res.available) {
this.connectedDeviceId = null
this._handleError('蓝牙适配器不可用', res)
}
})
}
/**
* 开始搜索蓝牙设备
* @param {Object} options - 搜索选项
* @param {Array} options.services - 服务UUID列表可选
* @param {number} options.interval - 上报间隔(毫秒)
* @param {boolean} options.allowDuplicatesKey - 是否允许重复上报
*/
async startScan(options = {}) {
if (this.isScanning) {
console.warn('已经在扫描中')
return
}
if (!this.adapterState) {
await this.initBluetoothAdapter()
}
return new Promise((resolve, reject) => {
// 监听设备发现
uni.onBluetoothDeviceFound((res) => {
const devices = res.devices || []
devices.forEach(device => {
// 过滤已存在的设备
const exists = this.deviceList.find(d => d.deviceId === device.deviceId)
if (!exists) {
this.deviceList.push({
deviceId: device.deviceId,
name: device.name || '未知设备',
RSSI: device.RSSI,
advertisData: device.advertisData,
advertisServiceUUIDs: device.advertisServiceUUIDs,
localName: device.localName
})
// 触发设备发现回调
if (this.onDeviceFound) {
this.onDeviceFound(device)
}
}
})
})
// 开始搜索
uni.startBluetoothDevicesDiscovery({
services: options.services || [],
allowDuplicatesKey: options.allowDuplicatesKey || false,
interval: options.interval || 0,
success: (res) => {
console.log('开始搜索蓝牙设备', res)
this.isScanning = true
resolve(res)
},
fail: (err) => {
console.error('搜索蓝牙设备失败', err)
this.isScanning = false
this._handleError('搜索蓝牙设备失败', err)
reject(err)
}
})
})
}
/**
* 停止搜索蓝牙设备
*/
async stopScan() {
if (!this.isScanning) {
return
}
return new Promise((resolve, reject) => {
uni.stopBluetoothDevicesDiscovery({
success: (res) => {
console.log('停止搜索蓝牙设备', res)
this.isScanning = false
resolve(res)
},
fail: (err) => {
console.error('停止搜索失败', err)
this._handleError('停止搜索失败', err)
reject(err)
}
})
})
}
/**
* 连接蓝牙设备
* @param {string} deviceId - 设备ID
*/
async connectDevice(deviceId) {
if (this.connectedDeviceId === deviceId) {
console.warn('设备已连接')
return
}
return new Promise((resolve, reject) => {
uni.createBLEConnection({
deviceId: deviceId,
success: async (res) => {
console.log('蓝牙设备连接成功', res)
this.connectedDeviceId = deviceId
// 监听连接状态
this._listenConnectionState(deviceId)
try {
// 获取服务列表
await this.getServices(deviceId)
// 触发连接成功回调
if (this.onDeviceConnected) {
this.onDeviceConnected(deviceId)
}
resolve(res)
} catch (err) {
reject(err)
}
},
fail: (err) => {
console.error('蓝牙设备连接失败', err)
this._handleError('蓝牙设备连接失败', err)
reject(err)
}
})
})
}
/**
* 监听连接状态
*/
_listenConnectionState(deviceId) {
uni.onBLEConnectionStateChange((res) => {
console.log('蓝牙连接状态变化', res)
if (res.deviceId === deviceId) {
if (!res.connected) {
// 连接断开
this.connectedDeviceId = null
this.isReceiving = false
if (this.onDeviceDisconnected) {
this.onDeviceDisconnected(deviceId)
}
}
}
})
}
/**
* 断开蓝牙设备连接
*/
async disconnectDevice() {
if (!this.connectedDeviceId) {
return
}
return new Promise((resolve, reject) => {
uni.closeBLEConnection({
deviceId: this.connectedDeviceId,
success: (res) => {
console.log('蓝牙设备断开成功', res)
this.connectedDeviceId = null
this.isReceiving = false
this.audioBuffer = []
resolve(res)
},
fail: (err) => {
console.error('蓝牙设备断开失败', err)
this._handleError('蓝牙设备断开失败', err)
reject(err)
}
})
})
}
/**
* 获取蓝牙设备服务列表
* @param {string} deviceId - 设备ID
*/
async getServices(deviceId) {
return new Promise((resolve, reject) => {
uni.getBLEDeviceServices({
deviceId: deviceId,
success: async (res) => {
console.log('获取服务列表成功', res)
this.services = res.services || []
// 遍历服务,获取特征值
for (const service of this.services) {
try {
await this.getCharacteristics(deviceId, service.uuid)
} catch (err) {
console.warn('获取特征值失败', service.uuid, err)
}
}
resolve(res)
},
fail: (err) => {
console.error('获取服务列表失败', err)
this._handleError('获取服务列表失败', err)
reject(err)
}
})
})
}
/**
* 获取特征值列表
* @param {string} deviceId - 设备ID
* @param {string} serviceId - 服务UUID
*/
async getCharacteristics(deviceId, serviceId) {
return new Promise((resolve, reject) => {
uni.getBLEDeviceCharacteristics({
deviceId: deviceId,
serviceId: serviceId,
success: (res) => {
console.log('获取特征值成功', serviceId, res)
const chars = res.characteristics || []
this.characteristics.push(...chars)
// 查找可用于通知和写入的特征值
chars.forEach(char => {
// 查找可通知的特征值(用于接收音频数据)
if (char.properties.notify || char.properties.indicate) {
if (!this.notifyCharacteristicId) {
this.notifyCharacteristicId = {
deviceId: deviceId,
serviceId: serviceId,
characteristicId: char.uuid
}
// 启用通知
this.enableNotify(deviceId, serviceId, char.uuid)
}
}
// 查找可写入的特征值(用于发送命令)
if (char.properties.write || char.properties.writeNoResponse) {
if (!this.writeCharacteristicId) {
this.writeCharacteristicId = {
deviceId: deviceId,
serviceId: serviceId,
characteristicId: char.uuid
}
}
}
})
resolve(res)
},
fail: (err) => {
console.error('获取特征值失败', err)
reject(err)
}
})
})
}
/**
* 启用特征值通知(用于接收音频数据)
* @param {string} deviceId - 设备ID
* @param {string} serviceId - 服务UUID
* @param {string} characteristicId - 特征值UUID
*/
async enableNotify(deviceId, serviceId, characteristicId) {
return new Promise((resolve, reject) => {
uni.notifyBLECharacteristicValueChange({
deviceId: deviceId,
serviceId: serviceId,
characteristicId: characteristicId,
state: true, // 启用通知
success: (res) => {
console.log('启用通知成功', res)
// 监听特征值变化(接收音频数据)
uni.onBLECharacteristicValueChange((res) => {
this._handleAudioData(res.value)
})
this.isReceiving = true
resolve(res)
},
fail: (err) => {
console.error('启用通知失败', err)
this._handleError('启用通知失败', err)
reject(err)
}
})
})
}
/**
* 处理接收到的音频数据
* @param {ArrayBuffer} data - 音频数据
*/
_handleAudioData(data) {
// 将 ArrayBuffer 转换为 Base64 或保存到缓冲区
const base64 = uni.arrayBufferToBase64(data)
// 添加到缓冲区
this.audioBuffer.push({
data: base64,
timestamp: Date.now()
})
// 触发回调
if (this.onAudioDataReceived) {
this.onAudioDataReceived({
data: base64,
arrayBuffer: data,
timestamp: Date.now()
})
}
}
/**
* 读取特征值
* @param {string} deviceId - 设备ID
* @param {string} serviceId - 服务UUID
* @param {string} characteristicId - 特征值UUID
*/
async readCharacteristic(deviceId, serviceId, characteristicId) {
return new Promise((resolve, reject) => {
uni.readBLECharacteristicValue({
deviceId: deviceId,
serviceId: serviceId,
characteristicId: characteristicId,
success: (res) => {
console.log('读取特征值成功', res)
resolve(res)
},
fail: (err) => {
console.error('读取特征值失败', err)
this._handleError('读取特征值失败', err)
reject(err)
}
})
})
}
/**
* 写入特征值(发送命令到设备)
* @param {ArrayBuffer} value - 要写入的数据
*/
async writeCharacteristic(value) {
if (!this.writeCharacteristicId) {
throw new Error('未找到可写入的特征值')
}
const { deviceId, serviceId, characteristicId } = this.writeCharacteristicId
return new Promise((resolve, reject) => {
uni.writeBLECharacteristicValue({
deviceId: deviceId,
serviceId: serviceId,
characteristicId: characteristicId,
value: value,
success: (res) => {
console.log('写入特征值成功', res)
resolve(res)
},
fail: (err) => {
console.error('写入特征值失败', err)
this._handleError('写入特征值失败', err)
reject(err)
}
})
})
}
/**
* 获取已缓存的音频数据
*/
getAudioBuffer() {
return this.audioBuffer
}
/**
* 清空音频缓冲区
*/
clearAudioBuffer() {
this.audioBuffer = []
}
/**
* 合并音频数据为完整文件Base64格式
*/
mergeAudioData() {
return this.audioBuffer.map(item => item.data).join('')
}
/**
* 处理错误
*/
_handleError(message, error) {
console.error(`[BluetoothRecorder] ${message}`, error)
if (this.onError) {
this.onError({
message,
error
})
}
}
/**
* 关闭蓝牙适配器
*/
async closeBluetoothAdapter() {
// 先断开连接
if (this.connectedDeviceId) {
await this.disconnectDevice()
}
// 停止扫描
if (this.isScanning) {
await this.stopScan()
}
return new Promise((resolve, reject) => {
uni.closeBluetoothAdapter({
success: (res) => {
console.log('关闭蓝牙适配器成功', res)
this.adapterState = false
resolve(res)
},
fail: (err) => {
console.error('关闭蓝牙适配器失败', err)
reject(err)
}
})
})
}
/**
* 获取设备列表
*/
getDeviceList() {
return this.deviceList
}
/**
* 清空设备列表
*/
clearDeviceList() {
this.deviceList = []
}
}
// 导出单例
export default new BluetoothRecorder()

View File

@@ -62,8 +62,7 @@
let cdbRef;
import statusBar from "@/uni_modules/uni-nav-bar/components/uni-nav-bar/uni-status-bar";
import Gps from '@/uni_modules/json-gps/js_sdk/gps.js';
const gps = new Gps(),db = uniCloud.database();
const db = uniCloud.database();
export default {
components: {
@@ -124,25 +123,7 @@
} catch (e) {
this.keyword = '';
}
//这里仅演示如何在onShow生命周期获取设备位置并在设备或者应用没有权限时自动引导。设置完毕自动重新获取。
//你可以基于他做自己的业务,比如:根据距离由近到远排序列表数据等
// uni.showLoading({
// title:"获取定位中"
// });
//默认h5端不获取定位
// #ifndef H5 || APP-HARMONY
let location = await gps.getLocation({
geocode: true
})
// console.log(location);
// #endif
// if(location){
// uni.showToast({
// title: JSON.stringify(location),
// icon: 'none'
// });
// }
// uni.hideLoading()
// GPS定位功能已被移除如需要请重新添加
},
methods: {
searchClick(e) { //点击搜索框