|
|
@@ -3,11 +3,12 @@ import {computed, nextTick, onMounted, onUnmounted, ref, watch} from 'vue'
|
|
|
import * as echarts from 'echarts'
|
|
|
import {useSSE} from '@/composables/useSSE'
|
|
|
import {useTheme} from '@/composables/useTheme'
|
|
|
-import type {SseMessage, StatusCode, StatusRecord} from '@/types'
|
|
|
+import type {DeviceStatus, SseMessage, StatusCode, StatusRecord} from '@/types'
|
|
|
import {STATUS_CONFIG} from '@/types'
|
|
|
import StatusCard from '@/components/StatusCard.vue'
|
|
|
import http from '@/api/index'
|
|
|
import {getHistory} from '@/api/history'
|
|
|
+import {getStatus} from '@/api/status'
|
|
|
|
|
|
const {connected, onMessage, onDisconnect} = useSSE('/api/events')
|
|
|
const {theme} = useTheme()
|
|
|
@@ -20,6 +21,7 @@ const statusSince = ref(Date.now())
|
|
|
const statusDuration = ref('0s')
|
|
|
const connectSince = ref<number | null>(null)
|
|
|
const connectDuration = ref('')
|
|
|
+const deviceStatus = ref<DeviceStatus | null>(null)
|
|
|
|
|
|
let clockTimer: ReturnType<typeof setInterval> | null = null
|
|
|
let durationTimer: ReturnType<typeof setInterval> | null = null
|
|
|
@@ -259,6 +261,7 @@ onMounted(() => {
|
|
|
durationTimer = setInterval(updateDurations, 1000)
|
|
|
|
|
|
fetchHistory()
|
|
|
+ fetchStatus()
|
|
|
|
|
|
if (pieChartRef.value) {
|
|
|
pieChart = echarts.init(pieChartRef.value)
|
|
|
@@ -303,6 +306,17 @@ async function fetchHistory() {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+async function fetchStatus() {
|
|
|
+ try {
|
|
|
+ const res = await getStatus()
|
|
|
+ const data = res.data
|
|
|
+ if (data.code === 0 && data.data) {
|
|
|
+ deviceStatus.value = data.data
|
|
|
+ }
|
|
|
+ } catch {
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
const statusLabel = computed(() => STATUS_CONFIG[currentCode.value]?.label ?? currentCode.value)
|
|
|
|
|
|
const greeting = computed(() => {
|
|
|
@@ -377,6 +391,18 @@ async function sendCode(code: string) {
|
|
|
<div class="section-title">今日工作时长</div>
|
|
|
<div class="work-duration">{{ workDuration }}</div>
|
|
|
</div>
|
|
|
+ <div class="device-status-section">
|
|
|
+ <div class="section-title">设备状态</div>
|
|
|
+ <div v-if="deviceStatus?.mqtt.connected" class="device-connected">
|
|
|
+ <span class="device-dot connected"/> MQTT ● 已连接
|
|
|
+ </div>
|
|
|
+ <div v-else-if="deviceStatus?.ble.running" class="device-connected">
|
|
|
+ <span class="device-dot connected"/> BLE ● 已连接
|
|
|
+ </div>
|
|
|
+ <div v-else class="device-disconnected">
|
|
|
+ <span class="device-dot disconnected"/> 设备未连接
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
<div class="stats-section">
|
|
|
<div class="section-title">今日统计</div>
|
|
|
<div v-if="todayStats.length === 0" class="stats-empty">暂无数据</div>
|
|
|
@@ -614,9 +640,6 @@ async function sendCode(code: string) {
|
|
|
border: 1px solid var(--border-color);
|
|
|
border-radius: 8px;
|
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- justify-content: center;
|
|
|
}
|
|
|
|
|
|
.work-duration {
|
|
|
@@ -625,7 +648,50 @@ async function sendCode(code: string) {
|
|
|
color: #1890ff;
|
|
|
font-variant-numeric: tabular-nums;
|
|
|
line-height: 1;
|
|
|
- margin-top: 4px;
|
|
|
+}
|
|
|
+
|
|
|
+.device-status-section {
|
|
|
+ width: 180px;
|
|
|
+ flex-shrink: 0;
|
|
|
+ padding: 16px 20px;
|
|
|
+ background: var(--card-bg);
|
|
|
+ border: 1px solid var(--border-color);
|
|
|
+ border-radius: 8px;
|
|
|
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
|
|
+}
|
|
|
+
|
|
|
+.device-connected {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #52c41a;
|
|
|
+}
|
|
|
+
|
|
|
+.device-disconnected {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: var(--text-secondary);
|
|
|
+}
|
|
|
+
|
|
|
+.device-dot {
|
|
|
+ width: 10px;
|
|
|
+ height: 10px;
|
|
|
+ border-radius: 50%;
|
|
|
+ flex-shrink: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.device-dot.connected {
|
|
|
+ background: #52c41a;
|
|
|
+ box-shadow: 0 0 6px rgba(82, 196, 26, 0.5);
|
|
|
+}
|
|
|
+
|
|
|
+.device-dot.disconnected {
|
|
|
+ background: var(--text-secondary);
|
|
|
}
|
|
|
|
|
|
.stats-section {
|