6.3 KiB
Monitor DockDeviceCard Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: 将 Monitor 侧栏机巢设备卡抽成 DockDeviceCard 薄展示组件;选中/多选/过滤仍由页面持有。
Architecture: 新增 DockDeviceCard.vue(dock + 选中态 props,emit select / toggle-select)。droneSummary / droneTag 迁入组件;飞行高度通过可选 altitudeOf(id) 回调。继续使用 prototype.css。
Tech Stack: Vue 3 <script setup>、TDesign t-checkbox、prototype.css
Spec: docs/superpowers/specs/2026-08-28-monitor-dock-device-card-design.md
File map
| 文件 | 职责 |
|---|---|
src/components/DockDeviceCard.vue |
单张机巢卡展示 |
src/views/MonitorView/MonitorView.vue |
接入组件;删除内联卡模板与 summary/tag |
不改: 地图、控制条、LivePlayer、filter-row、multi-select-bar、devicesStore、prototype.css
Task 1: DockDeviceCard + MonitorView 接入
Files:
-
Create:
src/components/DockDeviceCard.vue -
Modify:
src/views/MonitorView/MonitorView.vue -
Step 1: 创建 DockDeviceCard.vue
<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>
- Step 2: MonitorView 替换 device-list 循环
将 device-list 内 template v-for + 整段 article.device-card 替换为:
<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"
: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>
script:
import DockDeviceCard from '@/components/DockDeviceCard.vue'
function droneAltitude(id) {
return devices.asset(id)?.altitude
}
删除页面内的 droneSummary、droneTag 函数(已迁入组件)。
保留:groupedDocks、filteredDocks、filterCounts、selectedDockIds、isDockCardActive、setDockSelection、selectAllDocks、clearDockSelection、搜索、filter-row、multi-select-bar、地图与控制。
确认 select / setDockSelection 可直接作为事件处理器(select(id)、setDockSelection(id, checked))。
- Step 3: 静态核对
rg -n 'DockDeviceCard|device-card|droneSummary|droneTag' src/components/DockDeviceCard.vue src/views/MonitorView/MonitorView.vue
Expected:
-
组件内有
device-card/droneSummary/droneTag -
MonitorView 使用
DockDeviceCard,无内联article.device-card,无页面级function droneSummary -
Step 4: Commit
git add src/components/DockDeviceCard.vue src/views/MonitorView/MonitorView.vue
git commit -m "refactor: 抽取 Monitor DockDeviceCard 侧栏设备卡"
Task 2: 浏览器冒烟
Files: 无代码改动(验收)
-
Step 1: 打开 /monitor
-
侧栏有
article.device-card -
filter-row(全部/在线/告警)仍在
-
multi-select-bar 仍在
-
Step 2: 交互
-
点击机巢主行:选中态 / 右侧聚焦仍工作
-
点击绑定无人机行:选中无人机
-
checkbox 勾选:
multi-selected样式;全选/取消选择可用 -
Step 3: 回归
-
map-type 卫星/地图仍在
-
无 console error
-
Step 4: 记录结果
失败则修组件事件/class 后补 commit;通过则完成。
约束
- 工作目录:
/Users/qingyuan/CodeProject/laic-frontend - 不改后台;精确 git add;中文 commit
- 跳过全量单测;静态核对 + 浏览器冒烟