|
|
@@ -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>
|