1183 lines
27 KiB
Vue
1183 lines
27 KiB
Vue
<template>
|
||
<view class="bluetooth-container">
|
||
<view class="material-card" style="border-top: 1px solid #e0e0e2">
|
||
<view class="status-section">
|
||
<view class="text">
|
||
<p>
|
||
{{
|
||
currentDevice ? currentDevice.deviceId : "打印机连接失败,请重试"
|
||
}}
|
||
</p>
|
||
</view>
|
||
<uv-button
|
||
type="primary"
|
||
:plain="true"
|
||
@tap="toggleBluetooth"
|
||
text="切换打印机"
|
||
></uv-button>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="formContent">
|
||
<uv-form
|
||
labelPosition="left"
|
||
:model="formModel"
|
||
labelWidth="130"
|
||
:rules="rules"
|
||
ref="formRef"
|
||
>
|
||
<uv-form-item
|
||
label="打印信息"
|
||
borderBottom
|
||
class="title"
|
||
></uv-form-item>
|
||
<uv-form-item label="废物名称" prop="username" borderBottom>
|
||
<uv-input
|
||
v-model="formModel.username"
|
||
border="none"
|
||
placeholderStyle="text-align: right;"
|
||
placeholder="请输入"
|
||
/>
|
||
</uv-form-item>
|
||
<uv-form-item label="废物类别" prop="username1" borderBottom>
|
||
<uv-input
|
||
v-model="formModel.username1"
|
||
border="none"
|
||
placeholderStyle="text-align: right;"
|
||
placeholder="请输入"
|
||
/>
|
||
</uv-form-item>
|
||
<uv-form-item label="废物代码" prop="username2" borderBottom>
|
||
<uv-input
|
||
v-model="formModel.username2"
|
||
border="none"
|
||
placeholderStyle="text-align: right;"
|
||
placeholder="请输入"
|
||
/>
|
||
</uv-form-item>
|
||
<uv-form-item label="废物形态" prop="username3" borderBottom>
|
||
<uv-input
|
||
v-model="formModel.username3"
|
||
border="none"
|
||
placeholderStyle="text-align: right;"
|
||
placeholder="请输入"
|
||
/>
|
||
</uv-form-item>
|
||
<uv-form-item label="主要成分" prop="username4" borderBottom>
|
||
<uv-input
|
||
v-model="formModel.username4"
|
||
border="none"
|
||
placeholderStyle="text-align: right;"
|
||
placeholder="请输入"
|
||
/>
|
||
</uv-form-item>
|
||
<uv-form-item label="有害成分" prop="username5" borderBottom>
|
||
<uv-input
|
||
v-model="formModel.username5"
|
||
border="none"
|
||
placeholderStyle="text-align: right;"
|
||
placeholder="请输入"
|
||
/>
|
||
</uv-form-item>
|
||
<uv-form-item label="注意事项" prop="username6" borderBottom>
|
||
<uv-input
|
||
v-model="formModel.username6"
|
||
border="none"
|
||
placeholderStyle="text-align: right;"
|
||
placeholder="请输入"
|
||
/>
|
||
</uv-form-item>
|
||
<uv-form-item label="数字识别码" prop="username7" borderBottom>
|
||
<uv-input
|
||
v-model="formModel.username7"
|
||
border="none"
|
||
placeholderStyle="text-align: right;"
|
||
placeholder="请输入"
|
||
/>
|
||
</uv-form-item>
|
||
<uv-form-item label="产生/收集单位" prop="username8" borderBottom>
|
||
<uv-input
|
||
v-model="formModel.username8"
|
||
border="none"
|
||
placeholderStyle="text-align: right;"
|
||
placeholder="请输入"
|
||
/>
|
||
</uv-form-item>
|
||
<uv-form-item label="联系人和联系方式" prop="username9" borderBottom>
|
||
<uv-input
|
||
v-model="formModel.username9"
|
||
border="none"
|
||
placeholderStyle="text-align: right;"
|
||
placeholder="请输入"
|
||
/>
|
||
</uv-form-item>
|
||
<uv-form-item label="产生日期" prop="username10" borderBottom>
|
||
<uv-input
|
||
v-model="formModel.username10"
|
||
border="none"
|
||
placeholderStyle="text-align: right;"
|
||
placeholder="请输入"
|
||
/>
|
||
</uv-form-item>
|
||
<uv-form-item label="废物重量" prop="username11" borderBottom>
|
||
<uv-input
|
||
v-model="formModel.username11"
|
||
border="none"
|
||
placeholderStyle="text-align: right;"
|
||
placeholder="请输入"
|
||
/>
|
||
</uv-form-item>
|
||
<uv-form-item label="备注" prop="username12" borderBottom>
|
||
<uv-input
|
||
v-model="formModel.username12"
|
||
border="none"
|
||
placeholderStyle="text-align: right;"
|
||
placeholder="请输入"
|
||
/>
|
||
</uv-form-item>
|
||
</uv-form>
|
||
</view>
|
||
|
||
<!-- 设备列表 -->
|
||
<uv-modal
|
||
ref="modalBluetooth"
|
||
title="设备列表"
|
||
:closeOnClickOverlay="false"
|
||
:showConfirmButton="false"
|
||
>
|
||
<view class="devices-section" v-show="scanning">
|
||
<scroll-view class="devices-list" scroll-y>
|
||
<view
|
||
v-for="device in devices"
|
||
:key="device.deviceId + device.type"
|
||
class="device-item"
|
||
@click="connectDevice(device)"
|
||
>
|
||
<text class="device-name">
|
||
{{ device.name }}
|
||
<text class="device-type">{{
|
||
device.type ? showDeviceType(device.type) : ""
|
||
}}</text>
|
||
</text>
|
||
<text class="device-address">{{ device.deviceId }}</text>
|
||
</view>
|
||
</scroll-view>
|
||
</view>
|
||
</uv-modal>
|
||
|
||
<!-- canvas图片 -->
|
||
<view class="image-canvas">
|
||
<canvas
|
||
canvas-id="print_yd_1"
|
||
:style="{ width: canvasW + 'px', height: canvasH + 'px' }"
|
||
id="print_yd_1"
|
||
></canvas>
|
||
<canvas
|
||
canvas-id="print_yd_2"
|
||
:style="{ width: canvasW + 'px', height: canvasH + 'px' }"
|
||
id="print_yd_2"
|
||
></canvas>
|
||
</view>
|
||
|
||
<button type="primary" style="margin-top: 20rpx" @click="printFun()">
|
||
打印
|
||
</button>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref } from "vue";
|
||
import { onLoad, onUnload } from "@dcloudio/uni-app";
|
||
|
||
import tsc from "/static/js/tsc.js";
|
||
import classicBluetooth from "/static/js/bluetoothTool.js";
|
||
|
||
const formModel = ref({
|
||
username: "废铅蓄电池",
|
||
username1: "HW31",
|
||
username2: "900-052-31",
|
||
username3: "固态",
|
||
username4: "含铅物质、酸性物质",
|
||
username5: "废铅板·废铅膏、酸液",
|
||
username6: "防火",
|
||
username7: "91130200757512841X9000523120240911002",
|
||
username8: "配网管理管理中心",
|
||
username9: "张玉1510660962",
|
||
username10: "20250120",
|
||
username11: "4212kg",
|
||
username12: "810只",
|
||
});
|
||
|
||
const formRef = ref();
|
||
const rules = {
|
||
username1: {
|
||
type: "string",
|
||
required: true,
|
||
message: "输入废物名称",
|
||
trigger: ["blur", "change"],
|
||
},
|
||
};
|
||
|
||
const modalBluetooth = ref(null);
|
||
|
||
const canvasW = ref(184);
|
||
const canvasH = ref(180.5);
|
||
const getImage = (path, canvasId) => {
|
||
return new Promise((resolve, reject) => {
|
||
uni.getImageInfo({
|
||
src: plus.io.convertLocalFileSystemURL(path),
|
||
success: (res) => {
|
||
const image = {
|
||
width: res.width / 2,
|
||
height: res.height / 2,
|
||
}
|
||
canvasH.value = image.height;
|
||
const mw = image.width % 8;
|
||
canvasW.value = mw === 0 ? image.width : image.width + 8 - mw;
|
||
canvasH.value = image.height / image.width * canvasW.value;
|
||
const ctx = uni.createCanvasContext(canvasId);
|
||
ctx.setFillStyle("white");
|
||
ctx.fillRect(0, 0, canvasW.value, canvasH.value);
|
||
ctx.drawImage(res.path, 0, 0, canvasW.value, canvasH.value);
|
||
ctx.draw(false, () => {
|
||
uni.canvasGetImageData({
|
||
canvasId: canvasId,
|
||
x: 0,
|
||
y: 0,
|
||
width: canvasW.value,
|
||
height: canvasH.value,
|
||
success(data) {
|
||
resolve(data);
|
||
},
|
||
});
|
||
});
|
||
},
|
||
});
|
||
});
|
||
};
|
||
|
||
const drawEmptyCanvas = (canvasId) => {
|
||
// 直接创建 canvas 上下文,不传递第二个参数
|
||
const ctx = uni.createCanvasContext(canvasId);
|
||
ctx.setFillStyle('white');
|
||
ctx.fillRect(0, 0, canvasW.value, canvasH.value);
|
||
ctx.draw();
|
||
};
|
||
|
||
const printFun = async () => {
|
||
uni.showLoading({
|
||
title: "开始打印",
|
||
mask: true,
|
||
});
|
||
const print_yd_1 = await getImage(
|
||
"/static/print/print_yd_1.png",
|
||
"print_yd_1"
|
||
);
|
||
|
||
const print_yd_2 = await getImage(
|
||
"/static/print/print_yd_2.png",
|
||
"print_yd_2"
|
||
);
|
||
if (!bluetoothEnabled.value) {
|
||
uni.showToast({
|
||
title: "请先开启蓝牙",
|
||
icon: "none",
|
||
});
|
||
return;
|
||
}
|
||
|
||
let command = tsc.createNew();
|
||
command.setSize(100, 100);
|
||
command.setGapMM(2, 0);
|
||
command.setCls();
|
||
|
||
let height = 72;
|
||
let lineHeight = 20 + 50;
|
||
let border = 50 - 6;
|
||
let titleHeight = 10;
|
||
let width = 1130;
|
||
let col1Width = 660;
|
||
let col2Width = 815;
|
||
|
||
command.setBox(50, 50, width, width + 6, 6);
|
||
command.setBox(50, 50, width, height + 50 + titleHeight, 6);
|
||
command.setText(460, lineHeight, "TSS24.BF2", 2, 2, "危 险 废 物");
|
||
|
||
command.setBox(
|
||
50,
|
||
height + border + titleHeight,
|
||
col1Width,
|
||
height * 2 + 50 + titleHeight,
|
||
6
|
||
);
|
||
command.setText(
|
||
70,
|
||
height + lineHeight + titleHeight,
|
||
"TSS24.BF2",
|
||
1,
|
||
1,
|
||
"废物名称:" + formModel.value.username
|
||
);
|
||
|
||
command.setBox(
|
||
50,
|
||
height * 2 + border + titleHeight,
|
||
col1Width,
|
||
height * 3 + 50 + titleHeight,
|
||
6
|
||
);
|
||
command.setText(
|
||
70,
|
||
height * 2 + lineHeight + titleHeight,
|
||
"TSS24.BF2",
|
||
1,
|
||
1,
|
||
"废物类别:" + formModel.value.username1
|
||
);
|
||
|
||
command.setBox(
|
||
50,
|
||
height * 3 + border + titleHeight,
|
||
col1Width,
|
||
height * 4 + 50 + titleHeight,
|
||
6
|
||
);
|
||
command.setText(
|
||
70,
|
||
height * 3 + lineHeight + titleHeight,
|
||
"TSS24.BF2",
|
||
1,
|
||
1,
|
||
"废物代码:" +
|
||
formModel.value.username2 +
|
||
" 废物形态:" +
|
||
formModel.value.username3
|
||
);
|
||
|
||
command.setBox(
|
||
50,
|
||
height * 4 + border + titleHeight,
|
||
col1Width,
|
||
height * 6 + 50 + titleHeight,
|
||
6
|
||
);
|
||
command.setText(
|
||
70,
|
||
height * 4 + lineHeight + titleHeight,
|
||
"TSS24.BF2",
|
||
1,
|
||
1,
|
||
"主要成分:" + formModel.value.username4
|
||
);
|
||
|
||
command.setBox(
|
||
50,
|
||
height * 6 + border + titleHeight,
|
||
col1Width,
|
||
height * 8 + 50 + titleHeight,
|
||
6
|
||
);
|
||
command.setText(
|
||
70,
|
||
height * 6 + lineHeight + titleHeight,
|
||
"TSS24.BF2",
|
||
1,
|
||
1,
|
||
"有害成分:" + formModel.value.username5
|
||
);
|
||
|
||
command.setBox(
|
||
col1Width - 6,
|
||
height + border + titleHeight,
|
||
width,
|
||
height * 8 + 50 + titleHeight,
|
||
6
|
||
);
|
||
command.setText(
|
||
col1Width + 170,
|
||
height + lineHeight + titleHeight,
|
||
"TSS24.BF2",
|
||
1,
|
||
1,
|
||
"危 险 特 性"
|
||
);
|
||
|
||
command.setBitmap(
|
||
col1Width + 36,
|
||
height * 3 + lineHeight,
|
||
canvasW.value,
|
||
canvasH.value,
|
||
0,
|
||
print_yd_1
|
||
);
|
||
|
||
command.setBitmap(
|
||
col1Width + 184 + 72,
|
||
height * 3 + lineHeight,
|
||
canvasW.value,
|
||
canvasH.value,
|
||
0,
|
||
print_yd_2
|
||
);
|
||
|
||
command.setBox(
|
||
50,
|
||
height * 8 + border + titleHeight,
|
||
width,
|
||
height * 10 + 50 + titleHeight,
|
||
6
|
||
);
|
||
command.setText(
|
||
70,
|
||
height * 8 + lineHeight + titleHeight,
|
||
"TSS24.BF2",
|
||
1,
|
||
1,
|
||
"注意事项:" + formModel.value.username6
|
||
);
|
||
|
||
command.setBox(
|
||
50,
|
||
height * 10 + border + titleHeight,
|
||
width,
|
||
height * 11 + 50 + titleHeight,
|
||
6
|
||
);
|
||
command.setText(
|
||
70,
|
||
height * 10 + lineHeight + titleHeight,
|
||
"TSS24.BF2",
|
||
1,
|
||
1,
|
||
"数字识别码:" + formModel.value.username7
|
||
);
|
||
|
||
command.setBox(
|
||
50,
|
||
height * 11 + border + titleHeight,
|
||
col2Width,
|
||
height * 12 + 50 + titleHeight,
|
||
6
|
||
);
|
||
command.setText(
|
||
70,
|
||
height * 11 + lineHeight + titleHeight,
|
||
"TSS24.BF2",
|
||
1,
|
||
1,
|
||
"产生/收集单位:" + formModel.value.username8
|
||
);
|
||
|
||
command.setBox(
|
||
50,
|
||
height * 12 + border + titleHeight,
|
||
col2Width,
|
||
height * 13 + 50 + titleHeight,
|
||
6
|
||
);
|
||
command.setText(
|
||
70,
|
||
height * 12 + lineHeight + titleHeight,
|
||
"TSS24.BF2",
|
||
1,
|
||
1,
|
||
"联系人和联系方式:" + formModel.value.username9
|
||
);
|
||
|
||
command.setBox(
|
||
50,
|
||
height * 13 + border + titleHeight,
|
||
(col2Width - 50) / 2 + 50,
|
||
height * 14 + 50 + titleHeight,
|
||
6
|
||
);
|
||
command.setText(
|
||
70,
|
||
height * 13 + lineHeight + titleHeight,
|
||
"TSS24.BF2",
|
||
1,
|
||
1,
|
||
"产生日期:" + formModel.value.username10
|
||
);
|
||
|
||
command.setBox(
|
||
50 + (col2Width - 50) / 2 + 50,
|
||
height * 13 + border + titleHeight,
|
||
(col2Width - 50) / 2 + 50,
|
||
height * 14 + 50 + titleHeight,
|
||
6
|
||
);
|
||
command.setText(
|
||
20 + (col2Width - 50) / 2 + 50,
|
||
height * 13 + lineHeight + titleHeight,
|
||
"TSS24.BF2",
|
||
1,
|
||
1,
|
||
"废物重量:" + formModel.value.username11
|
||
);
|
||
|
||
command.setBox(
|
||
50,
|
||
height * 14 + border + titleHeight,
|
||
col2Width,
|
||
height * 15 + 50 + titleHeight,
|
||
6
|
||
);
|
||
command.setText(
|
||
70,
|
||
height * 14 + lineHeight + titleHeight,
|
||
"TSS24.BF2",
|
||
1,
|
||
1,
|
||
"备注:" + formModel.value.username12
|
||
);
|
||
|
||
command.setBox(
|
||
col2Width - 6,
|
||
height * 11 + border + titleHeight,
|
||
width,
|
||
height * 15 + 50 + titleHeight,
|
||
6
|
||
);
|
||
command.setQR(
|
||
col2Width + 46,
|
||
height * 11 + lineHeight + titleHeight + 10,
|
||
"L",
|
||
10,
|
||
"A",
|
||
"123"
|
||
);
|
||
command.setDirection(1);
|
||
command.setSpeed(3);
|
||
command.setDensity(10);
|
||
command.setPagePrint(1);
|
||
sendData.value = command.getData();
|
||
sendDataToDevice();
|
||
drawEmptyCanvas('print_yd_1');
|
||
drawEmptyCanvas('print_yd_2');
|
||
};
|
||
|
||
// 响应式数据定义
|
||
const bluetoothEnabled = ref(false);
|
||
const connected = ref(false);
|
||
const scanning = ref(false);
|
||
const processing = ref(false);
|
||
const devices = ref([]);
|
||
const currentDevice = ref(null);
|
||
const sendData = ref(null);
|
||
const isBleDevice = ref(false);
|
||
const bleMTU = ref(20);
|
||
const progress = ref("");
|
||
|
||
// ========== 核心方法定义 ==========
|
||
|
||
// 显示MTU设置弹窗
|
||
const showMTUSetting = () => {
|
||
uni.showModal({
|
||
title: "设置MTU值",
|
||
editable: true,
|
||
placeholderText: "请输入MTU值(20-220)",
|
||
content: bleMTU.value.toString(),
|
||
success: (res) => {
|
||
if (res.confirm && res.content) {
|
||
setBLEMTU(res.content);
|
||
}
|
||
},
|
||
});
|
||
};
|
||
|
||
// 设置BLE MTU值
|
||
const setBLEMTU = (value) => {
|
||
const mtuValue = parseInt(value);
|
||
|
||
if (isNaN(mtuValue) || mtuValue < 22 || mtuValue > 512) {
|
||
uni.showToast({
|
||
title: "MTU值必须在22-512之间",
|
||
icon: "none",
|
||
duration: 2000,
|
||
});
|
||
return;
|
||
}
|
||
|
||
if (!connected.value || !isBleDevice.value || !currentDevice.value) {
|
||
uni.showToast({
|
||
title: "请先连接低功耗蓝牙设备",
|
||
icon: "none",
|
||
duration: 2000,
|
||
});
|
||
return;
|
||
}
|
||
|
||
// #ifdef APP-PLUS
|
||
uni.setBLEMTU({
|
||
deviceId: currentDevice.value.deviceId,
|
||
mtu: mtuValue,
|
||
success: (res) => {
|
||
bleMTU.value = mtuValue;
|
||
uni.showToast({
|
||
title: "MTU设置成功",
|
||
duration: 2000,
|
||
});
|
||
},
|
||
fail: (err) => {
|
||
uni.showToast({
|
||
title: `MTU设置失败: ${err.errMsg}`,
|
||
icon: "none",
|
||
duration: 2000,
|
||
});
|
||
},
|
||
});
|
||
// #endif
|
||
|
||
// #ifndef APP-PLUS
|
||
uni.showToast({
|
||
title: "当前平台不支持设置MTU",
|
||
icon: "none",
|
||
duration: 2000,
|
||
});
|
||
// #endif
|
||
};
|
||
|
||
// 初始化蓝牙
|
||
const initBluetooth = () => {
|
||
initBleBluetooth();
|
||
initClassicBluetooth();
|
||
};
|
||
|
||
// 初始化经典蓝牙
|
||
const initClassicBluetooth = () => {
|
||
classicBluetooth.init({
|
||
listenBTStatusCallback: (state) => {
|
||
bluetoothEnabled.value = classicBluetooth.state.bluetoothEnable;
|
||
if (state == "STATE_ON") {
|
||
let lastBleDevice = uni.getStorageSync("lastBleDevice");
|
||
if (lastBleDevice) {
|
||
currentDevice.value = JSON.parse(lastBleDevice);
|
||
connectDevice(currentDevice.value);
|
||
}
|
||
getPairedDevices();
|
||
}
|
||
},
|
||
discoveryDeviceCallback: (newDevice) => {
|
||
if (
|
||
newDevice != null &&
|
||
newDevice.name != "" &&
|
||
newDevice.name != "未知设备"
|
||
) {
|
||
const device = {
|
||
...newDevice,
|
||
};
|
||
addDevice(device);
|
||
}
|
||
},
|
||
discoveryFinishedCallback: () => {
|
||
scanning.value = false;
|
||
// modalBluetooth.value.close()
|
||
},
|
||
readDataCallback: (dataByteArr) => {
|
||
const data = bytesToString(dataByteArr);
|
||
},
|
||
connExceptionCallback: (e) => {
|
||
connected.value = false;
|
||
},
|
||
});
|
||
};
|
||
|
||
// 初始化低功耗蓝牙
|
||
const initBleBluetooth = () => {
|
||
openBleAdapter();
|
||
};
|
||
|
||
// 切换蓝牙开关
|
||
const toggleBluetooth = () => {
|
||
processing.value = true;
|
||
|
||
// console.log(classicBluetooth.listenBluetoothStatus())
|
||
classicBluetooth.turnOnBluetooth();
|
||
|
||
startScan();
|
||
setTimeout(() => {
|
||
processing.value = false;
|
||
}, 1000);
|
||
};
|
||
|
||
// 开启低功耗蓝牙适配器
|
||
const openBleAdapter = () => {
|
||
// #ifdef APP-PLUS
|
||
uni.openBluetoothAdapter({
|
||
success: (res) => {
|
||
bluetoothEnabled.value = true;
|
||
},
|
||
fail: (err) => {},
|
||
});
|
||
// #endif
|
||
};
|
||
|
||
// 开始扫描设备
|
||
const startScan = () => {
|
||
devices.value = [];
|
||
scanning.value = true;
|
||
modalBluetooth.value.open();
|
||
classicBluetooth.discoveryNewDevice();
|
||
getPairedDevices();
|
||
startBleScan();
|
||
};
|
||
|
||
// 停止扫描
|
||
const stopScan = () => {
|
||
scanning.value = false;
|
||
if (modalBluetooth.value) {
|
||
modalBluetooth.value.close();
|
||
}
|
||
classicBluetooth.cancelDiscovery();
|
||
stopBleScan();
|
||
};
|
||
|
||
// 获取已配对设备
|
||
const getPairedDevices = () => {
|
||
try {
|
||
const pairedDevices = classicBluetooth.getPairedDevices();
|
||
console.log(">>> getPairedDevices", pairedDevices);
|
||
|
||
pairedDevices.forEach((device) => {
|
||
const pairedDevice = {
|
||
...device,
|
||
};
|
||
addDevice(pairedDevice);
|
||
});
|
||
} catch (error) {}
|
||
};
|
||
|
||
// 开始低功耗蓝牙扫描
|
||
const startBleScan = () => {
|
||
// #ifdef APP-PLUS
|
||
uni.startBluetoothDevicesDiscovery({
|
||
services: [],
|
||
allowDuplicatesKey: false,
|
||
success: (res) => {
|
||
uni.onBluetoothDeviceFound((res) => {
|
||
res.devices.forEach((device) => {
|
||
if (
|
||
device != null &&
|
||
device.name != "" &&
|
||
device.name != "未知设备"
|
||
) {
|
||
const newDevice = {
|
||
...device,
|
||
type: "ble",
|
||
};
|
||
addDevice(newDevice);
|
||
}
|
||
});
|
||
});
|
||
},
|
||
fail: (err) => {},
|
||
});
|
||
// #endif
|
||
};
|
||
|
||
// 停止低功耗蓝牙扫描
|
||
const stopBleScan = () => {
|
||
// #ifdef APP-PLUS
|
||
uni.stopBluetoothDevicesDiscovery({
|
||
success: (res) => {},
|
||
});
|
||
// #endif
|
||
};
|
||
|
||
// 生成设备唯一key
|
||
const generateDeviceKey = (device) => {
|
||
return `${device.deviceId}-${device.type || "unknown"}`;
|
||
};
|
||
|
||
// 显示设备类型
|
||
const showDeviceType = (type) => {
|
||
switch (type) {
|
||
case "ble":
|
||
return "低功耗蓝牙";
|
||
break;
|
||
case "classic":
|
||
return "经典蓝牙";
|
||
break;
|
||
case "dual":
|
||
return "双模";
|
||
break;
|
||
default:
|
||
return "未知";
|
||
}
|
||
};
|
||
|
||
// 设备类型判断
|
||
const getDeviceConnectType = (device) => {
|
||
console.log(">>> getDeviceConnectType", device);
|
||
const { type, name, deviceId } = device;
|
||
if (type.toLowerCase() !== "dual") {
|
||
return type;
|
||
}
|
||
if (name.toUpperCase().includes("BLE")) {
|
||
return "ble";
|
||
}
|
||
if (deviceId) {
|
||
const mac = deviceId.replace(/:/g, "").toUpperCase();
|
||
if (mac.length === 12) {
|
||
const secondChar = mac[1];
|
||
if (["2", "6", "A", "E"].includes(secondChar)) {
|
||
return "ble";
|
||
}
|
||
}
|
||
}
|
||
return "classic";
|
||
};
|
||
|
||
// 添加设备到列表(去重)
|
||
const addDevice = (newDevice) => {
|
||
const deviceKey = generateDeviceKey(newDevice);
|
||
|
||
const existingIndex = devices.value.findIndex(
|
||
(device) => generateDeviceKey(device) === deviceKey
|
||
);
|
||
|
||
if (existingIndex === -1) {
|
||
devices.value.push(newDevice);
|
||
} else {
|
||
const existingDevice = devices.value[existingIndex];
|
||
devices.value[existingIndex] = {
|
||
...existingDevice,
|
||
...newDevice,
|
||
};
|
||
}
|
||
};
|
||
|
||
// 连接设备
|
||
const connectDevice = (device) => {
|
||
processing.value = true;
|
||
console.log("connectDevice", device);
|
||
uni.showLoading({
|
||
title: "正在连接...",
|
||
});
|
||
const connectType = getDeviceConnectType(device);
|
||
if (connectType === "ble") {
|
||
connectBleDevice(device);
|
||
} else {
|
||
connectClassicDevice(device);
|
||
}
|
||
stopScan();
|
||
uni.hideLoading();
|
||
};
|
||
|
||
// 连接经典蓝牙设备
|
||
const connectClassicDevice = (device) => {
|
||
isBleDevice.value = false;
|
||
classicBluetooth.connDevice(device.deviceId, (success) => {
|
||
processing.value = false;
|
||
if (success) {
|
||
connected.value = true;
|
||
currentDevice.value = device;
|
||
uni.setStorageSync("lastBleDevice", JSON.stringify(device));
|
||
stopScan();
|
||
} else {
|
||
}
|
||
});
|
||
};
|
||
|
||
// 连接低功耗蓝牙设备
|
||
const connectBleDevice = (device) => {
|
||
isBleDevice.value = true;
|
||
// #ifdef APP-PLUS
|
||
uni.createBLEConnection({
|
||
deviceId: device.deviceId,
|
||
success: (res) => {
|
||
connected.value = true;
|
||
currentDevice.value = device;
|
||
processing.value = false;
|
||
|
||
getBLEDeviceServices(device.deviceId);
|
||
},
|
||
fail: (err) => {
|
||
processing.value = false;
|
||
},
|
||
});
|
||
// #endif
|
||
};
|
||
|
||
// 获取低功耗蓝牙设备服务
|
||
const getBLEDeviceServices = (deviceId) => {
|
||
// #ifdef APP-PLUS
|
||
uni.getBLEDeviceServices({
|
||
deviceId: deviceId,
|
||
success: (res) => {
|
||
for (var s = 0; s < res.services.length; s++) {
|
||
getBLEDeviceCharacteristics(deviceId, res.services[s].uuid);
|
||
}
|
||
},
|
||
fail: (err) => {},
|
||
});
|
||
// #endif
|
||
};
|
||
|
||
// 获取低功耗蓝牙设备特征值
|
||
const getBLEDeviceCharacteristics = (deviceId, serviceId) => {
|
||
// #ifdef APP-PLUS
|
||
uni.getBLEDeviceCharacteristics({
|
||
deviceId: deviceId,
|
||
serviceId: serviceId,
|
||
success: (res) => {
|
||
var re = JSON.parse(JSON.stringify(res));
|
||
for (var c = 0; c < re.characteristics.length; c++) {
|
||
if (re.characteristics[c].properties.write == true) {
|
||
let uuid = re.characteristics[c].uuid;
|
||
for (var index in devices.value) {
|
||
if (devices.value[index].deviceId == deviceId) {
|
||
devices.value[index].services = [];
|
||
currentDevice.value.services = [];
|
||
devices.value[index].services.push({
|
||
serviceId: serviceId,
|
||
characteristicId: uuid,
|
||
});
|
||
currentDevice.value.services.push({
|
||
serviceId: serviceId,
|
||
characteristicId: uuid,
|
||
});
|
||
uni.setStorageSync(
|
||
"lastBleDevice",
|
||
JSON.stringify(currentDevice.value)
|
||
);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
fail: (err) => {},
|
||
});
|
||
// #endif
|
||
};
|
||
|
||
// 断开设备连接
|
||
const disconnectDevice = () => {
|
||
if (connected.value && currentDevice.value) {
|
||
console.log(">>> disconnectDevice.currentDevice", currentDevice.value);
|
||
if (isBleDevice.value) {
|
||
closeBleConnection();
|
||
} else {
|
||
classicBluetooth.disConnDevice();
|
||
}
|
||
connected.value = false;
|
||
currentDevice.value = null;
|
||
}
|
||
};
|
||
|
||
// 关闭低功耗蓝牙连接
|
||
const closeBleConnection = () => {
|
||
// #ifdef APP-PLUS
|
||
if (currentDevice.value && isBleDevice.value) {
|
||
uni.closeBLEConnection({
|
||
deviceId: currentDevice.value.deviceId,
|
||
});
|
||
}
|
||
// #endif
|
||
};
|
||
|
||
// 发送数据到设备
|
||
const sendDataToDevice = () => {
|
||
console.log(">>> sendData.value", sendData.value);
|
||
if (!sendData.value) {
|
||
uni.showToast({
|
||
title: "没有打印数据",
|
||
duration: 1500,
|
||
});
|
||
return;
|
||
}
|
||
console.log(">>> sendDataToDevice.sendData", isBleDevice.value);
|
||
if (isBleDevice.value) {
|
||
sendBleData(sendData.value);
|
||
} else {
|
||
sendClassicData(sendData.value);
|
||
}
|
||
sendData.value = null;
|
||
};
|
||
|
||
// 发送数据到经典蓝牙设备
|
||
const sendClassicData = (data) => {
|
||
let sendDataArr = [];
|
||
//#ifdef APP-PLUS
|
||
sendDataArr = data.map((byte) => {
|
||
let b = byte & 0xff;
|
||
if (b >= 128) {
|
||
return (b % 128) - 128;
|
||
}
|
||
return b;
|
||
});
|
||
//#endif
|
||
//#ifdef MP-WEIXIN
|
||
sendDataArr = data;
|
||
//#endif
|
||
console.log("sendDataArr", sendDataArr);
|
||
classicBluetooth.sendByteData(sendDataArr);
|
||
console.log(">>> sendClassicData --- 发送数据成功");
|
||
uni.hideLoading();
|
||
};
|
||
|
||
// 发送数据到低功耗蓝牙设备
|
||
const sendBleData = (data) => {
|
||
console.log(">>> sendBleData.currentDevice", currentDevice.value);
|
||
if (!currentDevice.value) {
|
||
uni.showToast({
|
||
title: "请连接打印机",
|
||
});
|
||
return;
|
||
}
|
||
|
||
let deviceId = currentDevice.value.deviceId;
|
||
let serviceId = currentDevice.value.services[0].serviceId;
|
||
let characteristicId = currentDevice.value.services[0].characteristicId;
|
||
let uint8Array = sendData.value;
|
||
|
||
uni.showToast({
|
||
title: "发送打印数据成功",
|
||
duration: 1500,
|
||
});
|
||
let uint8Buf = Array.from(uint8Array);
|
||
|
||
function split_array1(datas, size) {
|
||
let result = [];
|
||
for (let i = 0; i < datas.length; i += size) {
|
||
result.push(datas.slice(i, i + size));
|
||
}
|
||
console.log(">>> sendBleData.split_array1 ", result);
|
||
return result;
|
||
}
|
||
|
||
let sendloop = split_array1(uint8Buf, bleMTU.value);
|
||
|
||
function realWriteData(sendloop, i) {
|
||
let data = sendloop[i];
|
||
if (typeof data == "undefined") {
|
||
uni.hideLoading();
|
||
uni.showToast({
|
||
title: "打印完成",
|
||
});
|
||
progress.value = "";
|
||
return;
|
||
}
|
||
|
||
progress.value = ":" + Math.round((i / sendloop.length) * 100) + "%";
|
||
|
||
console.log("第" + i + "次写数据:" + data.length);
|
||
let buffer = new ArrayBuffer(data.length);
|
||
let dataView = new DataView(buffer);
|
||
for (let j = 0; j < data.length; j++) {
|
||
dataView.setUint8(j, data[j]);
|
||
}
|
||
uni.writeBLECharacteristicValue({
|
||
deviceId,
|
||
serviceId,
|
||
characteristicId,
|
||
value: buffer,
|
||
success(res) {
|
||
setTimeout(() => {
|
||
realWriteData(sendloop, i + 1);
|
||
}, 10);
|
||
},
|
||
fail(res) {
|
||
console.log(">>> writeBLECharacteristicValue --- 失败", res);
|
||
uni.hideLoading();
|
||
uni.showToast({
|
||
title: "下发失败",
|
||
});
|
||
progress.value = "";
|
||
},
|
||
});
|
||
}
|
||
let i = 0;
|
||
realWriteData(sendloop, i);
|
||
};
|
||
|
||
// 工具方法:字节数组转字符串
|
||
const bytesToString = (byteArray) => {
|
||
return String.fromCharCode.apply(null, byteArray);
|
||
};
|
||
|
||
// 生命周期钩子
|
||
onLoad(() => {
|
||
initBluetooth();
|
||
});
|
||
|
||
onUnload(() => {
|
||
disconnectDevice();
|
||
});
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
::v-deep page,
|
||
::v-deep .uni-page-wrapper {
|
||
margin-top: 0 !important;
|
||
}
|
||
.bluetooth-container {
|
||
min-height: 100vh;
|
||
position: relative;
|
||
.status-section {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 20rpx;
|
||
::v-deep .uv-button {
|
||
height: 60rpx !important;
|
||
}
|
||
}
|
||
.formContent {
|
||
margin-top: 20rpx;
|
||
background: #ffffff;
|
||
.uv-form-item {
|
||
padding: 0 20rpx;
|
||
::v-deep .uni-input-input {
|
||
text-align: right !important;
|
||
}
|
||
}
|
||
.title {
|
||
::v-deep .uv-form-item__body__left__content__label {
|
||
font-weight: bold !important;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.devices-section {
|
||
width: 100%;
|
||
}
|
||
|
||
.devices-list {
|
||
max-height: 360rpx;
|
||
width: 100%;
|
||
}
|
||
|
||
.device-item {
|
||
padding: 0 20rpx;
|
||
border-bottom: 1rpx solid #eeeeee;
|
||
}
|
||
|
||
.device-item:active {
|
||
background-color: #f0f0f0;
|
||
}
|
||
|
||
.device-name {
|
||
font-weight: bold;
|
||
display: block;
|
||
}
|
||
|
||
.device-address {
|
||
font-size: 24rpx;
|
||
color: #666666;
|
||
display: block;
|
||
}
|
||
|
||
.device-type {
|
||
font-size: 24rpx;
|
||
color: #2196f3;
|
||
}
|
||
.image-canvas {
|
||
position: absolute;
|
||
right: 99999rpx;
|
||
bottom: 99999px;
|
||
}
|
||
</style> |