蓝牙调试页面初步完成

This commit is contained in:
2026-01-16 12:00:49 +08:00
parent d9f8358191
commit bbc4f205a3
60 changed files with 2201 additions and 51331 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -4,6 +4,8 @@
<view slot="content">{{DevName || 'B13设备'}}</view>
</cu-custom>
<!-- 顶部标签:参考主机升级页面的切换方式 -->
<scroll-view scroll-x class="bg-white nav text-center text-bold text-xl">
<view class="flex text-center">
@@ -14,6 +16,17 @@
<!-- Tab: 蓝牙调试 -->
<view wx:if="{{TabCur==1}}" class="content">
<!-- 顶部操作按钮 -->
<view class="top-actions" style="display:flex; gap:6rpx; padding:4rpx 6rpx;">
<button size="mini" type="primary" bindtap="sendDoorEvent">设置门磁延时</button>
<button size="mini" type="primary" bindtap="sendBathEvent">设置卫浴延时</button>
<button size="mini" type="{{radarReading ? 'warn' : 'primary'}}" bindtap="toggleRadarRead">{{radarReading ? '停止读取' : '读雷达状态'}}</button>
<button size="mini" type="primary" bindtap="onStartOta">OTA升级</button>
</view>
<!-- 顶部设备分类 -->
<view class="grid">
<view class="grid-item" wx:for="{{radarLights}}" wx:key="key">
@@ -22,50 +35,98 @@
</view>
</view>
<!-- 状态功能块 -->
<!-- 状态功能块:已改为测试按键按钮,点击发送测试按键命令 -->
<view class="cards">
<view class="card">
<view class="icon orange"></view>
<view class="card {{pressedMask==1?'pressed':''}}" bindtap="onTestKeyTap" bindtouchstart="onTestKeyTouchStart" bindtouchend="onTestKeyTouchEnd" bindtouchcancel="onTestKeyTouchEnd" data-mask="0x01" data-masknum="1">
<view class="icon">
<image class="icon-img" src="../../../../images/tabbar/jinmen.png" mode="aspectFill" />
</view>
<text class="card-title">房间有人</text>
</view>
<view class="card">
<view class="icon gray"></view>
<view class="card {{pressedMask==2?'pressed':''}}" bindtap="onTestKeyTap" bindtouchstart="onTestKeyTouchStart" bindtouchend="onTestKeyTouchEnd" bindtouchcancel="onTestKeyTouchEnd" data-mask="0x02" data-masknum="2">
<view class="icon">
<image class="icon-img" src="../../../../images/tabbar/chumen.png" mode="aspectFill" />
</view>
<text class="card-title">房间无人</text>
</view>
<view class="card">
<view class="icon red"></view>
<view class="card {{pressedMask==4?'pressed':''}}" bindtap="onTestKeyTap" bindtouchstart="onTestKeyTouchStart" bindtouchend="onTestKeyTouchEnd" bindtouchcancel="onTestKeyTouchEnd" data-mask="0x04" data-masknum="4">
<view class="icon">
<image class="icon-img" src="../../../../images/tabbar/menkaiqi.png" mode="aspectFill" />
</view>
<text class="card-title">门开</text>
</view>
<view class="card">
<view class="icon green"></view>
<view class="card {{pressedMask==8?'pressed':''}}" bindtap="onTestKeyTap" bindtouchstart="onTestKeyTouchStart" bindtouchend="onTestKeyTouchEnd" bindtouchcancel="onTestKeyTouchEnd" data-mask="0x08" data-masknum="8">
<view class="icon">
<image class="icon-img" src="../../../../images/tabbar/menguanbi.png" mode="aspectFill" />
</view>
<text class="card-title">门关</text>
</view>
<view class="card">
<view class="icon blue"></view>
<view class="card {{pressedMask==16?'pressed':''}}" bindtap="onTestKeyTap" bindtouchstart="onTestKeyTouchStart" bindtouchend="onTestKeyTouchEnd" bindtouchcancel="onTestKeyTouchEnd" data-mask="0x10" data-masknum="16">
<view class="icon">
<image class="icon-img" src="../../../../images/tabbar/youren.png" mode="aspectFill" />
</view>
<text class="card-title">卫浴有人</text>
</view>
<view class="card">
<view class="icon gray"></view>
<view class="card {{pressedMask==32?'pressed':''}}" bindtap="onTestKeyTap" bindtouchstart="onTestKeyTouchStart" bindtouchend="onTestKeyTouchEnd" bindtouchcancel="onTestKeyTouchEnd" data-mask="0x20" data-masknum="32">
<view class="icon">
<image class="icon-img" src="../../../../images/tabbar/shiyourenkou.png" mode="aspectFill" />
</view>
<text class="card-title">卫浴无人</text>
</view>
</view>
<!-- 延时滑块 -->
<view class="slider-card">
<view class="slider-head">
<text class="title">开门延时</text>
<text class="value">{{openDelay}}s</text>
<!-- 门磁/卫浴事件设置(替代原延时滑块 -->
<view class="cfg-card">
<view class="cfg-head head-row">
<text>门磁开廊灯事件设置</text>
</view>
<view class="form-row">
<view class="form-inline">
<text class="label">触发延迟</text>
<picker class="inline-picker" mode="selector" range="{{doorTriggerOptions}}" value="{{doorTriggerIndex}}" bindchange="onDoorTriggerPickerChange">
<view class="picker-text">{{doorTriggerOptions[doorTriggerIndex]}}</view>
</picker>
<text class="label">单位</text>
<picker class="inline-picker" mode="selector" range="{{eventUnits}}" value="{{doorTriggerUnitIndex}}" bindchange="onDoorUnitChange" data-field="triggerUnit">
<view class="picker-text">{{eventUnits[doorTriggerUnitIndex]}}</view>
</picker>
<text class="label">释放延迟</text>
<picker class="inline-picker" mode="selector" range="{{doorReleaseOptions}}" value="{{doorReleaseIndex}}" bindchange="onDoorReleasePickerChange">
<view class="picker-text">{{doorReleaseOptions[doorReleaseIndex]}}</view>
</picker>
<text class="label">单位</text>
<picker class="inline-picker" mode="selector" range="{{eventUnits}}" value="{{doorReleaseUnitIndex}}" bindchange="onDoorUnitChange" data-field="releaseUnit">
<view class="picker-text">{{eventUnits[doorReleaseUnitIndex]}}</view>
</picker>
</view>
</view>
<slider bindchange="onOpenDelayChange" value="{{openDelay}}" min="0" max="60" step="1"/>
</view>
<view class="slider-card">
<view class="slider-head">
<text class="title">卫浴延时</text>
<text class="value">{{bathDelay}}s</text>
<view class="cfg-card">
<view class="cfg-head head-row">
<text>卫浴雷达开卫浴灯事件设置</text>
</view>
<view class="form-row">
<view class="form-inline">
<text class="label">触发延迟</text>
<picker class="inline-picker" mode="selector" range="{{bathTriggerOptions}}" value="{{bathTriggerIndex}}" bindchange="onBathTriggerPickerChange">
<view class="picker-text">{{bathTriggerOptions[bathTriggerIndex]}}</view>
</picker>
<text class="label">单位</text>
<picker class="inline-picker" mode="selector" range="{{eventUnits}}" value="{{bathTriggerUnitIndex}}" bindchange="onBathUnitChange" data-field="triggerUnit">
<view class="picker-text">{{eventUnits[bathTriggerUnitIndex]}}</view>
</picker>
<text class="label">释放延迟</text>
<picker class="inline-picker" mode="selector" range="{{bathReleaseOptions}}" value="{{bathReleaseIndex}}" bindchange="onBathReleasePickerChange">
<view class="picker-text">{{bathReleaseOptions[bathReleaseIndex]}}</view>
</picker>
<text class="label">单位</text>
<picker class="inline-picker" mode="selector" range="{{eventUnits}}" value="{{bathReleaseUnitIndex}}" bindchange="onBathUnitChange" data-field="releaseUnit">
<view class="picker-text">{{eventUnits[bathReleaseUnitIndex]}}</view>
</picker>
</view>
</view>
<slider bindchange="onBathDelayChange" value="{{bathDelay}}" min="0" max="60" step="1"/>
</view>
<!-- 通讯日志 -->
@@ -122,13 +183,15 @@
<text class="dr-value">{{bleVersion || '-'}}</text>
</view>
</view>
<view class="dr-btn dr-btn-view" bindtap="onSendReadVersion">读取蓝牙信息</view>
<view class="dr-btn dr-btn-view dr-btn-small" bindtap="onSendReadVersion">读取蓝牙信息</view>
</view>
<!-- 功能栏(保留导出/导入) -->
<view class="cfg-card">
<view class="toolbar">
<!-- 文件名放左侧 -->
<view class="import-box">{{importFileName || '未选择文件'}}</view>
<!-- 按钮放右侧 -->
<view class="toolbar-actions">
<button size="mini" type="default" bindtap="onExport">导出文件</button>
<button size="mini" type="default" bindtap="onImport">导入文件</button>
@@ -142,8 +205,8 @@
<view class="cfg-head head-row">
<text>端口配置</text>
<view class="head-actions">
<button size="mini" type="default" bindtap="onReadPorts">读取配置</button>
<button size="mini" type="primary" bindtap="onSavePorts">保存配置</button>
<button class="hide-btn" size="mini" type="default" bindtap="onReadPorts">读取配置</button>
<button class="hide-btn" size="mini" type="primary" bindtap="onSavePorts">保存配置</button>
</view>
</view>
<view class="table header port">
@@ -189,10 +252,10 @@
<view class="cfg-head head-row">
<text>条件配置</text>
<view class="head-actions">
<button size="mini" type="warn" bindtap="onDeleteCondGroup">删除条件组</button>
<button size="mini" type="warn" bindtap="onDeleteCondition">删除条件</button>
<button size="mini" type="primary" bindtap="onAddCondGroup">添加条件组</button>
<button size="mini" type="primary" bindtap="onAddCondition">添加条件</button>
<button class="hide-btn" size="mini" type="warn" bindtap="onDeleteCondGroup">删除条件组</button>
<button class="hide-btn" size="mini" type="warn" bindtap="onDeleteCondition">删除条件</button>
<button class="hide-btn" size="mini" type="primary" bindtap="onAddCondGroup">添加条件组</button>
<button class="hide-btn" size="mini" type="primary" bindtap="onAddCondition">添加条件</button>
</view>
</view>
<scroll-view scroll-y class="cond-scroll">
@@ -204,9 +267,7 @@
<text class="group-seq">{{grp.group}}</text>
<view class="group-time">
<text class="label">超时:</text>
<picker class="timeout-picker" mode="selector" range="{{ grp.timeoutUnit === 0 ? hourValues : msValues }}" value="{{ (grp.timeout || 1) - 1 }}" bindchange="onGroupPickerChange" data-idx="{{gidx}}" data-field="timeout">
<view class="picker-text">{{grp.timeout || 1}}</view>
</picker>
<input class="picker-text" placeholder="超时时间" value="{{grp.timeout || 0}}" type="number" bindinput="onGroupNumberInput" data-idx="{{gidx}}" data-field="timeout" />
<text class="label">单位:</text>
<picker class="unit-picker" mode="selector" range="{{timeUnits}}" value="{{grp.timeoutUnit}}" bindchange="onGroupPickerChange" data-idx="{{gidx}}" data-field="timeoutUnit">
<view class="picker-text">{{timeUnits[grp.timeoutUnit]}}</view>
@@ -230,9 +291,7 @@
</view>
<view class="td">
<text class="label">持续判定时间:</text>
<picker mode="selector" range="{{ it.judgeUnit === 0 ? hourValues : msValues }}" value="{{ (it.judgeTime || 1) - 1 }}" bindchange="onItemPickerChange" data-gidx="{{gidx}}" data-iidx="{{iidx}}" data-field="judgeTime">
<view class="picker-text">{{it.judgeTime || 1}}</view>
</picker>
<input class="picker-text" placeholder="持续判定时间" value="{{it.judgeTime || ''}}" type="number" bindinput="onItemNumberInput" data-gidx="{{gidx}}" data-iidx="{{iidx}}" data-field="judgeTime" />
</view>
<view class="td">
<text class="label">单位:</text>
@@ -245,10 +304,10 @@
<view class="tr tr-mid">
<view class="th">有无人标记</view>
<view class="th">有卡取电</view>
<view class="th">门磁</view>
<view class="th">门口红外</view>
<view class="th">卫红外</view>
<view class="th">浴红外</view>
<view class="th">门磁</view>
<view class="th">门口</view>
<view class="th">洗手间</view>
<view class="th">卧室</view>
</view>
<!-- 第三行:对应值 -->
<view class="tr tr-bottom">
@@ -291,6 +350,52 @@
</view>
</scroll-view>
</view>
<!-- 删除模态框 -->
<view wx:if="{{deleteDialogVisible}}" class="dialog-mask">
<view class="dialog">
<text class="dialog-title">{{deleteDialogMode === 'group' ? '删除条件组' : '删除条件'}}</text>
<view class="dialog-body">
<view wx:if="{{deleteDialogMode === 'group'}}">
<text class="label">选择条件组:</text>
<picker mode="selector" range="{{deleteDialogGroups}}" value="{{deleteDialogSelectedGroupIndex}}" bindchange="onDeleteDialogGroupChange">
<view class="picker-text">{{deleteDialogGroups[deleteDialogSelectedGroupIndex]}}</view>
</picker>
</view>
<view wx:if="{{deleteDialogMode === 'condition'}}">
<text class="label">选择条件组:</text>
<picker mode="selector" range="{{deleteDialogGroups}}" value="{{deleteDialogSelectedGroupIndex}}" bindchange="onDeleteDialogGroupChange">
<view class="picker-text">{{deleteDialogGroups[deleteDialogSelectedGroupIndex]}}</view>
</picker>
<text class="label">选择条件序号:</text>
<picker mode="selector" range="{{deleteDialogSeqs}}" value="{{deleteDialogSelectedSeqIndex}}" bindchange="onDeleteDialogSeqChange">
<view class="picker-text">{{deleteDialogSeqs[deleteDialogSelectedSeqIndex] || '无'}}</view>
</picker>
</view>
</view>
<view class="dialog-actions">
<button bindtap="onCancelDeleteDialog" type="default">取消</button>
<button bindtap="onConfirmDeleteDialog" type="primary">确定</button>
</view>
</view>
</view>
<!-- 添加模态框 -->
<view wx:if="{{addDialogVisible}}" class="dialog-mask">
<view class="dialog">
<text class="dialog-title">添加条件</text>
<view class="dialog-body">
<text class="label">选择条件组:</text>
<picker mode="selector" range="{{addDialogGroups}}" value="{{addDialogSelectedGroupIndex}}" bindchange="onAddDialogGroupChange">
<view class="picker-text">{{addDialogGroups[addDialogSelectedGroupIndex]}}</view>
</picker>
</view>
<view class="dialog-actions">
<button bindtap="onCancelAddDialog" type="default">取消</button>
<button bindtap="onConfirmAddDialog" type="primary">确定</button>
</view>
</view>
</view>
</view>
</view>
</view>

View File

@@ -17,15 +17,20 @@
.label { font-size: 24rpx; color: #606266; }
.cards { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8rpx; margin-bottom: 10rpx; }
.card { background: #fff; border-radius: 14rpx; padding: 14rpx 6rpx; display: flex; flex-direction: column; align-items: center; }
.icon { width: 30rpx; height: 30rpx; border-radius: 8rpx; margin-bottom: 4rpx; }
.card { background: #fff; border-radius: 14rpx; padding: 24rpx 8rpx; display: flex; flex-direction: column; align-items: center; min-height: 140rpx; box-sizing: border-box; transition: transform 120ms ease, filter 120ms ease; }
.icon { width: 48rpx; height: 48rpx; border-radius: 10rpx; margin-bottom: 8rpx; }
.icon.orange { background: #ff8f00; }
.icon.red { background: #ff3b30; }
.icon.green { background: #21c161; }
.icon.blue { background: #0ea5e9; }
.icon.gray { background: #9aa0a6; }
.icon { overflow: hidden; display:flex; align-items:center; justify-content:center; }
.icon-img { width: 100%; height: 100%; display: block; }
.card-title { font-size: 24rpx; color: #555; }
/* 按下效果 */
.card.pressed { transform: translateY(4rpx) scale(0.985); filter: brightness(0.94); box-shadow: none; }
.slider-card { background: #fff; border-radius: 14rpx; padding: 12rpx; box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.04); margin-bottom: 10rpx; }
.slider-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 6rpx; }
.slider-head .title { font-size: 26rpx; color: #333; }
@@ -56,6 +61,26 @@
.dr-btn-view { background: #0ea5e9; color: #ffffff; border-radius: 12rpx; box-shadow: 0 2rpx 6rpx rgba(14,165,233,0.35); }
.dr-btn-view:active { opacity: 0.85; }
/* 顶部操作按钮:紧凑内间距,适配工具栏顶部的多个小按钮 */
.top-actions button {
padding: 0 10rpx;
height: 58rpx;
line-height: 58rpx;
border-radius: 10rpx;
font-size: 24rpx;
}
/* 确保 OTA 按钮为蓝色(优先覆盖可能的主题色) */
.top-actions .ota-btn {
background: #0ea5e9 !important;
color: #ffffff !important;
border: 1rpx solid rgba(14,165,233,0.9) !important;
box-shadow: 0 2rpx 6rpx rgba(14,165,233,0.25);
}
/* 小尺寸按钮,适用于工具栏中次级操作,如读取蓝牙信息 */
.dr-btn-small { padding: 0 12rpx; min-width: 120rpx; height: 64rpx; line-height: 64rpx; }
/* 配置表样式 */
.cfg-card { background:#fff; border-radius: 14rpx; padding: 12rpx; box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.04); }
.cfg-head { font-size: 26rpx; color:#333; margin-bottom: 10rpx; }
@@ -63,10 +88,41 @@
.head-actions { display:flex; gap: 10rpx; }
.cond-card .head-actions { gap: 8rpx; }
.cond-card .head-actions button { padding: 0 14rpx; height: 48rpx; line-height: 48rpx; }
.toolbar { display:flex; flex-wrap: wrap; gap: 8rpx; align-items: center; justify-content: space-between; }
.toolbar .import-box { min-width: 260rpx; padding: 6rpx 10rpx; border: 1rpx solid #e5e9f2; border-radius: 10rpx; background:#f7f8fa; font-size: 22rpx; color:#606266; }
.toolbar .toolbar-actions { display:flex; align-items:center; gap: 6rpx; }
.toolbar .toolbar-actions button { padding: 0 12rpx; }
.toolbar {
/* 单行布局:文件名在左,三个按钮在右 */
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
gap: 8rpx;
}
.toolbar .import-box {
/* 文件名靠左,占用剩余空间 */
flex: 1 1 auto;
margin-right: 8rpx;
padding: 6rpx 10rpx;
border: 1rpx solid #e5e9f2;
border-radius: 10rpx;
background: #f7f8fa;
font-size: 22rpx;
color: #606266;
text-align: left;
/* 限制不换行、不扩展容器,溢出时截断并显示省略号 */
min-width: 0;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.toolbar .toolbar-actions {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 10rpx;
}
.toolbar .toolbar-actions button { padding: 0 12rpx; height: 54rpx; line-height: 54rpx; margin: 0; }
/* 隐藏特定按钮(通过在 WXML 上添加 class="hide-btn" 使用) */
.hide-btn { display: none !important; }
.table.header, .table.body { display:flex; flex-direction: column; gap: 6rpx; }
.table.port .tr { display:grid; grid-template-columns: 1.4fr 1.2fr 1.4fr 1.4fr 0.8fr 1.1fr 1fr; gap: 6rpx; padding: 6rpx 0; }
.table.body.port { gap: 2rpx; }
@@ -102,6 +158,26 @@
.cond-scroll { max-height: 680rpx; border: 1rpx solid #e5e9f2; border-radius: 12rpx; padding: 6rpx; box-sizing: border-box; background: #fff; }
.group-card { background:#fafafa; border: 1rpx solid #edf0f5; border-radius: 12rpx; padding: 10rpx; }
.group-head .group-info { display:flex; align-items:center; gap: 12rpx; }
.dialog-mask {
position: fixed;
left: 0; right: 0; top: 0; bottom: 0;
background: rgba(0,0,0,0.4);
display: flex;
align-items: center;
justify-content: center;
z-index: 999;
}
.dialog {
width: 680rpx;
background: #fff;
border-radius: 12rpx;
padding: 24rpx;
}
.dialog-title { font-size: 32rpx; margin-bottom: 12rpx; }
.dialog-body { margin-bottom: 16rpx; }
.dialog-actions { display: flex; justify-content: flex-end; gap: 12rpx }
.dialog .picker-text { padding: 10rpx 6rpx; border: 1rpx solid #eee; border-radius: 6rpx }
.group-title { font-size: 24rpx; color:#1e3a8a; font-weight: 600; }
.group-seq { font-size: 24rpx; color:#1e3a8a; font-weight: 600; }
.group-time { display:flex; align-items:center; gap: 8rpx; }
@@ -115,3 +191,17 @@
.log-time { display: block; font-size: 22rpx; color: #9aa0a6; margin-bottom: 4rpx; }
.log-text { display: block; font-size: 24rpx; color: #333; word-break: break-all; }
.log-empty { font-size: 24rpx; color: #9aa0a6; text-align: center; padding: 20rpx 0; }
/* Inline form for trigger/release delay - keep controls on one row on mobile */
.form-row { display:block; }
.form-inline { display:flex; align-items:center; gap: 10rpx; flex-wrap: nowrap; }
.form-inline .label { font-size: 22rpx; color:#606266; white-space: nowrap; }
.inline-input { width: 110rpx; height: 44rpx; padding: 0 8rpx; border: 1rpx solid #e5e9f2; border-radius: 8rpx; font-size: 24rpx; box-sizing: border-box; }
.inline-picker .picker-text { width: 90rpx; height: 44rpx; padding: 0 8rpx; border-radius: 8rpx; font-size: 22rpx; }
/* If screen too narrow, allow wrapping but keep label + control groups together */
@media (max-width: 360px) {
.form-inline { flex-wrap: wrap; }
.inline-input { width: 90rpx; }
.inline-picker .picker-text { width: 80rpx; }
}