Browse Source

refactor: 抽取 Monitor DockDeviceCard 侧栏设备卡

main
xiaosi 3 weeks ago
parent
commit
d918e01ffc
  1. 72
      src/components/DockDeviceCard.vue
  2. 69
      src/views/MonitorView/MonitorView.vue

72
src/components/DockDeviceCard.vue

@ -0,0 +1,72 @@
<template>
<article class="device-card" :class="{ active, 'multi-selected': multiSelected }">
<div class="device-select-checkbox" :title="`选择${dock.name}`" @click.stop>
<t-checkbox
:checked="multiSelected"
@change="(checked) => $emit('toggle-select', dock.id, checked)"
/>
</div>
<button class="device-card-main" @click="$emit('select', dock.id)">
<span class="dock-glyph" :class="{ alarm: dock.statusClass === 'alarm', offline: dock.statusClass === 'offline' }">
<svg><use href="#i-home" /></svg>
</span>
<span class="device-copy">
<strong>{{ dock.name }}</strong>
<small>{{ dock.code }}</small>
</span>
<i class="status" :class="dock.statusClass">{{ dock.status }}</i>
</button>
<button
v-for="drone in dock.children || []"
:key="drone.id"
class="bound-drone"
:class="{
active: selectedDroneId === drone.id,
flying: drone.statusClass === 'mission',
offline: drone.statusClass === 'offline'
}"
@click="$emit('select', drone.id)"
>
<span class="tree-line"></span>
<svg><use href="#i-plane" /></svg>
<span><strong>{{ drone.name }}</strong><small>{{ droneSummary(drone) }}</small></span>
<i>{{ droneTag(drone) }}</i>
</button>
<div v-if="dock.statusClass === 'alarm'" class="device-alert">
<svg><use href="#i-alert" /></svg>检测到设备告警<span>{{ dock.updated }}</span>
</div>
<div v-else-if="dock.statusClass === 'offline'" class="device-alert muted">
最后在线 {{ dock.updated }}<span>网络中断</span>
</div>
</article>
</template>
<script setup>
const props = defineProps({
dock: { type: Object, required: true },
active: { type: Boolean, default: false },
multiSelected: { type: Boolean, default: false },
selectedDroneId: { type: String, default: null },
altitudeOf: { type: Function, default: null }
})
defineEmits(['select', 'toggle-select'])
function droneSummary(drone) {
if (drone.statusClass === 'mission') {
const alt = props.altitudeOf?.(drone.id)
if (alt != null) return `飞行中 · ${alt} m`
return '飞行中'
}
if (drone.statusClass === 'offline') return '设备离线'
return drone.battery && drone.battery !== '--' ? `已入巢 · 充电 ${drone.battery}` : '已入巢'
}
function droneTag(drone) {
if (drone.statusClass === 'mission') return '飞行中'
if (drone.statusClass === 'offline') return '离线'
return '在巢'
}
</script>

69
src/views/MonitorView/MonitorView.vue

@ -24,47 +24,17 @@
</div>
<div class="device-list">
<template v-for="dock in filteredDocks" :key="dock.id">
<article class="device-card" :class="{ active: isDockCardActive(dock), 'multi-selected': selectedDockIds.has(dock.id) }">
<div class="device-select-checkbox" :title="`选择${dock.name}`" @click.stop>
<t-checkbox
:checked="selectedDockIds.has(dock.id)"
@change="(checked) => setDockSelection(dock.id, checked)"
/>
</div>
<button class="device-card-main" @click="select(dock.id)">
<span class="dock-glyph" :class="{ alarm: dock.statusClass === 'alarm', offline: dock.statusClass === 'offline' }">
<svg><use href="#i-home" /></svg>
</span>
<span class="device-copy">
<strong>{{ dock.name }}</strong>
<small>{{ dock.code }}</small>
</span>
<i class="status" :class="dock.statusClass">{{ dock.status }}</i>
</button>
<button
v-for="drone in dock.children"
:key="drone.id"
class="bound-drone"
:class="{ active: selectedId === drone.id, flying: drone.statusClass === 'mission', offline: drone.statusClass === 'offline' }"
@click="select(drone.id)"
>
<span class="tree-line"></span>
<svg><use href="#i-plane" /></svg>
<span><strong>{{ drone.name }}</strong><small>{{ droneSummary(drone) }}</small></span>
<i>{{ droneTag(drone) }}</i>
</button>
<div v-if="dock.statusClass === 'alarm'" class="device-alert">
<svg><use href="#i-alert" /></svg>检测到设备告警<span>{{ dock.updated }}</span>
</div>
<div v-else-if="dock.statusClass === 'offline'" class="device-alert muted">
最后在线 {{ dock.updated }}<span>网络中断</span>
</div>
</article>
</template>
<DockDeviceCard
v-for="dock in filteredDocks"
:key="dock.id"
:dock="dock"
:active="isDockCardActive(dock)"
:multi-selected="selectedDockIds.has(dock.id)"
:selected-drone-id="selectedId"
:altitude-of="droneAltitude"
@select="select"
@toggle-select="setDockSelection"
/>
<div v-if="!filteredDocks.length" class="table-empty" style="height:120px;display:grid;place-items:center;color:var(--muted);font-size:10px">暂无匹配设备</div>
</div>
</aside>
@ -406,6 +376,7 @@ import mapHelper from '@/core/mapHelper'
import mapConfig, { MAP_STYLES, MAP_OVERVIEW, MAP_VIEW } from '@/config/map'
import { applyChineseLabels } from '@/utils/mapLabels'
import LivePlayer from '@/components/LivePlayer.vue'
import DockDeviceCard from '@/components/DockDeviceCard.vue'
import { getLivePlayURL, heartbeatLive, joinLive, leaveLive } from '@/api/live'
import request from '@/utils/http'
import * as urls from '@/config/urls'
@ -1375,20 +1346,8 @@ function isDockCardActive(dock) {
return selectedId.value === dock.id || dock.children.some((c) => c.id === selectedId.value)
}
function droneSummary(drone) {
if (drone.statusClass === 'mission') {
const alt = devices.asset(drone.id)?.altitude
if (alt != null) return `飞行中 · ${alt} m`
return '飞行中'
}
if (drone.statusClass === 'offline') return '设备离线'
return drone.battery && drone.battery !== '--' ? `已入巢 · 充电 ${drone.battery}` : '已入巢'
}
function droneTag(drone) {
if (drone.statusClass === 'mission') return '飞行中'
if (drone.statusClass === 'offline') return '离线'
return '在巢'
function droneAltitude(id) {
return devices.asset(id)?.altitude
}
function goDetail() {

Loading…
Cancel
Save