Quellcode durchsuchen

feat: 设备远程控制支持读取信号值,RSSI 按好/中/差/极差分级着色

skyline vor 3 Tagen
Ursprung
Commit
9cb50108c8
1 geänderte Dateien mit 152 neuen und 1 gelöschten Zeilen
  1. 152 1
      admin-web-new/src/views/admin/station/device-remote.vue

+ 152 - 1
admin-web-new/src/views/admin/station/device-remote.vue

@@ -1,7 +1,7 @@
 <script setup lang="ts">
 import { reactive, computed } from "vue";
 import { ElMessage, ElMessageBox } from "element-plus";
-import { Refresh, Reading, ChatDotSquare, CloseBold, SwitchButton, Loading } from "@element-plus/icons-vue";
+import { Refresh, Reading, ChatDotSquare, CloseBold, SwitchButton, Loading, Odometer } from "@element-plus/icons-vue";
 import { deviceRemoteApi } from "@/api/station";
 
 defineOptions({ name: "DeviceRemoteDialog" });
@@ -16,6 +16,7 @@ const initState = () => ({
   uptimeMs: "",
   hasPa: false,
   orderInfo: null as any,
+  hardwareInfo: null as any,
   showConfigEditor: false,
   configForm: {} as any,
   priceDialog: {
@@ -74,6 +75,33 @@ const temperatureDisplay = computed(() => {
   return state.temperatureChip != null ? `${state.temperatureChip}°C` : "--";
 });
 
+// 信号质量等级(ASU: 0-31,99 表示未知/无信号;好>24,中14~24,差9~14,极差≤9)
+const rssiLevelMap: Record<string, string> = { good: "好", mid: "中", bad: "差", "very-bad": "极差" };
+
+const getRssiLevel = (rssi: number | null | undefined) => {
+  if (rssi == null || rssi >= 99) return "";
+  if (rssi > 24) return "good";
+  if (rssi >= 14) return "mid";
+  if (rssi >= 9) return "bad";
+  return "very-bad";
+};
+
+const rssiLevel = computed(() => {
+  const level = getRssiLevel(state.hardwareInfo?.network?.rssi);
+  return level ? `signal-${level}` : "";
+});
+
+const rssiDisplay = computed(() => {
+  const rssi = state.hardwareInfo?.network?.rssi;
+  if (rssi == null || rssi >= 99) return "--";
+  return `${rssi}`;
+});
+
+const rssiLabel = computed(() => {
+  const level = getRssiLevel(state.hardwareInfo?.network?.rssi);
+  return level ? rssiLevelMap[level] : "";
+});
+
 const api = (url: string, data: any = {}) => {
   return deviceRemoteApi(url, { productKey, deviceName, ...data });
 };
@@ -184,6 +212,19 @@ const confirmPriceEdit = async () => {
   }
 };
 
+const handleReadHardwareInfo = async () => {
+  state.loading = true;
+  try {
+    const res: any = await api("queryHardwareInfo");
+    state.hardwareInfo = res || null;
+    ElMessage.success("信号值读取成功");
+  } catch (e) {
+    ElMessage.error("读取信号值失败");
+  } finally {
+    state.loading = false;
+  }
+};
+
 const handleShowMsgbox = () => {
   state.msgDialog.title = "";
   state.msgDialog.content = "";
@@ -310,6 +351,9 @@ defineExpose({ open });
           <el-button class="action-btn" type="primary" plain @click="handleReadConfig" :loading="state.loading">
             <el-icon><Reading /></el-icon> 读取配置
           </el-button>
+          <el-button class="action-btn" type="info" plain @click="handleReadHardwareInfo" :loading="state.loading">
+            <el-icon><Odometer /></el-icon> 读取信号值
+          </el-button>
           <el-button class="action-btn" type="warning" plain @click="handleShowMsgbox" :loading="state.loading">
             <el-icon><ChatDotSquare /></el-icon> 屏幕消息
           </el-button>
@@ -321,6 +365,48 @@ defineExpose({ open });
           </el-button>
         </div>
 
+        <!-- 信号值信息(点击「读取信号值」后显示) -->
+        <div class="signal-info-card" v-if="state.hardwareInfo">
+          <div class="signal-title">
+            <span>设备信号值</span>
+            <el-button size="small" text type="primary" @click="state.hardwareInfo = null">收起</el-button>
+          </div>
+          <div class="signal-grid">
+            <div class="signal-item">
+              <span class="signal-label">网络类型</span>
+              <span class="signal-value">{{ state.hardwareInfo.network?.type || '--' }}</span>
+            </div>
+            <div class="signal-item">
+              <span class="signal-label">信号质量(RSSI)</span>
+              <span class="signal-value" :class="rssiLevel">{{ rssiDisplay }}<span v-if="rssiLabel" class="signal-level-tag">{{ rssiLabel }}</span></span>
+            </div>
+            <div class="signal-item">
+              <span class="signal-label">误码率(BER)</span>
+              <span class="signal-value">{{ state.hardwareInfo.network?.ber ?? '--' }}</span>
+            </div>
+            <div class="signal-item">
+              <span class="signal-label">IMEI</span>
+              <span class="signal-value">{{ state.hardwareInfo.network?.imei || '--' }}</span>
+            </div>
+            <div class="signal-item">
+              <span class="signal-label">硬件版本</span>
+              <span class="signal-value">{{ state.hardwareInfo.board?.hard_version || '--' }}</span>
+            </div>
+            <div class="signal-item">
+              <span class="signal-label">固件版本</span>
+              <span class="signal-value">{{ state.hardwareInfo.board?.firmware_version || '--' }}</span>
+            </div>
+            <div class="signal-item">
+              <span class="signal-label">编译时间</span>
+              <span class="signal-value">{{ state.hardwareInfo.board?.build_time || '--' }}</span>
+            </div>
+            <div class="signal-item">
+              <span class="signal-label">芯片ID</span>
+              <span class="signal-value chip-id">{{ state.hardwareInfo.board?.chip_id || '--' }}</span>
+            </div>
+          </div>
+        </div>
+
         <!-- 订单信息(仅忙碌时显示) -->
         <div class="order-info-card" v-if="state.deviceState === 'busy' && state.orderInfo">
           <div class="order-title">当前订单</div>
@@ -591,4 +677,69 @@ defineExpose({ open });
     margin-bottom: 12px;
   }
 }
+
+.signal-info-card {
+  margin-top: 8px;
+  padding: 12px 16px;
+  background: #f5f7fa;
+  border-radius: 8px;
+
+  .signal-title {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    font-weight: 600;
+    margin-bottom: 10px;
+    color: #333;
+  }
+
+  .signal-grid {
+    display: grid;
+    grid-template-columns: repeat(2, 1fr);
+    gap: 6px 24px;
+  }
+
+  .signal-item {
+    display: flex;
+    justify-content: space-between;
+    font-size: 13px;
+    padding: 5px 0;
+    border-bottom: 1px dashed #e4e7ed;
+
+    .signal-label {
+      color: #909399;
+      flex-shrink: 0;
+      margin-right: 12px;
+    }
+
+    .signal-value {
+      color: #303133;
+      font-weight: 600;
+      word-break: break-all;
+      text-align: right;
+    }
+
+    .signal-value.chip-id {
+      font-size: 12px;
+      max-width: 220px;
+      overflow: hidden;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+    }
+  }
+
+  .signal-good { color: #67c23a; }
+  .signal-mid { color: #e6a23c; }
+  .signal-bad { color: #ff9800; }
+  .signal-very-bad { color: #f56c6c; }
+
+  .signal-level-tag {
+    margin-left: 6px;
+    font-size: 11px;
+    font-weight: 600;
+    padding: 1px 6px;
+    border-radius: 4px;
+    border: 1px solid currentColor;
+  }
+}
 </style>