Files
APP/pages/7305/BJQ7305.vue
2025-11-01 17:32:03 +08:00

1807 lines
45 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="content contentBg">
<view class="eq">
<view class="leftImg" @click.stop="previewImg(formData.img)">
<image class="img" :src="formData.img" mode="aspectFit"></image>
</view>
<view class="rightTxt">
<view class="row">
<image class="img" src="/static/images/6155/DeviceDetail/battry.png" mode="aspectFit"></image>
<view class="txt">
<view class="bigTxt">{{formData.battary}}%</view>
<view class="smallTxt">电量</view>
</view>
</view>
<view class="row">
<image class="img" src="/static/images/6155/DeviceDetail/time.png" mode="aspectFit"></image>
<view class="txt">
<view class="bigTxt">{{formData.xuhang}}</view>
<view class="smallTxt">续航时间</view>
</view>
</view>
</view>
</view>
<view class="eqinfo">
<view class="item">
<text class="lbl">蓝牙名称</text>
<text class="value">{{formData.blename}}</text>
</view>
<view class="item">
<text class="lbl">设备名称</text>
<text class="value">{{formData.deviceName}}</text>
</view>
<view class="item">
<text class="lbl">充电状态</text>
<text class="value">{{formData.statu}}</text>
</view>
<view class="item">
<text class="lbl">蓝牙状态</text>
<text class="value" :class="formData.bleStatu?'green':'red'">{{formData.bleStatu?'已连接':'未连接'}}</text>
</view>
</view>
<view class="lamp">
<view class="title">
<text>灯光亮度</text>
<text>{{formData.liangDu}}%</text>
</view>
<view class="slider-container">
<slider min="1" max="100" step="1" :disabled="false" :value="formData.liangDu" activeColor="#bbe600"
backgroundColor="#00000000" block-size="20" block-color="#ffffffde" @change="sliderChange"
@changing="sliderChange" class="custom-slider" />
</view>
</view>
<view class="lampMode">
<view class="mode fleft" v-on:click.stop="ModeSetting('main')">
<view class="leftImg">
<image class="img" src="/static/images/6155/DeviceDetail/mainLamp.png" mode="aspectFit"></image>
</view>
<view class="rightTxt">
<text class="bigTxt">主灯模式</text>
<text class="smallTxt">{{getMode}}</text>
</view>
</view>
<!-- <view class="mode marginLeft fleft" v-on:click.stop="ModeSetting('fu')">
<view class="leftImg">
<image class="img" src="/static/images/6155/DeviceDetail/fuLamp.png" mode="aspectFit"></image>
</view>
<view class="rightTxt">
<text class="bigTxt">辅灯模式</text>
<text class="smallTxt">泛光模式</text>
</view>
</view> -->
<view class="mode marginLeft fleft" v-on:click.stop="UploadOpenImg()">
<view class="leftImg">
<image class="img" src="/static/images/6155/DeviceDetail/open.png" mode="aspectFit"></image>
</view>
<view class="rightTxt">
<text class="bigTxt">开机画面</text>
<text class="smallTxt">上传</text>
</view>
</view>
<view class="clear"></view>
</view>
<view class="usrinfo">
<view>
<text class="usrtitle fleft">人员信息登记</text>
<view class="btnSend fright" v-on:click.stop="sendUsr">发送</view>
<view class="clear"></view>
<textToDotMatrixFor7305 class="TextToHex" ref="textToHex" :txts="formData.textLines"
:bgColor="'#FFFFFF'" :color="'#000000'" :fontSize="11" ></textToDotMatrixFor7305>
</view>
<view class="item">
<text class="lbl">单位</text>
<input class="value" v-model="formData.textLines[0]" placeholder="请输入"
placeholder-class="usrplace" />
</view>
<view class="item">
<text class="lbl">部门</text>
<input class="value" v-model="formData.textLines[1]" placeholder="请输入"
placeholder-class="usrplace" />
</view>
<view class="item">
<text class="lbl">姓名</text>
<input class="value" v-model="formData.textLines[2]" placeholder="请输入"
placeholder-class="usrplace" />
</view>
</view>
<view class="proinfo lamp">
<text class="title">产品信息</text>
<view class="itemcontent">
<view class="item" @click="proParam()">
<image class="img" src="/static/images/6155/DeviceDetail/param.png" mode="aspectFit"></image>
<text class="txt">产品参数</text>
</view>
<view class="item" @click="handRemark()">
<image class="img" src="/static/images/6155/DeviceDetail/remark.png" mode="aspectFit"></image>
<text class="txt">操作说明</text>
</view>
<view class="item" @click="handVideo()">
<image class="img" src="/static/images/6155/DeviceDetail/video.png" mode="aspectFit"></image>
<text class="txt">操作视频</text>
</view>
</view>
</view>
<!-- 弹窗通知 -->
<MessagePopup :visible="Status.Pop.showPop" :type="Status.Pop.popType" :bgColor="Status.Pop.bgColor"
:borderColor="Status.Pop.borderColor" :textColor="Status.Pop.textColor"
:buttonBgColor="Status.Pop.buttonBgColor" :buttonTextColor="Status.Pop.buttonTextColor"
:iconUrl="Status.Pop.iconUrl" :message="Status.Pop.message" :buttonText="Status.Pop.buttonText"
@buttonClick="HidePop" @closePop="closePop" :visiblePrompt="Status.Pop.visiblePrompt"
:promptTitle="Status.Pop.promptTitle" v-model="Status.Pop.modelValue" />
<!-- 下方菜单 -->
<BottomSlideMenuPlus :config="Status.BottomMenu" @close="closeMenu" @itemClick="handleItemClick"
@btnClick="btnClick">
<view class="addIco">
<view class="icoContent center" v-on:click.stop="checkImgUpload()">
<image mode="aspectFit" class="img" src="/static/images/6155/DeviceDetail/add.png"></image>
</view>
</view>
</BottomSlideMenuPlus>
<global-loading ref="loading" />
</view>
</template>
<script>
import textToDotMatrixFor7305 from '@/components/TextToHex/textToDotMatrixFor7305.vue';
import bleTool from '@/utils/BleHelper.js';
import usrApi from '@/api/670/HBY670.js'
import {
showLoading,
hideLoading,
updateLoading
} from '@/utils/loading.js'
import BleReceive from '@/utils/BleReceive';
var ble = null;
var these = null;
var BrighInteval = null;
var recei = null;
var pagePath = "/pages/7305/BJQ7305";
export default {
components: {
textToDotMatrixFor7305
},
data() {
return {
Status: {
pageHide: false,
Pop: {
showPop: false, //是否显示弹窗
popType: 'custom',
textColor: '#ffffffde',
bgColor: '#383934bd',
borderColor: '#BBE600',
buttonBgColor: '#BBE600',
buttonTextColor: '#232323DE',
iconUrl: '',
message: '您确定要这样做吗?',
buttonText: '确定',
clickEvt: '',
visiblePrompt: false,
promptTitle: '设备名称',
modelValue: '',
visibleClose: false,
okCallback: null
},
BottomMenu: {
show: false,
showHeader: true,
menuItems: [{
text: '强光',
icon: '/static/images/6155/DeviceDetail/qiang.png'
},
{
text: '弱光',
icon: '/static/images/6155/DeviceDetail/ruo.png'
},
{
text: '爆闪',
icon: '/static/images/6155/DeviceDetail/shan.png'
}
],
activeIndex: -1,
bgColor: '#2a2a2a',
itemBgColor: '#3a3a3a',
textColor: '#ffffffde',
textAlign: 'flex-start',
title: '主灯模式',
showDivider: false,
dividerColor: '#00000000',
dividerThickness: '0rpx',
dividerMargin: '10rpx',
itemHeight: '80rpx',
type: '',
showBtn: true,
btnBgColor: "#bbe600",
btnText: "确定",
btnTextColor: "#232323de",
showMask: true,
maskBgColor: '#00000066',
showClose: false
}
},
formData: {
img: '',
battary: '',
xuhang: '',
deviceName: '',
RSSI: '',
statu: '正常',
liangDu: '100',
id: '',
deviceId: '',
textLines: ['', '', ''],
mode: '',
bleStatu: ''
},
inteval: 500,
device: {
id: "",
deviceName: "",
deviceImei: "",
deviceMac: "",
communicationMode: 0,
devicePic: "",
typeName: "",
bluetoothName: null,
deviceStatus: null,
bindingTime: "",
onlineStatus: 0,
battery: "0",
latitude: null,
longitude: null,
alarmStatus: null,
detailPageUrl: "/pages/6155/deviceDetail",
showConfirm: false
}
}
},
onUnload() {
ble.removeAllCallback(pagePath);
},
onLoad: function() {
these = this;
recei = BleReceive.getBleReceive();
ble = bleTool.getBleTool();
ble.addReceiveCallback(these.bleValueNotify, pagePath);
ble.addStateBreakCallback(these.bleStateBreak, pagePath);
ble.addStateRecoveryCallback(these.bleStateRecovry, pagePath);
ble.addDisposeCallback(these.deviceDispose, pagePath);
ble.addRecoveryCallback(these.deviceRecovry, pagePath);
let eventChannel = this.getOpenerEventChannel();
eventChannel.on('detailData', function(data) {
let device = data.data;
these.device = device;
let f = ble.data.LinkedList.find((v) => {
if (v.macAddress == device.deviceMac) {
console.log("找到设备了", v);
these.formData.deviceId = v.deviceId;
return true;
}
return false;
});
if (!f) {
these.getDetail();
these.showBleUnConnect();
return;
}
// 确保设备对象的detailPageUrl正确设置以便ReceiveData能找到对应的handler
if (f.device) {
f.device.detailPageUrl = "/pages/7305/BJQ7305";
} else {
f.device = {
...device,
detailPageUrl: "/pages/7305/BJQ7305"
};
}
let form = f.formData;
if (form) {
let keys = Object.keys(form);
for (var i = keys.length; i >= 0; i--) {
let key = keys[i];
these.formData[key] = form[key];
}
}
these.formData.blename = f.name ? f.name : "Unname";
these.formData.deviceName = device.deviceName;
these.formData.img = device.devicePic;
these.formData.id = device.id;
these.formData.deviceId = f.deviceId;
// 更新缓存中的设备信息
ble.updateCache();
ble.LinkBlue(f.deviceId, f.writeServiceId, f.wirteCharactId, f.notifyCharactId).then(res => {
console.log("连接成功");
these.formData.bleStatu = true;
// 确保订阅消息已开启
let linkedDevice = ble.data.LinkedList.find(v => v.deviceId == f.deviceId);
if (linkedDevice && !linkedDevice.notifyState) {
console.log("连接成功但未订阅,主动订阅消息");
ble.subScribe(f.deviceId, true).then(() => {
console.log("订阅消息成功");
}).catch(err => {
console.error("订阅消息失败:", err);
});
}
});
these.setBleFormData();
these.getDetail();
});
},
onHide: function() {
this.Status.pageHide = true;
console.log("页面隐藏");
},
onShow: function() {
this.Status.pageHide = false;
},
computed: {
RSSIRemark: function() {
let remark = '极弱';
if (this.formData.RSSI <= 0 && this.formData.RSSI >= -30) {
remark = '强';
}
if (this.formData.RSSI < -30 && this.formData.RSSI >= -60) {
remark = '一般';
}
if (this.formData.RSSI < -60 && this.formData.RSSI >= -85) {
remark = '弱';
}
if (this.formData.RSSI < -85 && this.formData.RSSI >= -100) {
remark = '微弱';
}
},
getMode() {
let txt = "关闭";
if (this.Status.BottomMenu.type == 'fu') {
return txt;
}
switch (this.formData.mode) {
case 0:
txt = "强光模式";
break;
case 1:
txt = "弱光模式";
break;
case 2:
txt = "爆闪模式";
break;
case 3:
txt = "关闭";
break;
default:
txt = "关闭";
break;
}
return txt;
}
},
methods: {
deviceRecovry(res) {
if (this.Status.pageHide) {
return;
}
if (res.deviceId == these.formData.deviceId) {
this.formData.bleStatu = true;
// 连接恢复后,确保重新订阅消息以接收设备上报的数据
let f = this.getDevice();
if (f && (f.notifyServiceid || f.notifyCharactId)) {
console.log("连接恢复,重新订阅消息");
ble.subScribe(res.deviceId, true).then(() => {
console.log("订阅消息成功,等待设备上报数据");
}).catch(err => {
console.error("订阅消息失败:", err);
});
}
setTimeout(() => {
hideLoading(these, 1000);
});
uni.showToast({
icon: 'success',
title: '蓝牙连接成功'
});
}
},
deviceDispose(res) {
if (this.Status.pageHide) {
return;
}
if (res.deviceId == these.formData.deviceId) {
this.formData.bleStatu = false;
setTimeout(() => {
hideLoading(these, 1000);
});
uni.showToast({
icon: 'fail',
title: '蓝牙连接已断开'
});
}
},
bleStateBreak() {
if (this.Status.pageHide) {
return;
}
//蓝牙适配器不可用
this.formData.bleStatu = false;
uni.showToast({
icon: 'fail',
title: '蓝牙已不可用'
})
hideLoading(this);
},
bleStateRecovry() {
console.log("蓝牙可用");
if (this.Status.pageHide) {
return;
}
console.log("蓝牙可用");
//蓝牙适配器再次可用,尝试恢复连接
showLoading(this, {
text: "蓝牙恢复可用,正在连接设备"
});
ble.LinkBlue(these.formData.deviceId).then(() => {
these.formData.bleStatu = true;
updateLoading(these, {
text: '连接成功'
});
}).catch(ex => {
updateLoading(these, {
text: ex.msg
})
}).finally(() => {
setTimeout(() => {
hideLoading(these);
}, 1000);
});
},
previewImg(img) {
if (!img) {
return;
}
uni.previewImage({
urls: [img]
})
},
getDevice: function() {
// console.log("LinkedList=", ble.data.LinkedList);
// console.log("formData=", these.formData);
let f = ble.data.LinkedList.find((v) => {
return v.macAddress == these.device.deviceMac;
});
return f;
},
bleValueNotify: function(receive, device, path, recArr) {
if (this.Status.pageHide) {
return;
}
let json = recei.ReceiveData(receive, device, path, recArr);
// 检查返回的数据是否有效应该是解析后的对象不是原始receive对象
// 如果返回的是原始receive对象有bytes属性说明handler没有匹配或解析失败
if (!json || (json.hasOwnProperty('bytes') && (!json.battary && json.battary !== 0))) {
console.log("收到7305数据但未解析或解析失败:", json);
return;
}
console.log("收到7305解析数据:", json);
console.log("formData更新前 - battary:", these.formData.battary, "xuhang:", these.formData.xuhang, "statu:", these.formData.statu);
// 确保电量和续航时间正确更新(使用 $set 确保 Vue 响应式)
if (json.battary !== undefined && json.battary !== null) {
this.$set(these.formData, 'battary', json.battary);
console.log("更新电量:", json.battary, "->", these.formData.battary);
if (json.battary <= 20) {
this.showPop({
message: "设备电量低",
iconUrl: "/static/images/6155/DeviceDetail/uploadErr.png",
borderColor: "#e034344d",
buttonBgColor: "#E03434",
});
}
}
if (json.xuhang !== undefined && json.xuhang !== null) {
this.$set(these.formData, 'xuhang', json.xuhang);
console.log("更新续航时间:", json.xuhang, "->", these.formData.xuhang);
}
// 更新充电状态
if (json.statu !== undefined && json.statu !== null) {
this.$set(these.formData, 'statu', json.statu);
console.log("更新充电状态:", json.statu, "->", these.formData.statu);
}
// 更新其他字段
let keys = Object.keys(json);
keys.forEach((key) => {
if (key in these.formData && key !== 'battary' && key !== 'xuhang' && key !== 'statu') {
this.$set(these.formData, key, json[key]);
}
});
console.log("formData更新后 - battary:", these.formData.battary, "xuhang:", these.formData.xuhang, "statu:", these.formData.statu);
},
proParam: function() {
uni.navigateTo({
url: '/pages/common/productDes/index?id=' + this.formData.id,
success(ev) {
}
});
},
handRemark: function() {
uni.navigateTo({
url: '/pages/common/operatingInstruct/index?id=' + this.formData.id,
success(ev) {
}
});
},
handVideo: function() {
uni.navigateTo({
url: '/pages/common/operationVideo/index?id=' + this.formData.id,
success(ev) {
}
});
},
showBleUnConnect() {
this.showPop({
message: "蓝牙未连接过该设备,请使用蓝牙重新添加该设备",
iconUrl: "/static/images/6155/DeviceDetail/uploadErr.png",
borderColor: "#e034344d",
buttonBgColor: "#E03434",
buttonText: '去连接',
okCallback: function() {
console.log("1111");
uni.navigateTo({
url: "/pages/common/addBLE/addEquip",
events: {
BindOver: function(data) {
console.log(data)
these.formData.bleStatu = true;
}
},
success: function(res) {
// 通过eventChannel向被打开页面传送数据
res.eventChannel.emit('detailData', {
data: these.device
})
},
fail(ex) {
console.log("跳转失败", ex);
}
})
}
});
},
checkImgUpload: function() {
let f = these.getDevice();
if (!f) {
these.showBleUnConnect();
return;
}
// 分包发送图片数据
var sendImagePackets = function(packetData) {
return new Promise((resolve, reject) => {
try {
// 1. 构建一个包含完整头、数据和尾的完整数据包
const header = [0xFA, 0x09, 0x04, 0x00];
const footer = [0xFF];
const imageData = packetData.map(hex => parseInt(hex, 16));
// 确保图像数据为1024字节不足则补0
while (imageData.length < 1024) {
imageData.push(0x00);
}
const fullPacket = new Uint8Array([...header, ...imageData.slice(0, 1024), ...
footer
]);
const fullBuffer = fullPacket.buffer;
// 2. 将完整数据包切片成20字节的小块进行发送
const chunkSize = 20;
const numChunks = Math.ceil(fullBuffer.byteLength / chunkSize);
let chunkIndex = 0;
const sendNextChunk = () => {
if (chunkIndex >= numChunks) {
setTimeout(() => {
hideLoading(these);
these.Status.BottomMenu.show = false;
these.showPop({
message: "上传成功",
iconUrl: "/static/images/6155/DeviceDetail/uploadSuccess.png",
borderColor: '#BBE600',
buttonBgColor: '#BBE600'
});
resolve();
}, 0);
return;
}
const start = chunkIndex * chunkSize;
const end = Math.min(start + chunkSize, fullBuffer.byteLength);
const chunk = fullBuffer.slice(start, end);
const hexArray = Array.from(new Uint8Array(chunk)).map(b => b.toString(
16).padStart(2, '0'));
console.log(`发送图片数据块 ${chunkIndex + 1}/${numChunks}:`, hexArray.join(
' '));
updateLoading(these, {
text: "正在发送 " + (chunkIndex + 1) + "/" + numChunks
});
ble.sendData(f.deviceId, chunk, f.writeServiceId, f.wirteCharactId,
100)
.then(() => {
chunkIndex++;
setTimeout(sendNextChunk, 20); // 每个小包之间延时20ms
})
.catch(err => {
if (err.code == 10007 || err.code == -1) {
setTimeout(sendNextChunk, 20);
return;
}
console.log("发送数据包失败了", err);
these.Status.BottomMenu.show = false;
these.showPop({
message: "发送数据包失败了: " + err.msg,
iconUrl: "/static/images/6155/DeviceDetail/uploadErr.png",
borderColor: "#e034344d",
buttonBgColor: "#E03434",
});
hideLoading(these);
reject(err);
});
};
sendNextChunk();
} catch (ex) {
console.error("构建图片数据包失败", ex);
hideLoading(these);
reject(ex);
}
});
}
uni.chooseImage({
count: 1,
sizeType: ['original', 'compressed'],
sourceType: ['album'],
success: function(res) {
uni.navigateTo({
url: "/pages/6155/ImgCrop",
events: {
ImgCutOver: function(data) {
//将8位的二进制数组转换成16进制数据
let binaryToHex = (binaryArray) => {
if (!Array.isArray(binaryArray) || binaryArray
.length !== 8) {
throw new Error("输入必须是包含8个元素的二进制数组");
}
// 检查每个元素是否为0或1
if (!binaryArray.every(bit => bit === 0 || bit ===
1)) {
throw new Error("数组元素必须只能是0或1");
}
// 将二进制数组转换为十进制数
let decimalValue = 0;
for (let i = 0; i < 8; i++) {
// 计算每个位的权重并累加
decimalValue += binaryArray[i] * Math.pow(2,
7 - i);
}
// 将十进制转换为十六进制字符串并添加0x前缀
const hexString = "0x" + decimalValue.toString(16)
.padStart(2, '0').toUpperCase();
return hexString;
}
let imageData = data.piexls;
let arr = [];
for (let currIndex = 0; currIndex < imageData
.length; currIndex += 4) {
let R = imageData[currIndex];
let G = imageData[currIndex + 1];
let B = imageData[currIndex + 2];
let A = imageData[currIndex + 3];
let grayVal = Math.floor(R * 0.299 + G * 0.587 + B *
0.114);
arr.push(grayVal < 128 ? 1 : 0);
}
let f = arr.filter(v => {
return v === 1;
});
//如果有超过1/3的亮点,将进行反色处理
if (f.length > arr.length / 3) {
for (var r = 0; r < arr.length; r++) {
if (arr[r] === 0) {
arr[r] = 1;
} else {
arr[r] = 0;
}
}
}
// console.log("arr=", JSON.stringify(arr));
let matrix = [];
let size = 512;
let len = arr.length / size;
for (let j = 0; j < len; j++) {
let start = j * size;
let end = start + size;
let firstHalf = arr.slice(start, end);
let arr64 = [];
for (let row = 0; row < 8; row++) {
for (let col = 0; col < 64; col++) {
let ind = col + row * 64;
if (col === 63) {
}
let val = firstHalf[ind];
if (arr64.length < col + 1) {
arr64.push([])
}
arr64[col].push(val);
if (arr64[col].length === 8) {
arr64[col] = arr64[col].reverse();
matrix.push(binaryToHex(arr64[col]));
}
}
}
}
console.log("matrix=", JSON.stringify(matrix));
showLoading(these, {
text: "正在发送0/3"
});
these.Status.BottomMenu.show = false;
setTimeout(function() {
sendImagePackets(matrix).catch((
ex) => {
console.log("出现异常", ex);
hideLoading(these);
});
}, 0)
}
},
success(ev) {
ev.eventChannel.emit('checkImg', {
data: res.tempFiles[0].path,
width: 64,
height: 32,
times: 10
})
},
fail(ex) {
console.log("跳转页面失败" + JSON.stringify(ex));
}
});
}
});
},
UploadOpenImg: function() {
//上传开机画面
this.Status.BottomMenu.menuItems = [];
this.Status.BottomMenu.title = "上传开机画面";
this.Status.BottomMenu.type = "checkImg";
this.Status.BottomMenu.show = true;
},
ModeSetting: function(type) {
this.formData.mode = type;
let items = [];
let title = '';
switch (type) {
case "main":
title = '主灯模式';
items = [{
text: '强光',
icon: '/static/images/6155/DeviceDetail/qiang.png'
},
// {
// text: '工作光',
// icon: '/static/images/6155/DeviceDetail/fan.png'
// },
{
text: '弱光',
icon: '/static/images/6155/DeviceDetail/ruo.png'
},
{
text: '爆闪',
icon: '/static/images/6155/DeviceDetail/shan.png'
},
{
text: '关闭',
icon: '/static/images/6155/DeviceDetail/close.png'
}
];
break;
case "fu":
title = '辅灯模式';
items = [{
text: '泛光',
icon: '/static/images/6155/DeviceDetail/fan.png'
}];
break;
}
this.Status.BottomMenu.menuItems = items;
this.Status.BottomMenu.title = title;
this.Status.BottomMenu.type = type;
this.Status.BottomMenu.show = true;
},
showMenu(flag) {
this.Status.BottomMenu.show = true;
},
closeMenu() {
this.Status.BottomMenu.show = false;
},
btnClick(item, index) {
if (this.Status.BottomMenu.activeIndex == -1) {
this.showToast('请选择模式');
return;
}
//发送指令给设备
switch (this.Status.BottomMenu.type) {
case "main":
this.setMode(this.Status.BottomMenu.activeIndex, this.Status.BottomMenu.type);
break;
case "fu":
this.setMode(this.Status.BottomMenu.activeIndex, this.Status.BottomMenu.type);
break;
}
this.closeMenu();
},
setMode(mode, type) {
let dataValue = 0;
switch (mode) {
case 0:
if (type == 'main') {
dataValue = 0x01;
} else if (type == 'fu') {
dataValue = 0x04;
}
break;
case 1:
dataValue = 0x02;
break;
// case 2:
// dataValue = 0x02;
// break;
case 2:
dataValue = 0x03;
break;
case 3:
dataValue = 0x0B;
break;
}
// console.log("dataValue=", dataValue)
// 构建数据包
var buffer = new ArrayBuffer(6);
var dataView = new DataView(buffer);
dataView.setUint8(0, 0xFA); // 帧头
dataView.setUint8(1, dataValue); // 帧类型
dataView.setUint8(2, 0x00); // 数据
dataView.setUint8(3, 0x01); // 数据
dataView.setUint8(4, 0x00); // 数据
dataView.setUint8(5, 0xFF); // 数据
let f = this.getDevice();
// 发送数据
if (!f) {
these.showBleUnConnect();
return;
}
showLoading(these, {
text: "请稍候..."
});
ble.sendData(f.deviceId, buffer, f.writeServiceId, f.wirteCharactId, 100).then(() => {
this.formData.mode = mode;
this.setBleFormData();
}).catch((ex) => {
these.showPop({
message: "发送失败," + ex.msg,
iconUrl: "/static/images/6155/DeviceDetail/uploadErr.png",
borderColor: "#e034344d",
buttonBgColor: "#E03434",
});
}).finally(() => {
hideLoading(these);
});
},
handleItemClick(item, index) {
switch (this.Status.BottomMenu.type) {
case "main":
break;
case "fu":
break;
}
this.Status.BottomMenu.activeIndex = index;
},
setBleFormData() {
ble.data.LinkedList.find((v) => {
if (v.deviceId == these.formData.deviceId) {
v.formData = these.formData;
return true;
}
return false;
});
uni.setStorage({
key: ble.StorageKey,
data: ble.data.LinkedList
})
},
closePop: function() {
this.Status.Pop.showPop = false;
if (this.Status.Pop.cancelCallback) {
this.Status.Pop.cancelCallback();
}
},
HidePop: function() {
console.log("1111");
if (this.Status.Pop.clickEvt == 'SendUsr') {
}
if (this.Status.Pop.okCallback) {
this.Status.Pop.okCallback();
}
this.Status.Pop.showPop = false;
},
showPop: function(option) {
hideLoading(this);
let defaultCfg = {
showHeader: false,
headerTxt: "",
showHeader: false,
showCancel: false,
borderColor: '#BBE600',
buttonBgColor: '#BBE600',
okCallback: null,
cancelCallback: null,
popType: 'custom',
buttonText: '确定',
clickEvt: ''
};
if (!option) {
}
let keys = Object.keys(option);
for (var i = 0; i < keys.length; i++) {
let key = keys[i];
this.Status.Pop[key] = option[key];
}
keys = Object.keys(defaultCfg);
for (var i = 0; i < keys.length; i++) {
let key = keys[i];
if (key in option) {
continue;
}
this.Status.Pop[key] = defaultCfg[key];
}
this.Status.Pop.showPop = true;
},
sendUsr() {
let f = this.getDevice();
if (!f) {
these.showBleUnConnect()
return;
}
let err = false;
this.formData.textLines.find((txt) => {
if (txt.length === 0 || txt.length > 5) {
console.log("txt=", txt);
err = true;
return true;
}
return false;
})
if (err) {
this.showPop({
message: "单位、部门、姓名必须填写且最多5字",
iconUrl: "/static/images/6155/DeviceDetail/uploadErr.png",
borderColor: "#e034344d",
buttonBgColor: "#E03434",
});
return;
}
showLoading(these, {
text: "请稍候..."
});
this.setBleFormData();
let task = async () => {
var sendTxtPackge = (rgbdata, type, str) => {
var promise = new Promise((resolve, reject) => {
try {
// 1. 构建一个包含完整包头、数据和包尾的261字节数据包
const bufferSize = 261;
let fullBuffer = new ArrayBuffer(bufferSize);
let dataView = new DataView(fullBuffer);
dataView.setUint8(0, 0xFA);
dataView.setUint8(1, type);
dataView.setUint8(2, 0x01);
dataView.setUint8(3, 0x00);
for (let i = 0; i < rgbdata.length; i++) {
dataView.setUint8(4 + i, rgbdata[i]);
}
// 用0填充剩余部分直到最后一个字节
for (let i = 4 + rgbdata.length; i < bufferSize - 1; i++) {
dataView.setUint8(i, 0x00);
}
dataView.setUint8(bufferSize - 1, 0xFF);
// 2. 将完整数据包切片成20字节的小块进行发送
const chunkSize = 20;
const numChunks = Math.ceil(fullBuffer.byteLength / chunkSize);
let chunkIndex = 0;
let sendNextChunk = () => {
if (chunkIndex >= numChunks) {
resolve();
return;
}
const start = chunkIndex * chunkSize;
const end = Math.min(start + chunkSize, fullBuffer.byteLength);
const chunk = fullBuffer.slice(start, end);
const hexArray = Array.from(new Uint8Array(chunk)).map(b => b
.toString(16).padStart(2, '0'));
console.log(`发送数据块 ${chunkIndex + 1}/${numChunks}:`, hexArray
.join(' '));
ble.sendData(f.deviceId, chunk, f.writeServiceId, f
.wirteCharactId, 100).then(() => {
chunkIndex++;
setTimeout(sendNextChunk, 30); // 每个小包之间延时30ms
}).catch(err => {
if (err.code == '10007') {
setTimeout(sendNextChunk, 30);
} else {
reject(err);
}
});
}
sendNextChunk();
} catch (ex) {
console.log("ex=", ex);
reject(ex);
}
});
return promise;
}
console.log("11111");
var result = null;
try {
console.log("this.$refs.textToHex=", this.$refs.textToHex);
result = await this.$refs.textToHex.drawAndGetPixels();
} catch (ex) {
console.log("ex=", ex);
}
if (!result) {
hideLoading(this);
return;
}
console.log("result=", result);
result = result.map(level1 => {
return level1.flat(Infinity);
});
console.log("result=", result);
let h3dic = [0x06, 0x07, 0x08];
let pros = [];
let flag = true;
for (var i = 0; i < result.length; i++) {
let str = this.formData.textLines[i];
if (str.length > 0) {
let width = str.length * 16;
var rgb = result[i];
try {
// console.log("1111");
await sendTxtPackge(rgb, h3dic[i], str);
// console.log("222222");
} catch (ex) {
flag = false;
console.log("发送数据包出现异常", ex);
break;
}
}
}
hideLoading(these);
if (flag) {
console.log("发送成功");
this.showPop({
message: "发送成功",
iconUrl: "/static/images/6155/DeviceDetail/uploadSuccess.png",
borderColor: '#BBE600',
buttonBgColor: '#BBE600'
});
let json = {
deviceId: these.device.id,
name: these.formData.textLines[1],
position: these.formData.textLines[0],
unitName: these.formData.textLines[2],
code: ""
};
usrApi.sendUsr(json)
} else {
this.showPop({
message: "出现异常发送失败",
iconUrl: "/static/images/6155/DeviceDetail/uploadErr.png",
borderColor: "#e034344d",
buttonBgColor: "#E03434",
});
}
}
setTimeout(task, 0);
},
getDetail() {
var that = this;
usrApi.getDetail(this.device.id).then(res => {
if (res && res.code == 200) {
res = res.data;
let personnelInfo = res.personnelInfo;
if (personnelInfo) {
these.formData.textLines[2] = personnelInfo.unitName;
these.formData.textLines[1] = personnelInfo.name;
these.formData.textLines[0] = personnelInfo.position;
}
}
});
},
ack: function() {
let eventChannel = this.getOpenerEventChannel();
eventChannel.emit('ack', {
data: '我已收到了您的消息,谢谢。'
});
},
sliderChange: function(evt) {
this.formData.liangDu = evt.detail.value;
clearTimeout(BrighInteval)
//给蓝牙设备发送信号更新亮度
BrighInteval = setTimeout(() => {
this.sendBrightness();
this.setBleFormData();
}, 100);
},
sendBrightness: function() {
const buffer = new ArrayBuffer(6);
const dataView = new DataView(buffer);
let data = '0x' + parseInt(this.formData.liangDu).toString(16);
console.log("亮度:" + this.formData.liangDu + ',16进制:' + data);
dataView.setUint8(0, 0xFA); // 帧头
dataView.setUint8(1, 0x05); // 帧类型:亮度调节
dataView.setUint8(2, 0x00); // 包序号
dataView.setUint8(3, 0x01); // 包序号
dataView.setUint8(4, data); // 数据长度
dataView.setUint8(5, 0xFF); // 数据长度
let f = this.getDevice();
if (f) {
// 发送数据
ble.sendData(f.deviceId, buffer, f.writeServiceId, f.wirteCharactId, 100).catch(ex => {
these.showPop({
message: "发送失败," + ex.msg,
iconUrl: "/static/images/6155/DeviceDetail/uploadErr.png",
borderColor: "#e034344d",
buttonBgColor: "#E03434",
});
});
}
},
//获取当前设备状态
GetDeviceStatu: function() {
//数据头 命令码 数据 校验码
//JQOC 0B 00 9C41
//硬件响应
//数据头 命令码 主灯模式 副灯模式 亮度(%) 电量(%) 充电状态 校验码
//JQOC 0B 01 01 64 85 01 9C41
this.sendCmd('0x0B');
},
//设备重启
DeviceReStart: function() {
//数据头 命令码 数据 校验码
//JQOC 0C 01 9C412
this.sendCmd('0x0C');
},
//恢复出厂设置
DeviceReSet: function() {
//数据头 命令码 数据 校验码
//JQOC 0D 01 9C412
this.sendCmd('0x0D');
},
sendCmd: function(type, callback) {
//数据头 命令码 数据 校验码
const buffer = new ArrayBuffer(7);
const dataView = new DataView(buffer);
let data = '0x' + parseInt(this.formData.liangDu).toString(16);
console.log("亮度:" + this.formData.liangDu + ',16进制:' + data);
dataView.setUint8(0, 'JQOC'); // 帧头
dataView.setUint8(1, type); // 帧类型:亮度调节
dataView.setUint8(2, '0x01'); // 包序号
dataView.setUint8(3, '9C41'); // 数据长度
let f = this.getDevice();
if (f) {
// 发送数据
ble.sendData(f.deviceId, buffer, f.writeServiceId, f.wirteCharactId, 100).catch(ex => {
these.showPop({
message: "发送失败," + ex.msg,
iconUrl: "/static/images/6155/DeviceDetail/uploadErr.png",
borderColor: "#e034344d",
buttonBgColor: "#E03434",
});
});
}
}
}
}
</script>
<style>
.content {
box-sizing: border-box;
width: 100%;
min-height: 100vh;
height: auto;
}
.contentBg {
background-color: #121212;
color: #ffffffde;
}
.fleft {
float: left;
}
.fright {
float: right;
}
.clear {
clear: both;
}
.displayNone {
display: none !important;
}
.p100 {
width: 100%;
height: 100%;
}
.center {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
align-content: center;
justify-content: center;
align-items: center;
}
.centerLeft {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
align-content: center;
justify-content: flex-start;
align-items: center;
}
.content {
padding: 30rpx;
box-sizing: border-box;
}
.eq {
width: 100%;
height: 250rpx;
border-radius: 8px;
background: rgba(26, 26, 26, 1);
box-sizing: border-box;
padding: 24rpx 28rpx;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
align-content: center;
justify-content: center;
align-items: center;
margin-bottom: 24rpx;
}
.eq .leftImg {
box-sizing: border-box;
padding: 36rpx;
border-radius: 16rpx;
background: rgba(42, 42, 42, 0.5);
}
.eq .leftImg,
.eq .rightTxt {
width: 50%;
height: 100%;
}
.eq .rightTxt {
display: flex;
flex-direction: column;
flex-wrap: nowrap;
align-content: flex-start;
justify-content: space-evenly;
align-items: flex-start;
}
.eq .leftImg .img {
width: 100%;
height: 100%;
}
.eq .rightTxt .img {
width: 52rpx;
height: 52rpx;
}
.eq .rightTxt .row {
width: 100%;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
align-content: center;
justify-content: flex-start;
align-items: center;
box-sizing: border-box;
padding-left: 50rpx;
}
.eq .rightTxt .row .txt {
padding-left: 20rpx;
}
.eq .bigTxt {
color: rgba(255, 255, 255, 0.87);
font-family: "PingFang SC";
font-size: 28rpx;
font-weight: 400;
}
.eq .smallTxt {
color: rgba(255, 255, 255, 0.6);
font-family: "PingFang SC";
font-size: 24rpx;
font-weight: 400;
}
.eqinfo {
border-radius: 16rpx;
background: rgba(26, 26, 26, 1);
box-sizing: border-box;
padding: 14rpx 0rpx;
}
.eqinfo .item {
width: 100%;
height: 60rpx;
line-height: 60rpx;
padding: 0rpx 28rpx;
box-sizing: border-box;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
align-content: center;
justify-content: space-between;
align-items: center;
}
.eqinfo .lbl {
color: rgba(255, 255, 255, 0.87);
font-family: "PingFang SC";
font-size: 28rpx;
font-weight: 400;
letter-spacing: 0.07px;
text-align: left;
}
.eqinfo .value {
color: rgba(255, 255, 255, 0.6);
font-family: "PingFang SC";
font-size: 28rpx;
font-weight: 400;
letter-spacing: 0.07px;
text-align: left;
}
.lamp {
margin-top: 24rpx;
}
.lamp .title {
width: 100%;
height: 60rpx;
line-height: 60rpx;
padding: 0rpx 28rpx;
box-sizing: border-box;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
align-content: center;
justify-content: space-between;
align-items: center;
font-size: 28rpx;
}
.lampMode {
padding: 30rpx 0rpx;
width: 100%;
box-sizing: border-box;
}
.lampMode .mode {
width: calc(calc(100% - 34rpx) / 2);
border-radius: 8px;
margin-top: 20rpx;
background: rgba(26, 26, 26, 1);
display: flex;
flex-direction: row;
flex-wrap: nowrap;
align-content: center;
align-items: center;
justify-content: flex-start;
padding: 30rpx;
box-sizing: border-box;
}
.mode.marginLeft {
margin-left: 34rpx !important;
}
.mode .leftImg .img {
width: 70rpx;
height: 70rpx;
}
.mode .rightTxt {
display: flex;
flex-direction: column;
flex-wrap: nowrap;
align-content: flex-start;
justify-content: center;
align-items: flex-start;
padding-left: 20rpx;
}
.mode .bigTxt {
color: rgba(255, 255, 255, 0.87);
font-family: "PingFang SC";
font-size: 28rpx;
font-weight: 400;
letter-spacing: 0.07px;
}
.mode .smallTxt {
color: rgba(255, 255, 255, 0.6);
font-family: "PingFang SC";
font-size: 24rpx;
font-weight: 400;
letter-spacing: 0.07px;
}
.usrinfo {
border-radius: 8px;
width: 100%;
box-sizing: border-box;
padding: 24rpx 28rpx;
background: rgba(26, 26, 26, 1);
}
.usrtitle {
color: rgba(255, 255, 255, 0.87);
font-family: "PingFang SC";
font-size: 28rpx;
font-weight: 400;
height: 40rpx;
line-height: 40rpx;
letter-spacing: 0.07px;
}
.usrinfo .btnSend {
line-height: 65rpx;
border-radius: 8px;
width: 120rpx;
height: 65rpx;
color: rgba(35, 35, 35, 0.87);
font-family: "PingFang SC";
font-size: 20rpx;
font-weight: 400;
letter-spacing: 0.07px;
text-align: center;
background-color: #BBE600;
}
.usrinfo .item {
border-radius: 8px;
width: 100%;
box-sizing: border-box;
line-height: 90rpx;
height: 90rpx;
background: rgba(42, 42, 42, 1);
margin-top: 24rpx;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
align-content: center;
justify-content: flex-start;
align-items: center;
}
.usrinfo .lbl {
width: 120rpx;
height: 100%;
padding-left: 24rpx;
box-sizing: border-box;
text-align: right;
color: rgba(255, 255, 255, 0.87);
font-family: "PingFang SC";
font-size: 28rpx;
font-weight: 400;
letter-spacing: 0.07px;
}
.usrinfo .value {
width: calc(100% - 120rpx);
height: 100%;
}
.usrinfo .value .uni-input-input {
color: rgba(255, 255, 255, 0.87);
font-family: "PingFang SC";
font-size: 28rpx;
font-weight: 400;
letter-spacing: 0.07px;
text-align: left;
}
.usrplace {
color: rgba(255, 255, 255, 0.4);
font-family: "PingFang SC";
font-size: 28rpx;
font-weight: 400;
letter-spacing: 0.07px;
text-align: left;
}
.proinfo .itemcontent {
display: flex;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
align-content: center;
justify-content: space-evenly;
}
.proinfo .item {
width: 180rpx;
height: 180rpx;
box-sizing: border-box;
padding: 40rpx;
border-radius: 16rpx;
background: rgba(26, 26, 26, 1);
display: flex;
flex-direction: column;
flex-wrap: nowrap;
align-content: center;
justify-content: center;
align-items: center;
}
.proinfo .item .img {
width: 50rpx;
height: 50rpx;
}
.proinfo .item .txt {
color: rgba(255, 255, 255, 0.6);
font-family: "PingFang SC";
font-size: 24rpx;
font-weight: 400;
margin-top: 20rpx;
letter-spacing: 0.07px;
text-align: left;
}
.slider-container {
padding: 0px;
}
.addIco {
width: 100%;
height: 360rpx;
display: flex;
flex-wrap: nowrap;
flex-direction: row;
align-content: center;
justify-content: center;
align-items: center;
}
.icoContent {
width: 240rpx;
height: 240rpx;
border-radius: 8rpx;
background: rgba(58, 58, 58, 1);
}
.icoContent .img {
width: 70rpx;
height: 70rpx;
}
.TextToHex {
position: fixed;
top: -99999rpx;
left: -99999rpx;
visibility: hidden;
}
</style>