322 lines
6.9 KiB
Plaintext
322 lines
6.9 KiB
Plaintext
<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="输入 CLI 命令,如 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="uts">
|
||
import { MyApiResult } from "@/uni_modules/android-ble"
|
||
import * as Ble from "../../../src/BluetoothManager"
|
||
import * as McmChannel from "../../../src/mcm/McmChannel.uts"
|
||
import * as McmCmd from "../../../src/mcm/McmCommands.uts"
|
||
import { CliResult } from "../../../src/mcm/McmChannel.uts"
|
||
|
||
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 → 选中 CA10 服务 → 订阅 notify)
|
||
function onConnect() {
|
||
if (Ble.isConnected()) {
|
||
uni.hideLoading()
|
||
bleIsConnect.value = true
|
||
return
|
||
}
|
||
uni.showLoading({
|
||
title: "Connecting...",
|
||
mask: true
|
||
})
|
||
pushMsg("start", "connect: " + bleAddress.value)
|
||
getApp().globalData.ConnectBle(bleAddress.value, 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", "未知命令或行超长")
|
||
} else {
|
||
pushMsg("message", "命令失败 status=" + r.status + (r.text.length > 0 ? (", 已收: " + 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: UTSJSONObject | null) => {
|
||
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())
|
||
}
|
||
})
|
||
|
||
onPageShow(() => {
|
||
subscribe()
|
||
})
|
||
|
||
onPageHide(() => {
|
||
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>
|