Browse Source

refactor: 抽取 MonitorDeviceList 左侧设备列表

main
xiaosi 3 weeks ago
parent
commit
e7acbf1d63
  1. 107
      src/components/MonitorDeviceList.vue
  2. 75
      src/views/MonitorView/MonitorView.vue

107
src/components/MonitorDeviceList.vue

@ -0,0 +1,107 @@
<template>
<aside class="device-panel">
<div class="panel-heading">
<div><h1>设备列表</h1><span>{{ dockCount }} · {{ droneCount }} </span></div>
<button class="icon-button" title="收起面板" @click="emit('clear-selection')"><svg><use href="#i-x" /></svg></button>
</div>
<div class="device-search">
<t-input v-model="searchModel" clearable placeholder="搜索机巢或无人机">
<template #prefix-icon><t-icon name="search" /></template>
</t-input>
</div>
<div class="filter-row">
<button class="filter" :class="{ active: filter === 'all' }" @click="emit('update:filter', 'all')">全部 <b>{{ filterCounts.all }}</b></button>
<button class="filter" :class="{ active: filter === 'online' }" @click="emit('update:filter', 'online')">在线 <b>{{ filterCounts.online }}</b></button>
<button class="filter" :class="{ active: filter === 'alarm' }" @click="emit('update:filter', 'alarm')">告警 <b>{{ filterCounts.alarm }}</b></button>
</div>
<div v-if="docks.length" class="multi-select-bar" :class="{ 'has-selection': selectedSize }">
<span>已选择 {{ selectedSize }} 个机巢</span>
<button type="button" @click="emit('select-all')">全选</button>
<button type="button" @click="emit('clear-dock-selection')">取消选择</button>
</div>
<div class="device-list">
<DockDeviceCard
v-for="dock in docks"
:key="dock.id"
:dock="dock"
:active="isDockActive(dock)"
:multi-selected="selectedDockIds.has(dock.id)"
:selected-drone-id="selectedId"
:altitude-of="altitudeOf"
@select="emit('select', $event)"
@toggle-select="(id, checked) => emit('toggle-select', id, checked)"
/>
<div v-if="!docks.length" class="table-empty" style="height:120px;display:grid;place-items:center;color:var(--muted);font-size:10px">暂无匹配设备</div>
</div>
</aside>
</template>
<script setup>
import { computed } from 'vue'
import DockDeviceCard from '@/components/DockDeviceCard.vue'
const props = defineProps({
dockCount: { type: Number, default: 0 },
droneCount: { type: Number, default: 0 },
search: { type: String, default: '' },
filter: { type: String, default: 'all' },
filterCounts: {
type: Object,
default: () => ({ all: 0, online: 0, alarm: 0 }),
},
docks: { type: Array, default: () => [] },
selectedDockIds: {
type: Object,
default: () => new Set(),
},
selectedId: { type: String, default: null },
isDockActive: { type: Function, required: true },
altitudeOf: { type: Function, default: null },
})
const emit = defineEmits([
'clear-selection',
'select-all',
'clear-dock-selection',
'select',
'toggle-select',
'update:search',
'update:filter',
])
const selectedSize = computed(() => props.selectedDockIds?.size ?? 0)
const searchModel = computed({
get: () => props.search,
set: (value) => emit('update:search', value ?? ''),
})
</script>
<style scoped>
.device-search {
height: auto;
margin-top: 16px;
padding: 0;
border: 0;
background: transparent;
}
.device-search :deep(.t-input) {
width: 100%;
}
.device-select-checkbox {
width: auto;
height: auto;
display: flex;
align-items: center;
justify-content: center;
}
.device-select-checkbox :deep(.t-checkbox) {
margin: 0;
}
.device-select-checkbox :deep(.t-checkbox__input) {
margin: 0;
}
</style>

75
src/views/MonitorView/MonitorView.vue

@ -1,43 +1,22 @@
<template> <template>
<section class="monitor-layout" :class="{ focused: !!selectedId, 'control-expanded': controlExpanded }"> <section class="monitor-layout" :class="{ focused: !!selectedId, 'control-expanded': controlExpanded }">
<aside class="device-panel">
<div class="panel-heading">
<div><h1>设备列表</h1><span>{{ devices.docks.length }} · {{ devices.drones.length }} </span></div>
<button class="icon-button" title="收起面板" @click="clearSelection"><svg><use href="#i-x" /></svg></button>
</div>
<div class="device-search">
<t-input v-model="search" clearable placeholder="搜索机巢或无人机">
<template #prefix-icon><t-icon name="search" /></template>
</t-input>
</div>
<div class="filter-row">
<button class="filter" :class="{ active: filter === 'all' }" @click="filter = 'all'">全部 <b>{{ filterCounts.all }}</b></button>
<button class="filter" :class="{ active: filter === 'online' }" @click="filter = 'online'">在线 <b>{{ filterCounts.online }}</b></button>
<button class="filter" :class="{ active: filter === 'alarm' }" @click="filter = 'alarm'">告警 <b>{{ filterCounts.alarm }}</b></button>
</div>
<div v-if="filteredDocks.length" class="multi-select-bar" :class="{ 'has-selection': selectedDockIds.size }">
<span>已选择 {{ selectedDockIds.size }} 个机巢</span>
<button type="button" @click="selectAllDocks">全选</button>
<button type="button" @click="clearDockSelection">取消选择</button>
</div>
<div class="device-list">
<DockDeviceCard
v-for="dock in filteredDocks"
:key="dock.id"
:dock="dock"
:active="isDockCardActive(dock)"
:multi-selected="selectedDockIds.has(dock.id)"
:selected-drone-id="selectedId"
<MonitorDeviceList
:dock-count="devices.docks.length"
:drone-count="devices.drones.length"
v-model:search="search"
v-model:filter="filter"
:filter-counts="filterCounts"
:docks="filteredDocks"
:selected-dock-ids="selectedDockIds"
:selected-id="selectedId"
:is-dock-active="isDockCardActive"
:altitude-of="droneAltitude" :altitude-of="droneAltitude"
@clear-selection="clearSelection"
@select-all="selectAllDocks"
@clear-dock-selection="clearDockSelection"
@select="select" @select="select"
@toggle-select="setDockSelection" @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>
<div class="map-panel"> <div class="map-panel">
<div class="map-toolbar"> <div class="map-toolbar">
@ -114,8 +93,8 @@ import mapHelper from '@/core/mapHelper'
import mapConfig, { MAP_STYLES, MAP_OVERVIEW, MAP_VIEW } from '@/config/map' import mapConfig, { MAP_STYLES, MAP_OVERVIEW, MAP_VIEW } from '@/config/map'
import { applyChineseLabels } from '@/utils/mapLabels' import { applyChineseLabels } from '@/utils/mapLabels'
import MonitorDetailPanel from '@/components/MonitorDetailPanel.vue' import MonitorDetailPanel from '@/components/MonitorDetailPanel.vue'
import MonitorDeviceList from '@/components/MonitorDeviceList.vue'
import DockStatusDialog from '@/components/DockStatusDialog.vue' import DockStatusDialog from '@/components/DockStatusDialog.vue'
import DockDeviceCard from '@/components/DockDeviceCard.vue'
import CommandProgressFloat from '@/components/CommandProgressFloat.vue' import CommandProgressFloat from '@/components/CommandProgressFloat.vue'
import { getLivePlayURL, heartbeatLive, joinLive, leaveLive } from '@/api/live' import { getLivePlayURL, heartbeatLive, joinLive, leaveLive } from '@/api/live'
import request from '@/utils/http' import request from '@/utils/http'
@ -1389,29 +1368,3 @@ async function runCommand(title, desc) {
} }
} }
</script> </script>
<style scoped>
.device-search {
height: auto;
margin-top: 16px;
padding: 0;
border: 0;
background: transparent;
}
.device-search :deep(.t-input) {
width: 100%;
}
.device-select-checkbox {
width: auto;
height: auto;
display: flex;
align-items: center;
justify-content: center;
}
.device-select-checkbox :deep(.t-checkbox) {
margin: 0;
}
.device-select-checkbox :deep(.t-checkbox__input) {
margin: 0;
}
</style>

Loading…
Cancel
Save