2026-09-04 08:56:15 +08:00

340 lines
7.6 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="page">
<!-- 消息列表(可上下滑动) -->
<scroll-view class="msgList" scroll-y="true" :scroll-top="scrollTop">
<view v-for="(it, idx) in msgList" :key="idx" class="msgCard" :class="it.dir">
<view class="msgHeader">
<text v-if="it.dir == 'tx'" class="msgTag">{{ 'TX' }}</text>
<text v-else-if="it.dir == 'rx'" class="msgTag">{{ 'RX' }}</text>
<text v-else class="msgTag">{{ '' }}</text>
<text class="msgTime">{{ it.time }}</text>
</view>
<text class="msgBody">{{ it.text }}</text>
</view>
<view style="height: 20px;"></view>
</scroll-view>
<!-- 底部:命令输入 + 按钮 -->
<view class="bottomPanel">
<view class="cmdRow">
<input class="cmdInput" v-model="cmdText" placeholder="Enter CLI command, e.g. help / sysinfo" :disabled="!bleIsConnect" />
<button type="primary" size="mini" class="sendBtn" :disabled="!bleIsConnect || sending" @tap="onSendCmd">{{ sending ? '...' : 'send' }}</button>
</view>
<view class="btnRow">
<button
type="warn"
@click="onConnect"
:disabled="bleIsConnect"
class="btnType"
>connect</button>
<button
type="warn"
@click="onDisConnect"
:disabled="!bleIsConnect"
class="btnType"
>disconnect</button>
<button
type="primary"
@click="onGetStatus"
:disabled="!bleIsConnect"
class="btnType"
>rssi</button>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad, onShow, onHide, onUnload } from '@dcloudio/uni-app'
import { MyApiResult } from "../../../src/BluetoothManager"
import * as Ble from "../../../src/BluetoothManager"
import * as McmChannel from "../../../src/mcm/McmChannel"
import * as McmCmd from "../../../src/mcm/McmCommands"
import { CliResult } from "../../../src/mcm/McmChannel"
import * as BleINfo from "../../../common/bleInfo"
const bleAddress = ref('')
const bleIsConnect = ref(false) // 蓝牙连接标志 true-已连接 false-未连接
const cmdText = ref("")
const sending = ref(false)
type MsgItem = {
dir: string
text: string
time: string
}
const msgList = ref<Array<MsgItem>>([] as Array<MsgItem>)
const scrollTop = ref(0)
// ===========================功能函数
function nowStr(): string {
const d = new Date()
const hh = d.getHours().toString().padStart(2, '0')
const mm = d.getMinutes().toString().padStart(2, '0')
const ss = d.getSeconds().toString().padStart(2, '0')
return `${hh}:${mm}:${ss}`
}
function pushMsg(dir: string, text: string) {
const item: MsgItem = {
dir: dir,
text: text,
time: nowStr()
}
msgList.value.push(item)
scrollTop.value = scrollTop.value + 9999
}
// ==============================蓝牙事件
function onBleDataListener(res: MyApiResult) {
if (res.type == 1000) {
// notify 订阅成功:协商 MTU 247
Ble.setMtu(247, (mtuRes: MyApiResult) => {
if (mtuRes.type == 0) {
McmChannel.setMtuSize(247)
pushMsg("message", "set MTU 247")
}
})
bleIsConnect.value = true
pushMsg("message", "notify subscribed, ready")
uni.hideLoading()
}
}
function onBleStatueListener(state: number) {
if (state == 10000) {
pushMsg("message", "connect failed")
bleIsConnect.value = false
uni.hideLoading()
} else if (state == 10001) {
pushMsg("message", "disconnected")
bleIsConnect.value = false
}
}
// ==============================蓝牙操作
function onDisConnect() {
if (Ble.isConnected()) {
Ble.disConnect()
}
bleIsConnect.value = false
McmChannel.resetChannel()
}
// 连接蓝牙(走全局 ConnectBle:connect → scanServices → 订阅 notify,显式 CAIIC-MCM UUID)
// 本页是 tab 页,地址取最近连接过的设备(bleInfo),没有则提示去 Device 页连接
function onConnect() {
if (Ble.isConnected()) {
uni.hideLoading()
bleIsConnect.value = true
return
}
let addr = bleAddress.value
if (addr.length == 0) {
addr = BleINfo.getMac() ?? ""
}
if (addr.length == 0) {
pushMsg("message", "No device. Please scan and connect a device on the Device tab first")
return
}
bleAddress.value = addr
uni.showLoading({
title: "Connecting...",
mask: true
})
pushMsg("start", "connect: " + addr);
(getApp().globalData as any).ConnectBle(addr, false)
}
// 发送 CLI 命令
async function onSendCmd() {
const cmd = cmdText.value.trim()
if (cmd.length == 0) return
sending.value = true
pushMsg("tx", cmd)
cmdText.value = ""
const r: CliResult = await McmCmd.execCli(cmd, 8000)
sending.value = false
if (r.status == 0) {
pushMsg("rx", r.text.length > 0 ? r.text : "(ok)")
} else if (r.status == 1) {
pushMsg("message", "Unknown command or line too long")
} else {
pushMsg("message", "Command failed, status=" + r.status + (r.text.length > 0 ? (", received: " + r.text) : ""))
}
}
function onGetStatus() {
Ble.readRssi(function(res: MyApiResult) {
pushMsg("message", res.message + ": " + res.data)
})
}
// ======= 生命周期 ========
let subscribed = false
function subscribe() {
if (subscribed) return
uni.$on("bleData", onBleDataListener)
uni.$on("bleState", onBleStatueListener)
subscribed = true
}
function unsubscribe() {
if (!subscribed) return
uni.$off("bleData", onBleDataListener)
uni.$off("bleState", onBleStatueListener)
subscribed = false
}
onLoad((options: any) => {
if (options != null) {
const address = options["address"] as string | null
if (address != null) {
bleAddress.value = address
}
}
subscribe()
// 已连接(如从设备页过来)则直接进入可用状态
if (Ble.isConnected()) {
bleIsConnect.value = true
pushMsg("message", "already connected: " + Ble.getConnectMac())
}
})
onShow(() => {
subscribe()
// 从其他 tab 切回时同步连接状态
if (Ble.isConnected() && !bleIsConnect.value) {
bleIsConnect.value = true
pushMsg("message", "already connected: " + Ble.getConnectMac())
}
})
onHide(() => {
unsubscribe()
})
onUnload(() => {
unsubscribe()
})
</script>
<style scoped>
.page {
flex: 1;
position: relative;
display: flex;
flex-direction: column;
}
/* 底部面板 */
.bottomPanel {
position: absolute;
left: 0;
right: 0;
bottom: 20px;
background-color: #fff;
padding: 0 12px;
box-sizing: border-box;
box-shadow: 0 -6px 18px rgba(0, 0, 0, 0.12);
}
.cmdRow {
display: flex;
flex-direction: row;
align-items: center;
margin-top: 10px;
}
.cmdInput {
flex: 1;
border-width: 1px;
border-style: solid;
border-color: #ddd;
border-radius: 8px;
padding: 6px 10px;
font-size: 14px;
margin-right: 8px;
}
.sendBtn {
min-width: 60px;
}
.btnRow {
display: flex;
flex-direction: row;
margin-top: 10px;
}
.btnType {
flex: 1;
margin-left: 10px;
min-width: 0;
font-size: 12px;
}
/* 消息列表占满剩余高度 */
.msgList {
flex: 1;
margin-bottom: 120px;
margin-left: 12px;
margin-right: 12px;
}
/* 卡片 */
.msgCard {
border-radius: 12px;
padding: 10px 12px;
margin-bottom: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.06);
background-color: #fff;
}
.msgCard.tx {
border-left-width: 4px;
border-left-style: solid;
border-left-color: #3c9cff;
}
.msgCard.rx {
border-left-width: 4px;
border-left-style: solid;
border-left-color: #67c23a;
}
.msgCard.message {
border-left-width: 4px;
border-left-style: solid;
border-left-color: #7e50c2;
}
.msgCard.start {
border-left-width: 4px;
border-left-style: solid;
border-left-color: #ff5500;
}
.msgHeader {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
margin-bottom: 6px;
}
.msgTag {
font-size: 12px;
color: #333;
}
.msgTime {
font-size: 12px;
color: #999;
}
.msgBody {
font-size: 14px;
color: #222;
}
</style>