1
0
forked from dyf/APP

650完成,670完成部分

This commit is contained in:
liub
2025-08-25 17:13:13 +08:00
parent 793fe7132b
commit bbf55c58f0
23 changed files with 16897 additions and 221 deletions

View File

@ -1,15 +1,16 @@
<script>
import request from '@/utils/request.js';
import bleTool from '@/utils/BleHelper.js';
import upgrade from '@/utils/update.js'
export default {
onLaunch: function() {
// uni.clearStorageSync();
var ble = bleTool.getBleTool();
},
onShow: function() {
console.log('App Show')
console.log('App Show');
upgrade.checkAndUpdateWgt("http://114.55.111.217/app/CheckUpdate");
},
onHide: function() {
console.log('App Hide')

View File

@ -0,0 +1,194 @@
<template>
<view>
<canvas type="2d" canvas-id="reusableCanvas" :width="currentCanvasWidth" :height="currentCanvasHeight"
class="offscreen-canvas"></canvas>
</view>
</template>
<script>
export default {
name: "TextToHexV1",
props: {
txts: {
type: Array,
default: () => [],
validator: (value) => value.every(item => typeof item === 'string')
},
fontSize: {
type: Number,
default: 16,
validator: (value) => value > 0 && value <= 100
},
bgColor: {
type: String,
default: "#ffffff"
},
color: {
type: String,
default: "#000000"
}
},
data() {
return {
// 当前Canvas的宽高动态调整
currentCanvasWidth: 0,
currentCanvasHeight: 0,
// Canvas上下文复用
ctx: null
};
},
computed: {
validTxts() {
return this.txts.filter(line => line.trim() !== '');
}
},
mounted() {
// 初始化Canvas上下文只创建一次
this.ctx = uni.createCanvasContext('reusableCanvas', this);
},
methods: {
/**
* 估算单行文本所需的Canvas宽度
*/
calcLineWidth(textLine) {
return textLine.length * this.fontSize;
},
/**
* 清除Canvas内容
*/
clearCanvas() {
this.ctx.setFillStyle(this.bgColor);
this.ctx.fillRect(0, 0, this.currentCanvasWidth, this.currentCanvasHeight);
},
/**
* 复用单个Canvas处理所有文本行
*/
async drawAndGetPixels() {
let convertCharToMatrix=function(imageData) {
// console.log("imgData=",imageData)
let matrix = [];
// 逐行处理
for (let y = 0; y < 16; y++) {
let byte1 = 0,
byte2 = 0;
// 每行16个像素分为两个字节
for (let x = 0; x < 16; x++) {
// 计算像素在imageData中的索引 (RGBA格式)
let index = (y * 16 + x) * 4;
let red = imageData[index];
// 黑色像素R值较低视为1白色视为0
let isBlack = red < 128;
if (x < 8) {
// 第一个字节左8位
if (isBlack) {
byte1 |= 0x80 >> x; // 从左到右设置位
}
} else {
// 第二个字节右8位
if (isBlack) {
byte2 |= 0x80 >> (x - 8);
}
}
}
// 将字节转换为两位十六进制字符串
matrix.push('0x' + byte1.toString(16).padStart(2, '0').toUpperCase());
matrix.push('0x' + byte2.toString(16).padStart(2, '0').toUpperCase());
}
return matrix;
}
let drawTxt=async (textLine)=> {
let result = {};
let ctx = this.ctx;
// 1. 动态调整Canvas尺寸
this.currentCanvasWidth = this.calcLineWidth(textLine);
this.currentCanvasHeight = this.fontSize;
// 2. 清空Canvas绘制背景
this.clearCanvas();
// 3. 设置文字样式
ctx.setFillStyle(this.color);
ctx.setTextBaseline('middle');
ctx.setFontSize(this.fontSize);
ctx.font = `${this.fontSize}px "PingFang SC", PingFang SC, Arial, sans-serif`;
// 4. 绘制当前行文本
let currentX = 0;
let currentY = this.fontSize / 2;
for (let j = 0; j < textLine.length; j++) {
let char = textLine[j];
ctx.fillText(char, currentX, currentY);
// 按实际字符宽度计算间距
let charWidth = ctx.measureText(char).width;
currentX += charWidth;
}
// 5. 异步绘制并获取像素数据(串行处理避免冲突)
await new Promise((resolve, reject) => {
ctx.draw(false, () => {
uni.canvasGetImageData({
canvasId: 'reusableCanvas',
x: 0,
y: 0,
width: this.currentCanvasWidth,
height: this.currentCanvasHeight,
success: res => {
result={
line: textLine,
pixelData: res.data,
width: this.currentCanvasWidth,
height: this.currentCanvasHeight
};
resolve();
},
fail: err => {
// console.error(`处理第${i+1}行失败:`, err);
reject(err)
}
});
});
});
return result;
}
let arr = [];
// 循环处理每行文本
for (let i = 0; i < this.validTxts.length; i++) {
let linePixls = [];
let item = this.validTxts[i];
console.log("item=",item);
for (var j = 0; j < item.length; j++) {
let result = await drawTxt(item[j]);
linePixls.push(convertCharToMatrix(result.pixelData));
}
console.log("hexs=",linePixls.join(","));
arr.push(linePixls);
}
return arr;
}
}
};
</script>
<style>
.offscreen-canvas {
position: fixed;
left: -9999px;
top: -9999px;
visibility: hidden;
}
</style>

View File

@ -1,11 +1,19 @@
{
"pages": [
{
"path": "pages/common/login/index",
"style": {
"navigationStyle": "custom"
}
},
{
"path" : "pages/BlueTooth/ModeSetting/index",
"style" :
{
"navigationBarTitleText" : "设备类型"
}
},
{
"path": "pages/common/index/index",
"style": {
@ -191,7 +199,85 @@
"style": {
"navigationBarTitleText": "呼叫"
}
},
{
"path" : "pages/670/HBY670",
"style" :
{
"navigationBarTitleText" : "HBY670"
}
},
{
"path": "pages/BlueTooth/ModeSetting/VideoSend",
"style": {
"navigationBarTitleText": "发送视频"
}
},
{
"path": "pages/BlueTooth/ModeSetting/VideoSend_1",
"style": {
"navigationBarTitleText": "发送视频"
}
},
{
"path": "pages/BlueTooth/ModeSetting/VideoSend_670",
"style": {
"navigationBarTitleText": "发送视频"
}
},
{
"path": "pages/BlueTooth/ModeSetting/HBY650",
"style": {
"navigationBarTitleText": "HBY650"
}
},
{
"path": "pages/BlueTooth/ModeSetting/HBY650_1",
"style": {
"navigationBarTitleText": "HBY650"
}
},
{
"path": "pages/BlueTooth/ModeSetting/ModeSetting",
"style": {
"navigationBarTitleText": "7307-0.96TFT"
}
},
{
"path": "pages/BlueTooth/ModeSetting/update",
"style": {
"navigationBarTitleText": "版本更新"
}
},
{
"path": "pages/BlueTooth/ModeSetting/HBY6155",
"style": {
"navigationBarTitleText": "HBY6155"
}
},
{
"path": "pages/BlueTooth/ModeSetting/HBY6155V1",
"style": {
"navigationBarTitleText": "HBY6155_V1"
}
},
{
"path": "pages/BlueTooth/ModeSetting/HBY670",
"style": {
"navigationBarTitleText": "HBY670"
}
}
],
"tabBar": {
"color": "#fff",

View File

@ -25,18 +25,21 @@
onLoad: function(option) {
const eventChannel = this.getOpenerEventChannel();
var these = this;
eventChannel.on('checkImg', function(data) {
eventChannel.on('checkImg', (data)=> {
console.log("我收到你的消息了,消息内容是:" + JSON.stringify(data));
these.src = data.data;
this.src = data.data;
console.log("我收到你的消息了,消息内容是:",data);
})
},
methods: {
handleCrop(e) {
var these = this;
this.Statu = true;
console.log("裁剪完成");
console.log(e.tempFilePath);
//
const ctx = uni.createCanvasContext('splashCanvas', this);
ctx.drawImage(
e.tempFilePath,
@ -53,11 +56,12 @@
height: 80,
success: (res) => {
// 处理像素数据并发送
console.log("res.data.length="+res.data.length);
// this.processAndSendImageData(res.data).then(
// resolve).catch(reject);
const eventChannel = these.getOpenerEventChannel();
console.log("res.data.length="+res.data.length);
eventChannel.emit('ImgCutOver',res.data);
eventChannel.emit('ImgCutOver_Path',e.tempFilePath);
uni.navigateBack();
},
fail: (err) => {

View File

@ -77,7 +77,7 @@
</view>
<view class="mode marginLeft fleft" :class="formData.cMode=='mode'?'':'active'"
v-on:click.stop="UploadOpenImg()">
v-on:click.stop="ShowUpload()">
<view class="leftImg">
<image class="img" src="/static/images/6155/DeviceDetail/open.png" mode="aspectFit"></image>
</view>
@ -89,19 +89,19 @@
<view class="clear"></view>
</view>
<view class="usrinfo">
<view @click="formData.usrToggle=!formData.usrToggle">
<view @click="Status.usrToggle=!Status.usrToggle">
<text class="usrtitle fleft">人员信息登记</text>
<view class="image fright" :style="{transform:formData.usrToggle?'rotate(0deg)':'rotate(180deg)' }">
<view class="image fright" :style="{transform:Status.usrToggle?'rotate(0deg)':'rotate(180deg)' }">
<image class="img" src="/static/images/6155/DeviceDetail/slideToggle.png" mode="aspectFit"></image>
</view>
<view class="clear"></view>
</view>
<view :class="formData.usrToggle?'':'displayNone'">
<view :class="Status.usrToggle?'':'displayNone'">
<view style="margin: 20rpx 0rpx;">
<view class="btnSend fright" v-on:click.stop="sendUsr">发送</view>
<view class="btnSend fright" v-on:click.stop="sendUsr(null)">发送</view>
<view class="clear"></view>
</view>
<view class="item">
@ -235,7 +235,7 @@
maskBgColor: '#00000066',
showClose: false
},
usrToggle: true,
},
formData: {
img: '/static/images/6155/DeviceDetail/equip.png',
@ -251,17 +251,14 @@
macAddress: '',
cMode: false,
modeCurr: 'low',
usrToggle: true,
company: '黄石消防支队',
name: '菜英俊',
job: '小队长',
id: 'HSXF01061',
iswarn: false
},
rgb565Data: [],
videoHexArray: []
}
},
@ -379,16 +376,38 @@
bleValueNotify: function(receive, device, path) { //订阅消息
let data = recei.ReceiveData(receive, device, pagePath);
console.log("收到设备的数据",data)
if (data) {
console.log("收到订阅消息", receive);
if ("staBlue_picture" in data) {
//重发图片
console.log("收到重新发送图片的命令");
this.checkImgUpload('img', data.staBlue_picture);
return;
} else if ("staBlue_text" in data) {
//重发文本
console.log("收到重新发送文本的命令");
this.sendUsr(data.staBlue_text);
return;
} else if ("staBlue_vidio" in data) {
//重发视频
console.log("收到重新发送视频的命令");
this.checkImgUpload('video', data.staBlue_vidio);
return;
} else if ("staBlue" in data) {
if (data.staBlue == "finish") {
console.log("收到设备回复,全部传输完成");
}
return;
}
let keys = Object.keys(data);
for (var i = 0; i < keys.length; i++) {
let key = keys[i];
if(key in these.formData){
these.formData[key] = data[key];
}
}
if (these.formData.iswarn) {
these.showPop({
@ -412,8 +431,8 @@
},
getDevice: function() {
console.log("LinkedList=", ble.data.LinkedList);
console.log("formData=", these.formData);
// console.log("LinkedList=", ble.data.LinkedList);
// console.log("formData=", these.formData);
let f = ble.data.LinkedList.find((v) => {
return v.deviceId == these.formData.deviceId;
});
@ -558,33 +577,22 @@
}
});
},
checkImgUpload: function() {
checkImgUpload: function(type, index) {
console.log("123213213213");
// 处理像素数据并发送
var processAndSendImageData = function(pixels) {
return new Promise((resolve, reject) => {
// 创建RGB565格式的像素数据
const rgb565Data = ble.convertToRGB565(pixels);
// 分包发送
sendImagePackets(rgb565Data).then(resolve).catch(reject);
});
}
let f = these.getDevice();
// 分包发送图片数据
var sendImagePackets = function(imageData) {
var sendImagePackets = function(ReSendNo) {
return new Promise((resolve, reject) => {
// 总数据包数
const totalPackets = 52;
let totalPackets = 52;
let currentPacket = 1;
let f = these.getDevice();
if (ReSendNo) {
totalPackets = ReSendNo;
currentPacket = ReSendNo;
}
if (f) {
// 发送单个数据包
@ -597,6 +605,13 @@
message: "上传成功",
iconUrl: "/static/images/6155/DeviceDetail/uploadSuccess.png",
});
if (!ReSendNo) {
setTimeout(()=>{
these.HoldYouHand("transmit complete", 0, f.deviceId, f
.writeServiceId, f.wirteCharactId);
},500);
}
resolve();
return;
@ -604,23 +619,23 @@
// 计算当前包的数据
let packetSize = 250;
if (currentPacket <= 51) {
packetSize = 250; // 前51个包每个500字节
} else {
packetSize = 50; // 最后一个包100字节
}
// if (currentPacket <= 51) {
// packetSize = 250; // 前51个包每个500字节
// } else {
// packetSize = 50; // 最后一个包100字节
// }
// 创建数据包
const startIndex = (currentPacket - 1) * packetSize;
const endIndex = Math.min(startIndex + packetSize, imageData
const endIndex = Math.min(startIndex + packetSize, these.rgb565Data
.length);
if (startIndex > endIndex) {
return;
}
const packetData = imageData.slice(startIndex,
const packetData = these.rgb565Data.slice(startIndex,
endIndex);
// 构建数据包
const bufferSize = 5 + packetData.length * 2; // 头部5字节 + 数据部分
const bufferSize =505;// 5 + packetData.length * 2; // 头部5字节 + 数据部分
const buffer = new ArrayBuffer(bufferSize);
const dataView = new DataView(buffer);
@ -644,6 +659,11 @@
dataView.setUint16(5 + i * 2, packetData[i], false); // 大端字节序
}
if (currentPacket > 51) { //第52包补FF
for (var i = 105; i < bufferSize; i++) {
dataView.setUint8(i, 0xFF);
}
}
@ -681,7 +701,10 @@
});
}
if (ReSendNo) {
sendNextPacket(ReSendNo);
return;
}
// 开始牵手
these.HoldYouHand("picture transmit start", 120, f.deviceId, f.writeServiceId,
f.wirteCharactId).then(() => {
@ -713,8 +736,9 @@
});
these.Status.BottomMenu.show = false;
these.rgb565Data = ble.convertToRGB565(data);
setTimeout(function() {
processAndSendImageData(data).catch(() => {
sendImagePackets().catch(() => {
});
}, 0)
@ -730,6 +754,137 @@
});
}
let sendVideoPackets = (ReSendNo) => {
console.log("开始发送分片数据");
return new Promise((resolve, reject) => {
if (f) {
// 总数据包数
var totalPackets = 1536;
let currentPacket = 1;
console.log("发送数据准备中,总共" + totalPackets);
if (ReSendNo) {
totalPackets = ReSendNo;
currentPacket = ReSendNo;
}
this.totalPackets = totalPackets;
// 发送单个数据包
const sendNextVideoPacket = () => {
console.log("准备发送一段数据");
if (currentPacket > totalPackets) {
if (!ReSendNo) {
setTimeout(()=>{
these.HoldYouHand("transmit complete", 0, f.deviceId, f
.writeServiceId, f.wirteCharactId).then(()=>{
console.log("全部发送完毕")
}).catch((ex)=>{
console.log("出现异常",ex);
});
},500);
}
these.Status.BottomMenu.show = false;
hideLoading(these);
these.showPop({
showPop: true,
message: "上传成功",
iconUrl: "/static/images/6155/DeviceDetail/uploadSuccess.png"
});
resolve();
return;
}
// 计算当前包的数据
let packetSize = 500;
// 创建数据包
const startIndex = (currentPacket - 1) * packetSize;
const endIndex = Math.min(startIndex + packetSize, these.videoHexArray
.length);
if (startIndex > endIndex) {
resolve();
return;
}
////console.log("111111");
const packetData = these.videoHexArray.slice(startIndex, endIndex);
// 构建数据包
////console.log("packetData.length"+packetData.length);
const bufferSize = 506; // 头部5字节 + 数据部分
const buffer = new ArrayBuffer(bufferSize);
const dataView = new DataView(buffer);
let sortNo = currentPacket.toString(16).padStart(4, '0');
// 填充头部
dataView.setUint8(0, 0x55); // 帧头
dataView.setUint8(1, 0x04); // 帧类型:开机画面
dataView.setUint8(2, '0x' + sortNo.substring(0, 2)); // 包序号
dataView.setUint8(3, '0x' + sortNo.substring(2, 4)); // 包序号
dataView.setUint8(4, 0x01);
dataView.setUint8(5, 0xF4);
for (let i = 0; i < packetData.length; i++) {
dataView.setUint8(6 + i, '0x' + packetData[i]);
}
let inteval = 50;
console.log("开始发送一段视频"); //
ble.sendData(f.deviceId, buffer, f.writeServiceId, f
.wirteCharactId, 10).then(() => {
updateLoading(these, {
text: "正在发送:" + currentPacket + "/" +
totalPackets
})
currentPacket++;
console.log("发送一段成功,发送下一段");
setTimeout(sendNextVideoPacket, inteval);
}).catch(err => {
console.log("发送失败了");
if (err.code == '10007') { //遇到这个错误自动重新发送
console.log(err.errMsg + ",发送失败了,正在补偿:" +
currentPacket);
setTimeout(sendNextVideoPacket, inteval);
} else {
these.Status.BottomMenu.show = false;
hideLoading(these);
these.showPop({
showPop: true,
message: err.msg + ",发送失败了",
borderColor: "#e034344d",
buttonBgColor: "#E03434",
buttonText: "确定",
iconUrl: "/static/images/6155/DeviceDetail/uploadErr.png"
});
reject(err);
return;
}
});
};
sendNextVideoPacket();
} else {
console.log("Fail.......")
}
});
}
var sendVideo = (videoPath) => {
let uploadVideo = () => {
@ -740,7 +895,7 @@
}
console.log("开始处理,正在上传");
let f = this.getDevice();
showLoading(these, {
text: "上传中"
});
@ -809,123 +964,13 @@
})
}
let f = these.getDevice();
console.log("f=", f);
let sendVideoPackets = (imageData) => {
console.log("开始发送分片数据");
return new Promise((resolve, reject) => {
if (f) {
// 总数据包数
const totalPackets = 1536;
this.totalPackets = totalPackets;
let currentPacket = 1;
console.log("发送数据准备中,总共" + totalPackets);
// 发送单个数据包
const sendNextVideoPacket = () => {
console.log("准备发送一段数据");
if (currentPacket > totalPackets) {
these.Status.BottomMenu.show = false;
hideLoading(these);
these.showPop({
showPop: true,
message: "上传成功",
iconUrl: "/static/images/6155/DeviceDetail/uploadSuccess.png"
});
these.videoHexArray = [];
resolve();
return;
}
// 计算当前包的数据
let packetSize = 500;
// 创建数据包
const startIndex = (currentPacket - 1) * packetSize;
const endIndex = Math.min(startIndex + packetSize, imageData
.length);
if (startIndex > endIndex) {
resolve();
return;
}
////console.log("111111");
const packetData = imageData.slice(startIndex, endIndex);
// 构建数据包
////console.log("packetData.length"+packetData.length);
const bufferSize = 506; // 头部5字节 + 数据部分
const buffer = new ArrayBuffer(bufferSize);
const dataView = new DataView(buffer);
let sortNo = currentPacket.toString(16).padStart(4, '0');
// 填充头部
dataView.setUint8(0, 0x55); // 帧头
dataView.setUint8(1, 0x04); // 帧类型:开机画面
dataView.setUint8(2, '0x' + sortNo.substring(0, 2)); // 包序号
dataView.setUint8(3, '0x' + sortNo.substring(2, 4)); // 包序号
dataView.setUint8(4, 0x01);
dataView.setUint8(5, 0xF4);
for (let i = 0; i < packetData.length; i++) {
dataView.setUint8(6 + i, '0x' + packetData[i]);
}
let inteval = 50;
console.log("开始发送一段视频"); //
ble.sendData(f.deviceId, buffer, f.writeServiceId, f
.wirteCharactId, 10).then(() => {
updateLoading(these, {
text: "正在发送:" + currentPacket + "/" +
totalPackets
})
currentPacket++;
console.log("发送一段成功,发送下一段");
setTimeout(sendNextVideoPacket, inteval);
}).catch(err => {
if (err.code == '10007') { //遇到这个错误自动重新发送
console.log(err.errMsg + ",发送失败了,正在补偿:" +
currentPacket);
setTimeout(sendNextVideoPacket, inteval);
} else {
these.Status.BottomMenu.show = false;
hideLoading(these);
these.showPop({
showPop: true,
message: err.msg + ",发送失败了",
borderColor: "#e034344d",
buttonBgColor: "#E03434",
buttonText: "确定",
iconUrl: "/static/images/6155/DeviceDetail/uploadErr.png"
});
reject(err);
return;
}
});
};
sendNextVideoPacket();
} else {
console.log("Fail.......")
}
});
}
let shotVideoClick = (array) => {
let shotVideoClick = () => {
console.log("正在开始发送");
sendVideoPackets(array).then(() => {
sendVideoPackets().then(() => {
console.log("发送完成");
}).catch((ex1) => {
@ -940,6 +985,14 @@
}
if (type) {
if (type == 'img') {
sendImagePackets(index);
} else if (type == 'video') {
sendVideoPackets(index);
}
return;
}
uni.chooseMedia({
count: 1,
mediaType: ['image', 'video'],
@ -968,16 +1021,9 @@
}
});
// uni.chooseImage({
// count: 1,
// sizeType: ['original', 'compressed'],
// sourceType: ['album'],
// success: function(res) {
// gotoCutImg(res.tempFiles[0].path);
// }
// });
},
UploadOpenImg: function() {
ShowUpload: function() {
//上传开机画面
this.Status.BottomMenu.menuItems = [];
this.Status.BottomMenu.title = "上传开机画面";
@ -1043,7 +1089,7 @@
}
these.Status.Pop.showPop = true;
},
sendUsr: function() {
sendUsr: function(ReSendNo) {
@ -1052,7 +1098,6 @@
this.formData.name,
this.formData.job,
this.formData.id
];
@ -1070,17 +1115,28 @@
text: "请稍候..."
});
var sendText = function() {
// 总数据包数
const totalPackets = textLines.length;
let totalPackets = textLines.length;
let currentPacket = 1;
// console.log("currentPacket=",currentPacket);
// console.log("ReSendNo=",ReSendNo);
if (ReSendNo) {
totalPackets = ReSendNo;
currentPacket = ReSendNo;
}
// console.log("currentPacket=",currentPacket);
// 发送单个数据包
const sendNextPacket = () => {
if (currentPacket > totalPackets) {
if (!ReSendNo) {
setTimeout(()=>{
these.HoldYouHand("transmit complete", 0, f.deviceId, f.writeServiceId, f
.wirteCharactId);
},500);
}
these.showPop({
showPop: true, //是否显示弹窗
popType: 'custom',
@ -1107,9 +1163,9 @@
let arr = gbk.encode(text)
let gbkData = gbk.arr2hex(arr);
console.log(JSON.stringify(gbkData));
// 构建数据包
const bufferSize = 5 + text.length * 2; // 头部4字节 + 数据部分
const bufferSize = 5 + gbkData.length / 2; // 头部4字节 + 数据部分
const buffer = new ArrayBuffer(bufferSize);
const dataView = new DataView(buffer);
@ -1118,19 +1174,16 @@
dataView.setUint8(1, 0x03); // 帧类型:文字
dataView.setUint8(2, currentPacket.toString(16)); // 包序号
dataView.setUint16(3, (text.length * 2).toString(16)); // 数据长度
console.log("gbkData.length=" + gbkData.length)
// 填充数据
for (let i = 1; i <= text.length * 2; i++) {
const highNibble = gbkData[i * 2 - 2]; // 高4位
const lowNibble = gbkData[i * 2 - 1]; // 低4位如果是奇数长度补0
//console.log("i=" + i + "," + highNibble + lowNibble)
// 组合高低位并转换为字节值
const byteValue = parseInt(highNibble + lowNibble, 16);
dataView.setUint8(5 + i - 1, byteValue);
let index = 0;
for (var i = 0; i < gbkData.length; i += 2) {
let value = parseInt(gbkData[i] + "" + gbkData[i + 1], 16);
dataView.setUint8(5 + index, value);
index++;
}
console.log("dataView.length=" + dataView.byteLength)
// 发送数据包
@ -1166,7 +1219,10 @@
}
if(ReSendNo){
sendText(ReSendNo);
return;
}
setTimeout(() => {
these.HoldYouHand("word transmit start", 120, f.deviceId, f.writeServiceId, f
.wirteCharactId).then(

1841
pages/670/HBY670.vue Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,472 @@
<template>
<view class="content">
<canvas canvas-id="flashCanvas"
style="width: 160px; height: 80px; z-index: 9999;position: fixed; top:-9999px;left:-9999px;"></canvas>
<f-video ref="compARef" :src="videoPath" :direction="-90" :autoplay="true" @shotVideoClick="shotVideoClick"
:videoWidth="videoWidth" :videoHeight="videoHeight"></f-video>
<view>
<text>发送间隔</text>
<input type="text" v-model="inteval" />
</view>
<view>
<button @click="checkVideo">选择视频</button>
<!-- <button @click="CutImg">发送</button> -->
<button @click="uploadVideo">发送</button>
</view>
<view class="sending-progress" v-if="isSending">
<view class="progress-bar">
<view class="progress-fill" :style="{ width: progress + '%' }"></view>
</view>
<text>正在发送: {{ progress }}% ({{ currentPacket }}/{{ totalPackets }})</text>
</view>
</view>
</template>
<script>
import Common from '@/utils/Common';
export default {
data() {
return {
videoPath: '',
inteval: 0,
progress: 0,
currentPacket: 0,
totalPackets: 100,
connectedDeviceId: '',
serviceId: '0xFFE1',
writeCharacteristicId: '0xFFE1',
notifyCharacteristicId: '0xFFE2',
isSending: "",
textProgress: "",
imgs: [],
videoWidth: 320,
videoHeight: 160,
videoDuration: 2,
hexArray: []
}
},
onLoad() {
const eventChannel = this.getOpenerEventChannel();
eventChannel.on('receiveDevice', (data) => {
this.connectedDeviceId = data.connectedDeviceId;
this.serviceId = data.serviceId;
this.writeCharacteristicId = data.writeCharacteristicId;
this.notifyCharacteristicId = data.notifyCharacteristicId;
})
},
methods: {
checkVideo: function() {
uni.chooseVideo({
sourceType: ['album', 'camera'],
compressed: false,
maxDuration: 2,
camera: 'back',
success: (res) => {
this.videoPath = res.tempFilePath;
this.imgs = [];
this.hexArray = [];
this.$refs.compARef.base64 = [];
this.videoWidth = res.width;
this.videoHeight = res.height;
this.videoDuration = res.duration;
console.log("视频宽:" + res.width + ",视频高:" + res.height + ",视频时长:" + res.duration);
}
})
},
uploadVideo: function() {
if (this.hexArray.length > 0) {
console.log("开始处理,无需上传");
this.shotVideoClick(this.hexArray, 'rgb565');
return;
}
if(!this.videoPath){
uni.showToast({
title: "请选择视频",
icon: 'fail'
})
return;
}
console.log("正在上传视频");
uni.showLoading({
title: "上传中"
});
let p2=new Promise((resolve,reject)=>{
let start = new Date();
console.log("Common.baseURL="+Common.baseURL);
uni.uploadFile({
url:Common.baseURL+'video/upload',
filePath: this.videoPath,
name: 'file',
header: {
"Method": "POST",
"Content-Type": "multipart/form-data"
},
timeout: 600000,
fail: (ex) => {
//console.log("上传视频失败" + JSON.stringify(ex));
uni.showToast({
title: "视频文件上传失败了,请检查网络连接",
icon: 'fail'
})
uni.hideLoading();
reject(ex);
},
success: (res) => {
let end = new Date();
var diff = (end.getTime() - start.getTime()) / 1000;
let s = diff % 60;
let m = (diff - s) / 60;
console.log("上传完成,耗时:" + m + "分" + s + "秒");
uni.hideLoading();
resolve(res);
}
});
});
let p1=this.HoldYouHand();
Promise.all([p2,p1]).then((arr)=>{
if(arr[1]===true){
let res=arr[0];
res = JSON.parse(res.data);
if (res.data) {
this.hexArray = res.data;
uni.showLoading({
title: "正在发送"
});
setTimeout(() => {
this.shotVideoClick(res.data, 'rgb565');
}, 0)
} else {
console.log("res")
uni.showModal({
content: "服务器未返回RGB565数据",
title: '错误'
})
}
}else{
uni.showModal({
content:"与设备握手失败了",
title:"错误"
})
}
});
},
shotVideoClick: function(array, type) {
//console.log("处理视频完成", array);
//console.log("type=" + type)
//console.log("array=", array);
this.imgs = array;
var sendImagePackets = (imageData) => {
return new Promise((resolve, reject) => {
this.isSending = true;
this.progress = 0;
this.currentPacket = 0;
// 总数据包数
const totalPackets = 1536;
this.totalPackets = totalPackets;
let currentPacket = 1;
// 发送单个数据包
const sendNextPacket = () => {
////console.log("currentPacket="+currentPacket+",imageData.length="+imageData.length);
if (currentPacket > totalPackets) {
this.isSending = false;
resolve();
return;
}
// 计算当前包的数据
let packetSize = 250;
if (type == 'rgb565') {
packetSize = 500;
}
// 创建数据包
const startIndex = (currentPacket - 1) * packetSize;
const endIndex = Math.min(startIndex + packetSize, imageData.length);
if (startIndex > endIndex) {
resolve();
return;
}
////console.log("111111");
const packetData = imageData.slice(startIndex, endIndex);
// 构建数据包
////console.log("packetData.length"+packetData.length);
const bufferSize = 506; // 头部5字节 + 数据部分
const buffer = new ArrayBuffer(bufferSize);
const dataView = new DataView(buffer);
let sortNo = currentPacket.toString(16).padStart(4, '0');
// 填充头部
dataView.setUint8(0, 0x55); // 帧头
dataView.setUint8(1, 0x04); // 帧类型:开机画面
dataView.setUint8(2, '0x' + sortNo.substring(0, 2)); // 包序号
dataView.setUint8(3, '0x' + sortNo.substring(2, 4)); // 包序号
dataView.setUint8(4, 0x01);
dataView.setUint8(5, 0xF4);
if (type == 'rgb565') {
for (let i = 0; i < packetData.length; i++) {
dataView.setUint8(6 + i, '0x' + packetData[i]);
}
} else {
for (let i = 0; i < packetData.length; i++) {
dataView.setUint16(6 + i * 2, packetData[i], false);
}
}
let inteval = parseInt(this.inteval ? this.inteval : 0);
this.sendData(buffer).then(() => {
// 更新进度
this.currentPacket = currentPacket;
this.progress = Math.round((currentPacket / totalPackets) *
100);
//console.log(`发送数据包完成 ${currentPacket}/${totalPackets}`);
// 发送下一个包(添加延迟避免蓝牙缓冲区溢出)
currentPacket++;
setTimeout(sendNextPacket, inteval);
}).catch(err => {
// console.log(err.errMsg + ",发送失败了,正在补偿:" + currentPacket);
setTimeout(sendNextPacket, inteval);
// uni.showToast({
// title:"发送失败"+JSON.stringify(err)
// })
});
};
sendNextPacket();
});
}
if (type == 'rgb565') {
let start = new Date();
console.log("开始发送");
sendImagePackets(array).then(() => {
console.log("发送完成");
let end = new Date();
var diff = (end.getTime() - start.getTime()) / 1000;
let s = diff % 60;
let m = (diff-s) / 60;
console.log("发送完成,耗时:" + m + "分" + s + "秒");
uni.showToast({
title: "发送完成,耗时:" + m + "分" + s + "秒",
icon: 'success'
})
}).catch((ex1) => {
//console.log("出现了异常", ex1)
}).finally(() => {
uni.hideLoading();
});
return;
}
},
HoldYouHand() {
var promise=new Promise((resolve,reject)=>{
try{
let start=new Date();
var str = "video transmit start"; //握手的协议字符串
console.log("开始握手:"+str)
// 1. 创建 ArrayBuffer 和 DataView
const buffer = new ArrayBuffer(str.length);
const dataView = new DataView(buffer);
// 2. 将字符串转换为 ASCII 码并写入 DataView
for (let i = 0; i < str.length; i++) {
dataView.setUint8(i, str.charCodeAt(i));
}
//console.log("111111");
this.sendData(buffer).then(() => {
// 开始发送第一个包
setTimeout(()=>{
let end = new Date();
var diff = (end.getTime() - start.getTime()) / 1000;
let s = diff % 60;
let m = (diff - s) / 60;
console.log("握手成功并完成2200ms等待,耗时"+m+"分"+s+"秒");
resolve(true);
}, 2200);
}).catch(err => {
//console.log("握手没有成功");
reject(err);
});
}catch(ex){
reject(ex);
}
});
return promise;
},
sendData(buffer) {
////console.log("deviceId=" + this.connectedDeviceId);
////console.log("serviceId=" + this.serviceId);
return new Promise(async (resolve, reject) => {
// //console.log("开始发送数据buffer");
var promise = new Promise((succ, err) => {
uni.writeBLECharacteristicValue({
deviceId: this.connectedDeviceId,
serviceId: this.serviceId,
characteristicId: this.writeCharacteristicId,
value: buffer,
writeType: plus.os.name == 'iOS' ? 'write' : 'writeNoResponse',
success: () => {
// //console.log("发送数据成功");
succ();
},
fail: (ex) => {
//console.log("发送数据失败", ex);
err(ex);
},
complete: function() {
// //console.log("123456");
}
});
});
if (plus.os.name == 'iOS') {
function timeout(ms) {
return new Promise((_, err) => {
setTimeout(() => {
err({
code: -1,
errMsg: '超时了'
})
}, ms);
});
}
let inteval = parseInt(this.inteval ? this.inteval : 0);
Promise.race([promise, timeout(inteval)]).then(resolve).catch((ex) => {
//console.log("ex=", ex);
if (ex.code == -1) {
resolve();
} else {
reject();
}
}).finally(() => {
//console.log("完成了")
});
} else {
promise.then(resolve).catch(reject);
}
});
},
CutImg: function() {
if (this.imgs.length == 30) {
this.shotVideoClick(this.imgs, 'img');
return;
}
//console.log("开始处理视频")
uni.showLoading({
title: '开始处理'
});
this.$refs.compARef.shotVideoClick(0);
}
}
}
</script>
<style>
.sending-progress {
margin-top: 30rpx;
width: 100%;
}
.progress-bar {
height: 12rpx;
background-color: #e0e0e0;
border-radius: 6rpx;
overflow: hidden;
}
.progress-fill {
height: 100%;
background-color: #409eff;
transition: width 0.3s;
}
input {
border: 2rpx solid #000000;
}
button {
height: 100rpx;
line-height: 100rpx;
font-size: 28rpx;
float: left;
}
.content {
width: 100vw;
height: 300px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
</style>

View File

@ -0,0 +1,517 @@
<template>
<view class="content">
<canvas canvas-id="flashCanvas"
style="width: 160px; height: 80px; z-index: 9999;position: fixed; top:-9999px;left:-9999px;"></canvas>
<f-video ref="compARef" :src="videoPath" :direction="-90" :autoplay="true" @shotVideoClick="shotVideoClick"
:videoWidth="videoWidth" :videoHeight="videoHeight"></f-video>
<view>
<text>并发包数量</text>
<input type="text" v-model="packgeCnt" />
</view>
<view>
<text>发送间隔</text>
<input type="text" v-model="inteval" />
</view>
<view>
<button @click="checkVideo">选择视频</button>
<!-- <button @click="CutImg">发送</button> -->
<button @click="uploadVideo">发送</button>
</view>
<view class="sending-progress" v-if="isSending">
<view class="progress-bar">
<view class="progress-fill" :style="{ width: progress + '%' }"></view>
</view>
<text>正在发送: {{ progress }}% ({{ currentPacket }}/{{ totalPackets }})</text>
</view>
</view>
</template>
<script>
export default {
data() {
return {
videoPath: '',
packgeCnt: 20,
inteval: 0,
progress: 0,
currentPacket: 0,
totalPackets: 100,
connectedDeviceId: '',
serviceId: '0xFFE1',
writeCharacteristicId: '0xFFE1',
notifyCharacteristicId: '0xFFE2',
isSending: "",
textProgress: "",
imgs: [],
videoWidth: 320,
videoHeight: 160,
videoDuration: 2,
hexArray: []
}
},
onLoad() {
const eventChannel = this.getOpenerEventChannel();
eventChannel.on('receiveDevice', (data) => {
this.connectedDeviceId = data.connectedDeviceId;
this.serviceId = data.serviceId;
this.writeCharacteristicId = data.writeCharacteristicId;
this.notifyCharacteristicId = data.notifyCharacteristicId;
})
},
methods: {
checkVideo: function() {
uni.chooseVideo({
sourceType: ['album', 'camera'],
compressed: false,
maxDuration: 2,
camera: 'back',
success: (res) => {
this.videoPath = res.tempFilePath;
this.imgs = [];
this.hexArray = [];
this.$refs.compARef.base64 = [];
this.videoWidth = res.width;
this.videoHeight = res.height;
this.videoDuration = res.duration;
console.log("视频宽:" + res.width + ",视频高:" + res.height + ",视频时长:" + res.duration);
}
})
},
uploadVideo: function() {
if (this.hexArray.length > 0) {
console.log("开始处理,无需上传");
this.shotVideoClick(this.hexArray, 'rgb565');
return;
}
if(!this.videoPath){
uni.showToast({
title: "请选择视频",
icon: 'fail'
})
return;
}
console.log("正在上传视频");
uni.showLoading({
title: "上传中"
});
let p2=new Promise((resolve,reject)=>{
console.log("Common.baseURL="+Common.baseURL);
let start = new Date();
uni.uploadFile({
url: Common.baseURL+'video/upload',
// url: 'http://192.168.110.169:5000/video/upload',
filePath: this.videoPath,
name: 'file',
header: {
"Method": "POST",
"Content-Type": "multipart/form-data"
},
timeout: 600000,
fail: (ex) => {
//console.log("上传视频失败" + JSON.stringify(ex));
uni.showToast({
title: "视频文件上传失败了,请检查网络连接",
icon: 'fail'
})
uni.hideLoading();
reject(ex);
},
success: (res) => {
let end = new Date();
var diff = (end.getTime() - start.getTime()) / 1000;
let s = diff % 60;
let m = (diff - s) / 60;
console.log("上传完成,耗时:" + m + "分" + s + "秒");
uni.hideLoading();
resolve(res);
}
});
});
let p1=this.HoldYouHand();
Promise.all([p2,p1]).then((arr)=>{
if(arr[1]===true){
let res=arr[0];
res = JSON.parse(res.data);
if (res.data) {
this.hexArray = res.data;
uni.showLoading({
title: "正在发送"
});
setTimeout(() => {
this.shotVideoClick(res.data, 'rgb565');
}, 0)
} else {
console.log("res")
uni.showModal({
content: "服务器未返回RGB565数据",
title: '错误'
})
}
}else{
uni.showModal({
content:"与设备握手失败了",
title:"错误"
})
}
});
},
pause(e) {
////console.log('pause--------------------------', e);
},
shotVideoClick: function(array, type) {
//console.log("处理视频完成", array);
//console.log("type=" + type)
//console.log("array=", array);
this.imgs = array;
var sendImagePackets = (imageData) => {
return new Promise((resolve, reject) => {
this.isSending = true;
this.progress = 0;
this.currentPacket = 0;
// 总数据包数
const totalPackets = 1536;
this.totalPackets = totalPackets;
let currentPacket = 1;
let promises = [];
// 发送单个数据包
const sendNextPacket = () => {
////console.log("currentPacket="+currentPacket+",imageData.length="+imageData.length);
if (currentPacket > totalPackets) {
this.isSending = false;
resolve();
return;
}
// 计算当前包的数据
let packetSize = 250;
if (type == 'rgb565') {
packetSize = 500;
}
// 创建数据包
const startIndex = (currentPacket - 1) * packetSize;
const endIndex = Math.min(startIndex + packetSize, imageData.length);
if (startIndex > endIndex) {
resolve();
return;
}
////console.log("111111");
const packetData = imageData.slice(startIndex, endIndex);
// 构建数据包
////console.log("packetData.length"+packetData.length);
const bufferSize = 506; // 头部5字节 + 数据部分
const buffer = new ArrayBuffer(bufferSize);
const dataView = new DataView(buffer);
let sortNo = currentPacket.toString(16).padStart(4, '0');
// 填充头部
dataView.setUint8(0, 0x55); // 帧头
dataView.setUint8(1, 0x04); // 帧类型:开机画面
dataView.setUint8(2, '0x' + sortNo.substring(0, 2)); // 包序号
dataView.setUint8(3, '0x' + sortNo.substring(2, 4)); // 包序号
dataView.setUint8(4, 0x01);
dataView.setUint8(5, 0xF4);
if (type == 'rgb565') {
for (let i = 0; i < packetData.length; i++) {
dataView.setUint8(6 + i, '0x' + packetData[i]);
}
} else {
for (let i = 0; i < packetData.length; i++) {
dataView.setUint16(6 + i * 2, packetData[i], false);
}
}
let inteval = parseInt(this.inteval ? this.inteval : 0);
let promise = this.sendData(buffer);
promises.push(promise);
let packgeCnt = parseInt(this.packgeCnt || 20);
if (currentPacket % packgeCnt == 0 || (currentPacket >= totalPackets &&
promises.length > 0)) {
Promise.all(promises).then(() => {
if (currentPacket >= totalPackets) {
this.isSending = false;
resolve();
return;
}
this.currentPacket = currentPacket;
this.progress = Math.round((currentPacket / totalPackets) *
100);
currentPacket++;
setTimeout(sendNextPacket, inteval);
})
} else {
currentPacket++;
sendNextPacket();
}
// .then(() => {
// // 更新进度
// this.currentPacket = currentPacket;
// this.progress = Math.round((currentPacket / totalPackets) *
// 100);
// currentPacket++;
// setTimeout(sendNextPacket, inteval);
// }).catch(err => {
// console.log(err.errMsg+",发送失败了,正在补偿:" + currentPacket);
// setTimeout(sendNextPacket, inteval);
// });
};
//
sendNextPacket();
});
}
if (type == 'rgb565') {
let start = new Date();
console.log("开始发送");
sendImagePackets(array).then(() => {
let end = new Date();
var diff = (end.getTime() - start.getTime()) / 1000;
let s = diff % 60;
let m = (diff - s) / 60;
console.log("发送完成,耗时:" + m + "分" + s + "秒");
uni.showToast({
title: "发送完成,耗时:" + m + "分" + s + "秒",
icon: 'success',
})
}).catch((ex1) => {
//console.log("出现了异常", ex1)
}).finally(() => {
uni.hideLoading();
});
return;
}
},
HoldYouHand() {
var promise=new Promise((resolve,reject)=>{
try{
let start=new Date();
var str = "video transmit start"; //握手的协议字符串
console.log("开始握手:"+str)
// 1. 创建 ArrayBuffer 和 DataView
const buffer = new ArrayBuffer(str.length);
const dataView = new DataView(buffer);
// 2. 将字符串转换为 ASCII 码并写入 DataView
for (let i = 0; i < str.length; i++) {
dataView.setUint8(i, str.charCodeAt(i));
}
//console.log("111111");
this.sendData(buffer).then(() => {
// 开始发送第一个包
setTimeout(()=>{
let end = new Date();
var diff = (end.getTime() - start.getTime()) / 1000;
let s = diff % 60;
let m = (diff - s) / 60;
console.log("握手成功并完成2200ms等待,耗时"+m+"分"+s+"秒");
resolve(true);
}, 2200);
}).catch(err => {
//console.log("握手没有成功");
reject(err);
});
}catch(ex){
reject(ex);
}
});
return promise;
},
sendData(buffer) {
let sendBuffer = () => {
return new Promise((resolve, reject) => {
var promise = new Promise((succ, err) => {
uni.writeBLECharacteristicValue({
deviceId: this.connectedDeviceId,
serviceId: this.serviceId,
characteristicId: this.writeCharacteristicId,
value: buffer,
writeType: plus.os.name == 'iOS' ? 'write' : 'writeNoResponse',
success: () => {
// console.log("发送数据成功");
succ();
},
fail: (ex) => {
if (ex.code == '10007') {
// console.log("失败重试");
setTimeout(() => {
sendBuffer().then(succ).catch(err);
}, this.inteval || 0)
// succ()
} else
{
// console.log("发送数据失败",ex);
err(ex);
}
},
complete: function() {
//console.log("123456");
}
});
});
if (plus.os.name == 'iOS') {
function timeout(ms) {
return new Promise((_, err) => {
setTimeout(() => {
err({
code: -1,
errMsg: '超时了'
})
}, ms);
});
}
Promise.race([promise, timeout(this.inteval ? this.inteval : 0)]).then(() => {
// console.log("成功了");
resolve();
})
.catch((ex) => {
if (ex.code == -1) {
// console.log("超时了")
resolve();
} else {
reject(ex);
// console.log("异常了", ex);
//sendBuffer().then(resolve).catch(reject);
}
});
} else {
promise.then(() => {
//console.log("then........")
resolve();
}).catch(() => {
//console.log("catch.........")
reject()
});
}
});
}
return sendBuffer();
},
CutImg: function() {
if (this.imgs.length == 30) {
this.shotVideoClick(this.imgs, 'img');
return;
}
//console.log("开始处理视频")
uni.showLoading({
title: '开始处理'
});
this.$refs.compARef.shotVideoClick(0);
}
}
}
</script>
<style>
.sending-progress {
margin-top: 30rpx;
width: 100%;
}
.progress-bar {
height: 12rpx;
background-color: #e0e0e0;
border-radius: 6rpx;
overflow: hidden;
}
.progress-fill {
height: 100%;
background-color: #409eff;
transition: width 0.3s;
}
input {
border: 2rpx solid #000000;
}
button {
height: 100rpx;
line-height: 100rpx;
font-size: 28rpx;
float: left;
}
.content {
width: 100vw;
height: 300px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
</style>

View File

@ -0,0 +1,537 @@
<template>
<view class="content">
<canvas canvas-id="flashCanvas"
style="width: 160px; height: 80px; z-index: 9999;position: fixed; top:-9999px;left:-9999px;"></canvas>
<view>
<view>
选择的视频:{{videoPath}}</view>
</view>
<view>
重发包序号:{{reSendNumber}}
</view>
<view>
<text>发送间隔</text>
<input type="text" v-model="inteval" />
</view>
<view>
<button @click="checkVideo">选择视频</button>
<button @click="uploadVideo">发送</button>
</view>
<view class="sending-progress" v-if="isSending">
<view class="progress-bar">
<view class="progress-fill" :style="{ width: progress + '%' }"></view>
</view>
<text>正在发送: {{ progress }}% ({{ currentPacket }}/{{ totalPackets }})</text>
</view>
</view>
</template>
<script>
import Common from '../../../utils/Common';
var mqttClient=null;
export default {
data() {
return {
videoPath: '',
inteval: 0,
progress: 0,
currentPacket: 0,
totalPackets: 100,
connectedDeviceId: '',
serviceId: '0xFFE1',
writeCharacteristicId: '0xFFE1',
notifyCharacteristicId: '0xFFE2',
isSending: "",
textProgress: "",
netMode:"ble",
IMEI:"",
imgs: [],
videoWidth: 320,
videoHeight: 160,
videoDuration: 2,
hexArray: [],
reSendNumber:""
}
},
onLoad() {
const eventChannel = this.getOpenerEventChannel();
eventChannel.on('receiveDevice', (data) => {
this.connectedDeviceId = data.connectedDeviceId;
this.serviceId = data.serviceId;
this.writeCharacteristicId = data.writeCharacteristicId;
this.notifyCharacteristicId = data.notifyCharacteristicId;
this.netMode=data.netMode;
mqttClient=data.mqttClient;
this.IMEI=data.IMEI;
});
eventChannel.on('ReSendVideo',(data)=>{
//重新发送某一包
this.reSendNumber=data.videoNo;
setTimeout(()=>{
this.shotVideoClick(this.hexArray,'rgb565',data.videoNo);
},0);
});
},
methods: {
checkVideo: function() {
uni.chooseVideo({
sourceType: ['album', 'camera'],
compressed: false,
maxDuration: 2,
camera: 'back',
success: (res) => {
this.videoPath = res.tempFilePath;
this.imgs = [];
this.hexArray = [];
this.videoWidth = res.width;
this.videoHeight = res.height;
this.videoDuration = res.duration;
console.log("视频宽:" + res.width + ",视频高:" + res.height + ",视频时长:" + res.duration);
}
})
},
uploadVideo: function() {
if (this.hexArray.length > 0) {
console.log("开始处理,无需上传");
this.shotVideoClick(this.hexArray, 'rgb565');
return;
}
if(!this.videoPath){
uni.showToast({
title: "请选择视频",
icon: 'fail'
})
return;
}
console.log("正在上传视频");
uni.showLoading({
title: "上传中"
});
let p2=new Promise((resolve,reject)=>{
let start = new Date();
console.log("Common.baseURL="+Common.baseURL);
uni.uploadFile({
url:Common.baseURL+'video/upload',
filePath: this.videoPath,
name: 'file',
header: {
"Method": "POST",
"Content-Type": "multipart/form-data"
},
timeout: 600000,
fail: (ex) => {
//console.log("上传视频失败" + JSON.stringify(ex));
uni.showToast({
title: "视频文件上传失败了,请检查网络连接",
icon: 'fail'
})
uni.hideLoading();
reject(ex);
},
success: (res) => {
let end = new Date();
var diff = (end.getTime() - start.getTime()) / 1000;
let s = diff % 60;
let m = (diff - s) / 60;
console.log("上传完成,耗时:" + m + "分" + s + "秒");
uni.hideLoading();
resolve(res);
}
});
});
let p1=this.HoldYouHand();
Promise.all([p2,p1]).then((arr)=>{
if(arr[1]===true){
let res=arr[0];
res = JSON.parse(res.data);
if (res.data) {
this.hexArray = res.data;
uni.showLoading({
title: "正在发送"
});
setTimeout(() => {
this.shotVideoClick(res.data, 'rgb565');
}, 0)
} else {
console.log("res")
uni.showModal({
content: "服务器未返回RGB565数据",
title: '错误'
})
}
}else{
uni.showModal({
content:"与设备握手失败了",
title:"错误"
})
}
});
},
shotVideoClick: function(array, type,ReSendNo) {
//console.log("处理视频完成", array);
//console.log("type=" + type)
//console.log("array=", array);
this.imgs = array;
var sendImagePackets = (imageData) => {
return new Promise((resolve, reject) => {
this.isSending = true;
this.progress = 0;
this.currentPacket = 0;
// 总数据包数
var totalPackets = 1536;
this.totalPackets = totalPackets;
let currentPacket = 1;
if(ReSendNo){
this.progress = ReSendNo-1;
this.currentPacket = ReSendNo-1;
currentPacket=ReSendNo;
totalPackets=ReSendNo;
this.totalPackets=ReSendNo;
}
// 发送单个数据包
const sendNextPacket = () => {
////console.log("currentPacket="+currentPacket+",imageData.length="+imageData.length);
if (currentPacket > totalPackets) {
this.isSending = false;
if(!ReSendNo){
this.bleSendComplete();
}else{
// this.reSendNumber="";
}
resolve();
return;
}
// 计算当前包的数据
let packetSize = 250;
if (type == 'rgb565') {
packetSize = 500;
}
// 创建数据包
const startIndex = (currentPacket - 1) * packetSize;
const endIndex = Math.min(startIndex + packetSize, imageData.length);
if (startIndex > endIndex) {
resolve();
return;
}
////console.log("111111");
const packetData = imageData.slice(startIndex, endIndex);
// 构建数据包
////console.log("packetData.length"+packetData.length);
const bufferSize = 506; // 头部5字节 + 数据部分
const buffer = new ArrayBuffer(bufferSize);
const dataView = new DataView(buffer);
let sortNo = currentPacket.toString(16).padStart(4, '0');
// 填充头部
dataView.setUint8(0, 0x55); // 帧头
dataView.setUint8(1, 0x04); // 帧类型:开机画面
dataView.setUint8(2, '0x' + sortNo.substring(0, 2)); // 包序号
dataView.setUint8(3, '0x' + sortNo.substring(2, 4)); // 包序号
dataView.setUint8(4, 0x01);
dataView.setUint8(5, 0xF4);
if (type == 'rgb565') {
for (let i = 0; i < packetData.length; i++) {
dataView.setUint8(6 + i, '0x' + packetData[i]);
}
} else {
for (let i = 0; i < packetData.length; i++) {
dataView.setUint16(6 + i * 2, packetData[i], false);
}
}
let inteval = parseInt(this.inteval ? this.inteval : 0);
this.sendData(buffer).then(() => {
// 更新进度
this.currentPacket = currentPacket;
this.progress = Math.round((currentPacket / totalPackets) *
100);
console.log(`发送数据包完成 ${currentPacket}/${totalPackets}`);
// 发送下一个包(添加延迟避免蓝牙缓冲区溢出)
currentPacket++;
setTimeout(sendNextPacket, inteval);
}).catch(err => {
console.log(err.errMsg + ",发送失败了,正在补偿:" + currentPacket);
setTimeout(sendNextPacket, inteval);
// uni.showToast({
// title:"发送失败"+JSON.stringify(err)
// })
});
};
sendNextPacket();
});
}
if (type == 'rgb565') {
let start = new Date();
console.log("开始发送");
sendImagePackets(array).then(() => {
console.log("发送完成");
let end = new Date();
var diff = (end.getTime() - start.getTime()) / 1000;
let s = diff % 60;
let m = (diff-s) / 60;
console.log("发送完成,耗时:" + m + "分" + s + "秒");
uni.showModal({
content: "发送完成,耗时:" + m + "分" + s + "秒",
title: 'success'
});
}).catch((ex1) => {
console.log("出现了异常", ex1)
}).finally(() => {
uni.hideLoading();
});
return;
}
},
HoldYouHand() {
var promise=new Promise((resolve,reject)=>{
try{
let start=new Date();
var str = "video transmit start"; //握手的协议字符串
console.log("开始握手:"+str)
// 1. 创建 ArrayBuffer 和 DataView
const buffer = new ArrayBuffer(str.length);
const dataView = new DataView(buffer);
// 2. 将字符串转换为 ASCII 码并写入 DataView
for (let i = 0; i < str.length; i++) {
dataView.setUint8(i, str.charCodeAt(i));
}
//console.log("111111");
this.sendData(buffer).then(() => {
// 开始发送第一个包
setTimeout(()=>{
let end = new Date();
var diff = (end.getTime() - start.getTime()) / 1000;
let s = diff % 60;
let m = (diff - s) / 60;
console.log("握手成功并完成2200ms等待,耗时"+m+"分"+s+"秒");
resolve(true);
}, 2200);
}).catch(err => {
//console.log("握手没有成功");
reject(err);
});
}catch(ex){
reject(ex);
}
});
return promise;
},
bleSendComplete() {
var str = "transmit complete"; //握手的协议字符串
let buffer = new ArrayBuffer(str.length);
let dataView = new DataView(buffer);
for (let i = 0; i < str.length; i++) {
dataView.setUint8(i, str.charCodeAt(i));
}
setTimeout(()=>{
this.sendData(buffer).then(() => {
console.log("完成指令发送成功");
}).catch(err => {
console.log("完成指令发送失败");
});
},500)
},
sendData(buffer) {
if(this.netMode=='ble'){
return this.sendBle(buffer);
}
return this.sendMQ(buffer);
},
sendBle(buffer){
return new Promise(async (resolve, reject) => {
// //console.log("开始发送数据buffer");
var promise = new Promise((succ, err) => {
uni.writeBLECharacteristicValue({
deviceId: this.connectedDeviceId,
serviceId: this.serviceId,
characteristicId: this.writeCharacteristicId,
value: buffer,
writeType: plus.os.name == 'iOS' ? 'write' : 'writeNoResponse',
success: () => {
// //console.log("发送数据成功");
succ();
},
fail: (ex) => {
//console.log("发送数据失败", ex);
err(ex);
},
complete: function() {
// //console.log("123456");
}
});
});
if (plus.os.name == 'iOS') {
function timeout(ms) {
return new Promise((_, err) => {
setTimeout(() => {
err({
code: -1,
errMsg: '超时了'
})
}, ms);
});
}
let inteval = parseInt(this.inteval ? this.inteval : 0);
Promise.race([promise, timeout(inteval)]).then(resolve).catch((ex) => {
//console.log("ex=", ex);
if (ex.code == -1) {
resolve();
} else {
reject();
}
}).finally(() => {
//console.log("完成了")
});
} else {
promise.then(resolve).catch(reject);
}
});
},
sendMQ(message) {
const topic = `B/${this.IMEI}`;
return new Promise((resolve, reject) => {
if(!mqttClient){
reject("MQTT未连接");
return;
}
try {
let flag=mqttClient.publish(topic, message, {
qos: 1
});
if(flag){
resolve();
return;
}
reject("MQTT未连接无法发布消息");
} catch (error) {
reject(error);
}
});
}
}
}
</script>
<style>
.sending-progress {
margin-top: 30rpx;
width: 100%;
}
.progress-bar {
height: 12rpx;
background-color: #e0e0e0;
border-radius: 6rpx;
overflow: hidden;
}
.progress-fill {
height: 100%;
background-color: #409eff;
transition: width 0.3s;
}
input {
border: 2rpx solid #000000;
}
button {
height: 100rpx;
line-height: 100rpx;
font-size: 28rpx;
float: left;
}
.content {
width: 100vw;
height: 300px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
</style>

View File

@ -0,0 +1,115 @@
<template>
<view class="container">
<view class="grid">
<view class="cell" @click="goToDetail(item.name)" v-for="item,index in options">{{item.url}}</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
options: [{
name: '/pages/BlueTooth/ModeSetting/ModeSetting',
url: '7307'
},
{
name: '/pages/BlueTooth/ModeSetting/HBY650',
url: 'HBY650'
},
{
name: '/pages/BlueTooth/ModeSetting/HBY650_1',
url: 'HBY650_V1'
},
{
name: '/pages/BlueTooth/ModeSetting/HBY6155',
url: '6155'
},
{
name: '/pages/BlueTooth/ModeSetting/HBY6155V1',
url: '6155_V1'
},
{
name: "/pages/BlueTooth/ModeSetting/HBY670",
url: 'HBY670'
},
{
name: '/pages/MapTest/MapTest',
url: '地图测试'
},
{
name: '',
url: '更多'
}
]
}
},
onShow: () => {
},
methods: {
goToDetail: function(url) {
console.log("url=" + url)
let qd = () => {
uni.showToast({
title: '敬请期待',
duration: 2000,
icon: "none"
});
};
if (url) {
uni.navigateTo({
url: url,
success: () => {
},
fail: () => {
qd();
}
});
} else {
qd();
}
}
}
}
</script>
<style>
.container {
padding: 15px;
background-color: #f5f5f5;
min-height: 100vh;
}
.grid {
display: flex;
align-content: space-around;
align-items: stretch;
justify-items: center;
flex-direction: row;
flex-wrap: wrap;
justify-content: flex-start;
}
.cell {
padding: 0rpx 20rpx;
border: 2rpx solid rgba(0, 0, 0, 0.3);
border-radius: 15rpx;
font-size: 28rpx;
height: 70rpx;
line-height: 70rpx;
margin-left: 30rpx;
margin-top: 20rpx;
}
</style>

View File

@ -0,0 +1,273 @@
<template>
<view class="update-container">
<!-- 进度条 -->
<view v-if="showProgress" class="progress-container">
<view class="progress-title">正在更新 {{ progress }}%</view>
<view class="progress-bar">
<view class="progress" :style="{ width: progress + '%' }"></view>
</view>
</view>
<!-- 更新提示弹窗 -->
<view v-if="showUpdateDialog" class="dialog-mask">
<view class="dialog">
<view class="dialog-title">发现新版本 v{{ newVersion }}</view>
<view class="dialog-content">{{ updateInfo.desc || '有新的功能和优化,建议立即更新' }}</view>
<view class="dialog-buttons">
<button v-if="!updateInfo.force" class="cancel-btn" @click="showUpdateDialog = false">稍后更新</button>
<button class="confirm-btn" @click="startUpdate">立即更新</button>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
// 当前版本
currentVersion: '',
// 最新版本
newVersion: '',
// 更新信息
updateInfo: {},
// 是否显示更新弹窗
showUpdateDialog: false,
// 是否显示进度条
showProgress: false,
// 更新进度
progress: 0
};
},
onLoad() {
// 初始化时检查版本
this.checkVersion();
},
methods: {
/**
* 检查当前版本
*/
async checkVersion() {
try {
// 获取当前应用版本
const versionInfo = plus.runtime.version;
this.currentVersion = versionInfo;
console.log('当前版本:', this.currentVersion);
// 调用后端接口检查最新版本
// 这里替换为你的后端接口地址
const res = await this.$http.get('/api/checkVersion', {
platform: uni.getSystemInfoSync().platform,
version: this.currentVersion
});
if (res.code === 0 && res.data.hasUpdate) {
this.newVersion = res.data.version;
this.updateInfo = res.data;
// 显示更新提示
this.showUpdateDialog = true;
} else {
console.log('当前已是最新版本');
}
} catch (error) {
console.error('版本检查失败:', error);
}
},
/**
* 开始更新
*/
startUpdate() {
if (!this.updateInfo.downloadUrl) {
uni.showToast({
title: '更新地址不存在',
icon: 'none'
});
return;
}
this.showUpdateDialog = false;
this.showProgress = true;
this.downloadWgt(this.updateInfo.downloadUrl);
},
/**
* 下载wgt包
*/
downloadWgt(url) {
const downloadTask = uni.downloadFile({
url: url,
success: (downloadResult) => {
if (downloadResult.statusCode === 200) {
// 下载成功安装wgt包
this.installWgt(downloadResult.tempFilePath);
} else {
this.showProgress = false;
uni.showToast({
title: '下载失败',
icon: 'none'
});
}
},
fail: (err) => {
this.showProgress = false;
console.error('下载失败:', err);
uni.showToast({
title: '下载失败,请稍后重试',
icon: 'none'
});
}
});
// 监听下载进度
downloadTask.onProgressUpdate((res) => {
this.progress = res.progress;
});
},
/**
* 安装wgt包
*/
installWgt(path) {
plus.runtime.install(
path,
{
force: false // 是否强制安装
},
() => {
console.log('安装成功');
this.showProgress = false;
// 安装成功后提示重启
uni.showModal({
title: '更新完成',
content: '应用已更新,是否立即重启?',
showCancel: !this.updateInfo.force,
success: (res) => {
if (res.confirm) {
// 重启应用
plus.runtime.restart();
}
}
});
},
(err) => {
console.error('安装失败:', err);
this.showProgress = false;
uni.showToast({
title: '更新失败: ' + err.message,
icon: 'none',
duration: 3000
});
}
);
}
}
};
</script>
<style scoped>
.update-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 9999;
}
/* 进度条样式 */
.progress-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
background-color: #fff;
padding: 10rpx 20rpx;
pointer-events: auto;
}
.progress-title {
font-size: 28rpx;
color: #333;
margin-bottom: 10rpx;
text-align: center;
}
.progress-bar {
height: 8rpx;
background-color: #eee;
border-radius: 4rpx;
overflow: hidden;
}
.progress {
height: 100%;
background-color: #007aff;
transition: width 0.3s ease;
}
/* 弹窗样式 */
.dialog-mask {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
pointer-events: auto;
}
.dialog {
width: 600rpx;
background-color: #fff;
border-radius: 16rpx;
overflow: hidden;
}
.dialog-title {
font-size: 36rpx;
font-weight: bold;
color: #333;
padding: 30rpx;
text-align: center;
border-bottom: 1px solid #eee;
}
.dialog-content {
font-size: 32rpx;
color: #666;
padding: 40rpx 30rpx;
line-height: 1.6;
}
.dialog-buttons {
display: flex;
border-top: 1px solid #eee;
}
.cancel-btn, .confirm-btn {
flex: 1;
height: 100rpx;
line-height: 100rpx;
font-size: 32rpx;
border: none;
background-color: transparent;
}
.cancel-btn {
color: #666;
border-right: 1px solid #eee;
}
.confirm-btn {
color: #007aff;
}
</style>

View File

@ -146,10 +146,31 @@
}
try {
console.log('44444');
if(uni.getStorageSync("token") && uni.getStorageSync("clientID")){//免登陆
let time=uni.getStorageSync("tokenTime");
if(!time){
time=0;
}
let currTime=new Date().getTime();
if(currTime<time){
uni.switchTab({
url: '/pages/common/index/index'
});
return;
}else{
//token过期了
uni.removeStorageSync("token")
uni.removeStorageSync("clientID")
uni.removeStorageSync("tokenTime")
}
}
uni.showLoading({
title: '登录中...'
})
console.log('44444');
// 调用登录接口
const res = await login({
phonenumber: this.phone,
@ -158,16 +179,21 @@
})
if (res.code == 200) {
console.log(res, 'ressss');
uni.hideLoading()
uni.setStorageSync('token', res.data.access_token) // 缓存token
uni.setStorageSync('clientID', res.data.client_id) // 缓存token
uni.hideLoading();
uni.setStorageSync('token', res.data.access_token); // 缓存token
uni.setStorageSync('clientID', res.data.client_id); // 缓存token
uni.setStorageSync('tokenTime',new Date().getTime()+86400000);//过期时间
uni.showToast({
title: '登录成功',
icon: 'success'
})
uni.switchTab({
url: '/pages/common/index/index'
})
});
// uni.navigateTo({
// url:"/pages/BlueTooth/ModeSetting/index"
// });
} else {
uni.showToast({
title: res.msg,

View File

@ -433,6 +433,7 @@ class BleHelper {
uni.onBLECharacteristicValueChange((receive) => {
//订阅消息
console.log("收到订阅消息",receive);
let f=this.data.LinkedList.find((v) => {
return v.deviceId == receive.deviceId;
})
@ -505,10 +506,8 @@ class BleHelper {
this.cfg.receivDataCallback.forEach((rec) => {
if (rec.callback) {
}
rec.callback(recData, f, path);
}
})
}
} else {
@ -1142,7 +1141,7 @@ class BleHelper {
})); //没有找到指定设备
return;
}
console.log("device=", device);
// console.log("device=", device);
uni.writeBLECharacteristicValue({
deviceId: device.deviceId,
serviceId: device.writeServiceId,

View File

@ -55,7 +55,10 @@ class BleReceive {
console.log("该设备是650");
return this.Receive_650(receive,f,path);
}
if(f.detailPageUrl=='/pages/670/HBY670'){
console.log("该设备是670");
return this.Receive_670(receive,f,path);
}
}
console.log("已收到该消息,但无法处理",f);
@ -63,7 +66,7 @@ class BleReceive {
}
Receive_650(receive,f,path) {
console.log("通用程序正在处理650的数据");
console.log("通用程序正在处理650的数据",receive);
var parseData = () => {
let bytes = receive.bytes;
@ -74,6 +77,7 @@ class BleReceive {
let staticLevelByte = bytes[1];
let staticLevelText = '未知';
let modeCurr = "";
switch (staticLevelByte) {
case 0x65:
staticLevelText = '高档';
@ -190,6 +194,46 @@ class BleReceive {
this.setBleFormData(formData,f);
return formData;
}
else{
let receiveData={a:1};
try {
let json=JSON.parse(str);
if("staBlue_picture" in json){
//重发图片
console.log("收到重新发送图片的命令");
receiveData=json;
}
else if("staBlue_text" in json){
//重发文本
console.log("收到重新发送文本的命令");
receiveData=json;
}
else if("staBlue_vidio" in json){
//重发视频
console.log("收到重新发送视频的命令");
receiveData=json;
}
else if("staBlue" in json){
if(json.staBlue=="finish"){
console.log("收到设备回复,全部传输完成");
receiveData=json;
}
}
else{
receiveData={};
console.log("无法解析该文本");
}
} catch (error) {
receiveData={};
console.log("文本解析失败")
}
return receiveData;
}
return null;
} catch (ex) {
return null;
@ -199,11 +243,149 @@ class BleReceive {
}
let data=parseData();
console.log("data=",data);
return data;
}
Receive_670(receive,f,path){
var todo = (bytes) =>{
let receiveData = {};
if (bytes[0] == 0x55) {
try {
// 跳过帧头(第一个字节),从第二个字节开始解析
let staticLevelByte = bytes[1];
let staticLevelText = '未知';
switch (staticLevelByte) {
case 0x65:
staticLevelText = '高档';
break
case 0x66:
staticLevelText = '中档';
break
case 0x67:
staticLevelText = '低档';
break
case 0x68:
staticLevelText = '关闭';
break
}
// 解析照明档位
let lightingLevelByte = bytes[2];
let lightingLevelText = lightingLevelByte === 0x6d ? '强光': lightingLevelByte === 0x6e ? '弱光': '关闭';
// 解析剩余照明时间(第三和第四字节,小端序)
let lightingTime = (bytes[3] << 8) | bytes[4];
// 解析剩余电量 // 电量百分比范围检查
let batteryLevelByte = bytes[5];
let batteryLevel = Math.max(0, Math.min(100, batteryLevelByte));
let warn = bytes[6];
if (warn == 0x00) {
warn = '无预警';
} else if (warn == 0x01) {
warn = '弱预警';
} else if (warn == 0x02) {
warn = '中预警';
} else if (warn == 0x03) {
warn = '强预警';
} else if (warn == 0x04) {
warn = '非常强预警';
}
let staticWarn = bytes[7] == 0x01 ? '静止报警': '无静止报警';
let fourGStrenth = bytes[8]; //4g信号强度
let sosTxt = bytes[9] == 0x00 ? '关闭' : bytes[9] == 0x01 ? '爆闪模式' : '红蓝模式';
receiveData.staticLevel = staticLevelText;
receiveData.lightingLevel = lightingLevelText;
receiveData.lightingTime = lightingTime ;
receiveData.batteryLevel = batteryLevel;
receiveData.warnLevel = warn;
receiveData.staticWarn = staticWarn;
receiveData.fourGStrenth = fourGStrenth;
receiveData.SOS=sosTxt;
} catch(error) {
console.log('数据解析错误:', error);
}
} else {
try {
let uint8Array = new Uint8Array(receive.value);
let str = '';
for (let i = 0; i < uint8Array.length; i++) {
// 将每个字节转换为对应的字符
str += String.fromCharCode(uint8Array[i]);
}
if (str.indexOf('mac address:') == 0) {
receiveData.macAddress = str.split(':').slice(1).join(":");
console.log('收到mac地址:', +this.receiveData.macAddress);
} else if (str.indexOf('imei:') == 0) {
receiveData.IMEI = str.split(':')[1];
console.log('收到IEMI:', +this.receiveData.macAddress);
} else if (str.indexOf('longitude:') == 0) {
receiveData.Lon = str.split(':')[1];
console.log('收到经度:', +this.receiveData.macAddress);
} else if (str.indexOf('latitude:') == 0) {
receiveData.Lat = str.split(':')[1];
console.log('收到纬度:', +this.receiveData.macAddress);
} else {
try {
let json=JSON.parse(str);
if("staBlue_picture" in json){
//重发图片
console.log("收到重新发送图片的命令");
receiveData=json;
}
else if("staBlue_text" in json){
//重发文本
console.log("收到重新发送文本的命令");
receiveData=json;
}
else if("staBlue_vidio" in json){
//重发视频
console.log("收到重新发送视频的命令");
receiveData=json;
}
else if("staBlue" in json){
if(json.staBlue=="finish"){
console.log("收到设备回复,全部传输完成");
receiveData=json;
}
}
else{
receiveData={};
console.log("无法解析该文本");
}
} catch (error) {
receiveData={};
console.log("文本解析失败")
}
}
} catch(ex) {
receiveData={};
console.log('将数据转文本失败', ex);
}
}
this.setBleFormData(receiveData,f);
return receiveData;
}
let data=todo();
return data;
}
Receive_6155() {
console.log("通用程序正在处理6155的数据");
}

218
utils/Common.js Normal file
View File

@ -0,0 +1,218 @@
var cfg={
Version:'Uat',//Dev:开发环境Uat:Uat环境Relese正式环境
DevApi:'http://192.168.110.54:8000/',//开发环境
UatApi:'http://114.55.111.217/',//UAT环境
ReleseApi:'http://relese:3169/api/'//Relese环境
}
export default {
baseURL : cfg.Version=='Dev'?cfg.DevApi:(cfg.Version=='Uat'?cfg.UatApi:cfg.ReleseApi),
guid:function generateUUID() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
const r = Math.random() * 16 | 0;
const v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
},
alert: function(title, content, callback) {
if(!title){
title='提示'
}
if(!content){
content=title;
}
uni.showModal({
title: title,
content: content,
success: function(res) {
if (res.confirm) {
console.log('用户点击确定');
} else if (res.cancel) {
console.log('用户点击取消');
}
if (callback) {
callback(res);
}
}
});
},
showLoading:function(title,mask){
uni.showLoading({
title:title,
mask:mask,
})
},
hideLoading:function(){
uni.hideLoading();
},
showToast:function(title,mask,duration,callback){
if(!duration){
duration=1500;
}
if(mask==undefined){
mask=false;
}
uni.showToast({
title:title,
mask:mask,
duration:duration,
callback:callback,
icon: 'none'
})
},
GetData:function(url,data,method,contentType,succ,err,complete){
var these=this;
if(!url){
console.error("url为空");
return;
}
if(url.toLowerCase().indexOf('http://')==-1 || url.toLowerCase().indexOf('https://')==-1){
if(url.indexOf('/')==0){
url=url.substr(1,url.length-1);
}
let ServerPath=these.DevApi;
if(these.Version==='Dev'){
ServerPath=these.DevApi;
}
else if(these.Version==='Uat'){
ServerPath=these.UatApi;
}
else if(these.Version==='Relese'){
ServerPath=these.ReleseApi;
}else{
these.DevApi
}
url=ServerPath+url;
}
var these=this;
if(!method){
method='POST';
}
method=method.toUpperCase();
if(!contentType){
contentType='application/json;charset=UTF-8';
}
these.checkLAN(
function(){
these.showLoading('请稍候..',true);
setTimeout(function(){
uni.request({
url:url,
data:data,
header:{
"Content-Type":contentType
},
method:method,
timeout:60000,
dataType:'json',
success:function(json){
if(succ){
succ(json);
}
},
fail:function(ex){
if(err){
err(ex);
}
},
complete:function(){
if(complete){
complete();
}
}
});
},0);
}
,function(){
these.showToast('无网络连接');
});
},
checkLAN:function(succ,error){
uni.getNetworkType({
success: (res) => {
const networkType = res.networkType;
// 判断网络是否连接
if (networkType === 'none') {
console.error('无网络连接')
if(error){
error();
}
}else{
if(succ){
succ();
}
}
},
fail: (err) => {
console.error('获取网络状态失败:', err);
if(error){
error();
}
}
});
},
DateFormat: function(date, format) {
if(!date){
date=new Date();
}
if(!format){
format='yyyy-MM-dd HH:mm:ss';
}
// 处理参数默认值
if (typeof date === 'string' || typeof date === 'number') {
date = new Date(date);
}
date = date instanceof Date ? date : new Date();
format = format || 'yyyy-MM-dd';
// 检查日期是否有效
if (isNaN(date.getTime())) {
return 'Invalid Date';
}
// 定义格式化映射
const formatMap = {
'yyyy': date.getFullYear(),
'MM': String(date.getMonth() + 1).padStart(2, '0'),
'dd': String(date.getDate()).padStart(2, '0'),
'HH': String(date.getHours()).padStart(2, '0'),
'mm': String(date.getMinutes()).padStart(2, '0'),
'ss': String(date.getSeconds()).padStart(2, '0'),
'SSS': String(date.getMilliseconds()).padStart(3, '0'),
'M': date.getMonth() + 1,
'd': date.getDate(),
'H': date.getHours(),
'm': date.getMinutes(),
's': date.getSeconds(),
'S': date.getMilliseconds()
};
// 替换格式字符串中的占位符
return format.replace(/(yyyy|MM|dd|HH|mm|ss|SSS|M|d|H|m|s|S)/g, (match) => {
return formatMap[match];
});
}
}

View File

@ -1,6 +1,7 @@
// 兼容性补丁:为 Paho MQTT 提供 uni-app 环境下的 WebSocket 实现
(function(root) {
// 如果未能找到全局对象,则无法应用补丁。
console.log("root=",root);
if (!root) {
console.error("MQTT Polyfill: 未能找到全局对象 (global/window/self 均未定义)。");
return;
@ -171,7 +172,7 @@ class MqttClient {
potentialJsons.forEach(jsonString => {
if (jsonString.trim() === '') return;
if (this.messageCallbacks.has(topic)) {
this.messageCallbacks.get(topic)(jsonString);
this.messageCallbacks.get(topic)(jsonString,message);
}
});
};
@ -281,8 +282,10 @@ class MqttClient {
mqttMessage.qos = 1;
this.client.send(mqttMessage);
console.log(`成功发布消息到主题 ${topic}: ${message}`);
return true;
} else {
console.error('MQTT未连接无法发布');
return false;
}
}

153
utils/update.js Normal file
View File

@ -0,0 +1,153 @@
/**
* 检查并执行wgt热更新
* @param {String} updateUrl - 检查更新的接口地址
*/
function checkAndUpdateWgt(updateUrl) {
if(!plus){
return;
}
// 显示加载提示
// 1. 获取当前应用版本信息
plus.runtime.getProperty(plus.runtime.appid, (widgetInfo) => {
const currentVersion = widgetInfo.version;
console.log("当前版本:" + currentVersion);
// 2. 调用后端接口检查是否有更新
uni.request({
url: updateUrl,
method: 'GET',
data: {
currentVersion: currentVersion,
platform: uni.getSystemInfoSync().platform
},
success: (res) => {
uni.hideLoading();
console.log("res=", res)
if (res.statusCode === 200) {
const updateInfo = res.data.data;
if (!updateInfo.hasUpdate) {
return;
}
// 3. 显示更新提示
uni.showModal({
title: '检测到更新',
content: updateInfo.description || '有新版本可用,是否立即更新?',
confirmText: '立即更新',
cancelText: '稍后更新',
success: (modalRes) => {
if (modalRes.confirm) {
downloadAndInstallWgt(updateInfo.downloadUrl);
}
}
});
} else {
uni.showToast({
title: '当前已是最新版本',
icon: 'none',
duration: 2000
});
}
},
fail: (err) => {
uni.hideLoading();
uni.showToast({
title: '检查更新失败',
icon: 'none',
duration: 2000
});
console.error('检查更新失败:', err);
}
});
});
}
/**
* 下载并安装wgt更新包
* @param {String} wgtUrl - wgt包下载地址
*/
function downloadAndInstallWgt(wgtUrl) {
// 显示下载进度
var wating=plus.nativeUI.showWaiting({
title:"下载中0%"
});
// uni.showLoading({
// title: '更新下载中...',
// mask: true
// });
// 1. 下载wgt包
const downloadTask = uni.downloadFile({
url: wgtUrl,
success: (downloadRes) => {
wating.setTitle("下载完成,正在安装");
if (downloadRes.statusCode === 200) {
// 2. 安装wgt包
plus.runtime.install(downloadRes.tempFilePath, {
force: true // 是否强制安装
}, () => {
uni.removeSavedFile({
filePath: downloadRes.tempFilePath,
success() {
console.log("临时文件已删除");
},
fail() {
console.log("无法删除临时文件");
},
complete() {
wating.close();
uni.showModal({
title: '更新完成',
content: '应用已更新,是否重启应用?',
showCancel: false,
success: () => {
// 3. 重启应用
plus.runtime.restart();
}
});
}
});
}, (error) => {
wating.close();
uni.showToast({
title: '安装失败: ' + error.message,
icon: 'none',
duration: 3000
});
console.error('wgt安装失败:', error);
});
} else {
wating.close();
uni.showToast({
title: '下载失败',
icon: 'none',
duration: 2000
});
}
},
fail: (err) => {
uni.hideLoading();
uni.showToast({
title: '下载失败: ' + err.errMsg,
icon: 'none',
duration: 2000
});
console.error('wgt下载失败:', err);
}
});
// 监听下载进度
downloadTask.onProgressUpdate((progress) => {
console.log('下载进度: ' + progress.progress + '%');
wating.setTitle("下载中"+ progress.progress + '%');
// 可以在这里更新自定义进度条
});
}
export default {
checkAndUpdateWgt
};