最近做用uniapp做大屏的安卓操作系统,遇到的问题和方法总结

首先需要就是和硬件的交互,就是RFID感应将读取的卡的数据通过MQTT传给前端,以及前端通过后台返回的仓储位置进行一键寻物,点亮对应储位的指示灯,语音播报用的是TTS,做的这个项目有点类似现在菜鸟驿站那种扫码寻物的感觉,反正都是仓储管理,不同的是,我们这个有拿走有归还!

1. 首先要使用3.0.0版本的MQTT,"mqtt": "^3.0.0",很多时候H5正常App会出现一些莫名其妙的错误
-
错误1 (4.x的版本App有这个问题)
-
错误2 (3.0.0的版本有这个问题)
js
TypeError: socketTask.onOpen is not a function
解决方式方式在 node_modlues 中 mqtt/dist/mqtt.js里将
js
socketTask = wx.connectSocket({
url: url,
protocols: websocketSubProtocol,
success: () => {} //增加这行(没错就是这一行)
})
- mqtt 封装支持H5和App,可以拿过来直接使用只需修改自己对应的参数
js
//@ts-ignore
let url = null;
// #ifdef H5
import mqtt from "mqtt/dist/mqtt.js";
url = 'ws://x.x.x.x:9527/mqtt';
// #endif
// #ifdef MP-WEIXIN||APP-PLUS
import * as mqtt from 'mqtt/dist/mqtt.js'
url = 'wx://x.x.x.x:9527/mqtt';
// #endif
const mqttTool = {
client: null,
subscriptions: new Map(), // 用于存储订阅的主题和对应的处理函数
};
/** 连接Mqtt */
mqttTool.connect = function() {
const options = {
username: "",
password: "",
cleanSession: true,
keepAlive: 30,
clientId: "mqtt_" + Math.random().toString(16).substr(2, 8),
connectTimeout: 60000,
reconnection: true, //断开是否重连
reconnectPeriod: 5000, // 断开后每5秒重连一次
};
// 配置Mqtt地址
// let url = mqtt_url;
console.log("mqtt地址:", url);
mqttTool.client = mqtt.connect(url, options);
mqttTool.client.on("connect", (e) => {
console.log("mqtt连接成功");
// 重新订阅之前的主题
mqttTool.subscriptions.forEach((handler, topic) => {
mqttTool.client.subscribe(topic, { qos: 0 });
});
});
// 重新连接
mqttTool.client.on("reconnect", (error) => {
console.log("正在重连:", error);
});
// 发生错误
mqttTool.client.on("error", (error) => {
console.log("Mqtt客户端连接失败:", error);
// 销毁当前连接
mqttTool.client.end(true);
mqttTool.client = null;
// 重新创建连接
setTimeout(() => {
mqttTool.connect();
}, 5000);
});
// 断开连接
mqttTool.client.on("close", function(res) {
console.log("已断开Mqtt连接");
// 销毁当前连接
mqttTool.client.end(true);
mqttTool.client = null;
// 重新创建连接
setTimeout(() => {
mqttTool.connect();
}, 5000);
});
// 设置全局消息处理器
mqttTool.client.on("message", (topic, message) => {
const messageString = message.toString("utf8");
console.log("收到消息,主题:", topic, "内容:", messageString);
// 调用对应主题的处理函数
const handler = mqttTool.subscriptions.get(topic);
if (handler) {
handler(topic, messageString);
}
});
};
/** 断开连接 */
mqttTool.end = function() {
return new Promise((resolve, reject) => {
if (mqttTool.client == null) {
resolve("未连接");
console.log("未连接");
return;
}
mqttTool.client.end(true);
mqttTool.client = null;
mqttTool.subscriptions.clear(); // 清空订阅列表
console.log("Mqtt服务器已断开连接!");
resolve("连接终止");
});
};
/** 重新连接 */
mqttTool.reconnect = function() {
return new Promise((resolve, reject) => {
if (mqttTool.client == null) {
// 如果客户端不存在,重新连接
mqttTool.connect();
resolve("重新连接成功");
return;
}
// 销毁当前连接
mqttTool.client.end(true);
mqttTool.client = null;
// 重新创建连接
mqttTool.connect();
resolve("重新连接成功");
});
};
/** 消息订阅 */
mqttTool.subscribe = function(topic, messageHandler) {
return new Promise((resolve, reject) => {
if (mqttTool.client == null) {
resolve("未连接");
console.log("未连接");
return;
}
mqttTool.client.subscribe(topic, { qos: 0 }, function(err) {
if (!err) {
console.log("订阅成功,主题:", topic);
mqttTool.subscriptions.set(topic, messageHandler);
resolve("订阅成功");
} else {
console.log("订阅失败,主题:", topic);
resolve("订阅失败");
}
});
});
};
/** 取消订阅 */
mqttTool.unsubscribe = function(topic) {
return new Promise((resolve, reject) => {
if (mqttTool.client == null) {
resolve("未连接");
console.log("未连接");
return;
}
mqttTool.client.unsubscribe(topic, function(err) {
if (!err) {
mqttTool.subscriptions.delete(topic);
resolve("取消订阅成功");
console.log("取消订阅成功,主题:", topic);
} else {
resolve("取消订阅失败");
console.log("取消订阅失败,主题:", topic);
}
});
});
};
mqttTool.publish = function(topic, message, name) {
return new Promise((resolve, reject) => {
if (mqttTool.client == null) {
resolve("Mqtt客户端未连接");
console.log("Mqtt客户端未连接");
return;
}
mqttTool.client.publish(topic, message, { qos: 1 }, function(err) {
console.log("发送主题:", topic);
console.log("发送内容:", message);
if (!err) {
if (topic.indexOf("offline") > 0) {
console.log("[ " + name + " ] 影子指令发送成功");
resolve("[ " + name + " ] 影子指令发送成功");
} else {
console.log("[ " + name + " ] 指令发送成功");
resolve("[ " + name + " ] 指令发送成功");
}
} else {
console.log("[ " + name + " ] 指令发送失败");
reject("[ " + name + " ] 指令发送失败");
}
});
});
};
export default mqttTool;

在项目中使用
javascript
app.vue
可以将mqttTool.connect()连接放到App.vue中,然后在需要接收数据的地方订阅主题Topic
import mqttTool from './common/mqtt.js'; //mqtt连接
onMounted( () => {
mqttTool.connect();//mqtt连接
mqttTool.subscribe(`login`, (topic, msg) => { //订阅(可以放到需要接收消息的页面)
console.log(msg)// 订阅login主题接收的 mqtt返回的消息
});
})
2. TTS语音播放使用的是Uniapp插件,感谢牛逼的作者
ext.dcloud.net.cn/plugin?id=3...
- 插件下载下来一定要放到这个名字命名的文件夹里,其他的都不需要修改

- 封装成公共的全局方法使用
javascript
common 中voice.js--------------------------
const SpeechTTS = uni.requireNativePlugin("MT-TTS-Speech"); //引入包
// SpeechTTS.setEngine('com.google.android.tts');//加载谷歌的文字转语音包
SpeechTTS.init((callback) => {
console.log('>> tts: init success', '成功了!!!!!!!!!!!!!');
// SpeechTTS.speak({
// text: 'motherFucker!'
// });
})
export default SpeechTTS;
plugin.js中--------------------------
import tools from './index.js'
// #ifdef APP-PLUS
import SpeechTTS from './voice.js'
//#endif
// import countDown from './countDown.js'
export const ToolPlugin = {
install(app) {
app.config.globalProperties.$tools = tools;
// #ifdef APP-PLUS
app.config.globalProperties.$SpeechTTS = SpeechTTS;
//#endif
}
};
在main.js中--------------------------
import App from './App'
import store from '@/store';
import { ToolPlugin } from './common/plugin.js';
// #ifndef VUE3
import Vue from 'vue'
import './uni.promisify.adaptor'
Vue.config.productionTip = false
App.mpType = 'app'
const app = new Vue({
...App
})
app.$mount()
// #endif
// #ifdef VUE3
import { createSSRApp } from 'vue'
export function createApp() {
const app = createSSRApp(App);
app.use(ToolPlugin) //这里是使用ToolPlugin的所有方法
app.use(store)
return {
app
}
}
// #endif
- 在组件中使用
xml
<script setup>
const { proxy } = getCurrentInstance();
proxy.$SpeechTTS.speak({
text: '你好,叼毛'
});
</script>
注意: 因为使用了原生插件,我们做完这些还不够,要在配置中导入这个插件,然后测试的话记得要先自定义基座运行一次以后就可以在手机测试了,安卓设备有文字转语音设置,可以切换声音语速等

3.霍尼韦尔红外扫描枪普通的扫码枪,非PAD 在无输入框的情况读取(keyCode.js是我安卓设备无key值和code只有keyCode的值)
javascript
common 中keyCode.js 安卓中需要使用,h5不必
export default {
"7": "0",
"8": "1",
"9": "2",
"10": "3",
"11": "4",
"12": "5",
"13": "6",
"14": "7",
"15": "8",
"16": "9",
"29": "A",
"30": "B",
"31": "C",
"32": "D",
"33": "E",
"34": "F",
"35": "G",
"36": "H",
"37": "I",
"38": "J",
"39": "K",
"40": "L",
"41": "M",
"42": "N",
"43": "O",
"44": "P",
"45": "Q",
"46": "R",
"47": "S",
"48": "T",
"49": "U",
"50": "V",
"51": "W",
"52": "X",
"53": "Y",
"54": "Z",
"55": ",",
"56": ".",
"59": "",
"69": "-",
"70": "=",
"71": "{",
"72": "}",
"74": ":",
"75": "\"",
"81": "+"
}
common 中 scan.js
import keymap from "./keyCode.js";
export const useScan = (options) => {
let keypress = null;
const keyupSymbol = Symbol('keyupHandler');
const { onScanComplete } = options;
const codeResult = ref('');
const inputCache = ref('')
const onConfirm = (code) => {
console.log('拿到的code', code);
onScanComplete(code)
}
keypress = (e) => {
if (e.keyCode === 23 || e.key == 'Enter') {
onConfirm(inputCache.value)
inputCache.value = '';
} else {
// #ifdef APP-PLUS
inputCache.value += keymap[e.keyCode] || '';
// #endif
// #ifdef H5
if (e.key != 'Shift') {
inputCache.value += e.key;
}
// #endif
}
}
onMounted(() => {
// #ifdef APP-PLUS
plus.key.addEventListener("keydown", keypress);
// #endif
// #ifdef H5
document.addEventListener("keydown", keypress);
// #endif
})
return {
keyupSymbol // 可选:暴露标识符供外部管理
};
}
项目中使用
javascript
import { useScan } from "@/common/scan.js";
useScan({ //扫码枪
onScanComplete: (code) => {
console.log(code)//处理逻辑
}
})
3.人脸识别(我们这边就是拍照上传给后端后端去比对的,比较low的那种,不需要什么摇头点头的)用的是mcc-face3 好像2和1都有,就是3可以定制样式 支持H5和 app
ext.dcloud.net.cn/plugin?id=1...
在这里修改 用到了render.js 我还特地研究了一番,学习了

这个是在H5中的效果(捕捉到的一帧)
分享结束,散会!!!