Browse Source

docs: 增加 Alarms/Media 卡片组件实现计划

AlarmItem + MediaCard 薄组件抽取,分 A/B/C 验收。
main
xiaosi 3 weeks ago
parent
commit
b563d0f427
  1. 259
      docs/superpowers/plans/2026-08-28-alarms-media-card-components.md

259
docs/superpowers/plans/2026-08-28-alarms-media-card-components.md

@ -0,0 +1,259 @@
# Alarms + Media 薄卡片组件 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:** 将 Alarms 列表项与 Media 网格卡抽成本地薄展示组件,页面保留数据/请求/空态,视觉与交互不变。
**Architecture:** 新增 `AlarmItem.vue`、`MediaCard.vue`(props + emit only)。`AlarmsView` / `MediaView` 用组件替换 `v-for``article`。继续使用全局 `prototype.css` 类名,不加 scoped 新皮肤,不引入 t-list/t-card。
**Tech Stack:** Vue 3 `<script setup>`、既有 TDesign `t-button`、`prototype.css`
**Spec:** `docs/superpowers/specs/2026-08-28-alarms-media-card-components-design.md`
---
## File map
| 文件 | 职责 |
|------|------|
| `src/components/AlarmItem.vue` | 告警单行展示;emit `handle` |
| `src/components/MediaCard.vue` | 直播/视频卡;`variant`;emit `play` / `action` |
| `src/views/AlarmsView/AlarmsView.vue` | 接入 AlarmItem;load/handle 不变 |
| `src/views/MediaView/MediaView.vue` | 接入 MediaCard;load/openLive/download 不变 |
**不改:** Monitor、Tasks、LivePlayer、`prototype.css`、后端
---
### Task 1: AlarmItem + AlarmsView
**Files:**
- Create: `src/components/AlarmItem.vue`
- Modify: `src/views/AlarmsView/AlarmsView.vue`
- [ ] **Step 1: 创建 AlarmItem.vue**
```vue
<template>
<article class="alarm-item" :class="severityClass">
<span class="alarm-severity">{{ severity }}</span>
<div>
<strong>{{ title }}</strong>
<small>{{ device }} · {{ time }}</small>
</div>
<em class="status" :class="statusClass">{{ status }}</em>
<t-button class="alarm-handle" variant="outline" size="small" @click="$emit('handle')">处理</t-button>
</article>
</template>
<script setup>
defineProps({
severity: { type: String, required: true },
severityClass: { type: String, required: true },
title: { type: String, required: true },
device: { type: String, required: true },
time: { type: String, required: true },
status: { type: String, required: true },
statusClass: { type: String, required: true }
})
defineEmits(['handle'])
</script>
```
- [ ] **Step 2: AlarmsView 替换循环体**
将:
```html
<article v-for="a in alarms" :key="a.id" class="alarm-item" :class="a.severityClass">
...
</article>
```
改为:
```html
<AlarmItem
v-for="a in alarms"
:key="a.id"
:severity="a.severity"
:severity-class="a.severityClass"
:title="a.title"
:device="a.device"
:time="a.time"
:status="a.status"
:status-class="a.statusClass"
@handle="handle(a)"
/>
```
script 增加:
```js
import AlarmItem from '@/components/AlarmItem.vue'
```
保留空态 `table-empty`、`load`、`handle`、侧栏与 metrics。
- [ ] **Step 3: 静态核对**
Run:
```bash
rg -n 'AlarmItem|alarm-item' src/components/AlarmItem.vue src/views/AlarmsView/AlarmsView.vue
```
Expected: 组件内有 `alarm-item`;页面循环用 `AlarmItem`,无内联 `article.alarm-item`
- [ ] **Step 4: Commit**
```bash
git add src/components/AlarmItem.vue src/views/AlarmsView/AlarmsView.vue
git commit -m "refactor: 抽取 AlarmItem 告警列表项组件"
```
---
### Task 2: MediaCard + MediaView
**Files:**
- Create: `src/components/MediaCard.vue`
- Modify: `src/views/MediaView/MediaView.vue`
- [ ] **Step 1: 创建 MediaCard.vue**
```vue
<template>
<article>
<div class="media-visual" :class="visualClass">
<span v-if="variant === 'live'" class="live-corner"><i></i>LIVE</span>
<t-button shape="square" variant="text" @click="$emit('play')">
<svg><use href="#i-play" /></svg>
</t-button>
<small v-if="variant === 'live'">{{ source }}</small>
<small v-else class="media-duration">{{ duration }}</small>
</div>
<div class="media-card-copy">
<span><strong>{{ name }}</strong><small>{{ desc }}</small></span>
<t-button
class="icon-button"
shape="square"
variant="text"
@click="$emit('action')"
>
<svg>
<use :href="variant === 'live' ? '#i-more' : '#i-download'" />
</svg>
</t-button>
</div>
</article>
</template>
<script setup>
defineProps({
variant: { type: String, required: true }, // 'live' | 'video'
name: { type: String, required: true },
desc: { type: String, required: true },
visualClass: { type: String, required: true },
source: { type: String, default: '' },
duration: { type: String, default: '' }
})
defineEmits(['play', 'action'])
</script>
```
- [ ] **Step 2: MediaView 替换两处网格 article**
直播:
```html
<MediaCard
v-for="s in liveStreams"
:key="s.id"
variant="live"
:name="s.name"
:desc="s.desc"
:source="s.source"
:visual-class="s.visualClass"
@play="openLive(s)"
@action="ui.toast('更多操作')"
/>
```
视频:
```html
<MediaCard
v-for="v in videos"
:key="v.id"
variant="video"
:name="v.name"
:desc="v.desc"
:duration="v.duration"
:visual-class="v.visualClass"
@play="ui.toast('播放:' + v.name)"
@action="download(v)"
/>
```
import:
```js
import MediaCard from '@/components/MediaCard.vue'
```
保留 `live-media-grid` / `media-grid` 容器、空态、tabs、load、openLive、download。
- [ ] **Step 3: 静态核对**
```bash
rg -n 'MediaCard|media-visual|media-card-copy' src/components/MediaCard.vue src/views/MediaView/MediaView.vue
```
Expected: 视觉 class 在组件内;页面用 `MediaCard`,网格容器仍在页面。
- [ ] **Step 4: Commit**
```bash
git add src/components/MediaCard.vue src/views/MediaView/MediaView.vue
git commit -m "refactor: 抽取 MediaCard 媒体网格卡组件"
```
---
### Task 3: 浏览器冒烟
**Files:** 无代码改动(验收)
- [ ] **Step 1: 告警页**
登录后打开 `/alarms`
- 列表为 `article.alarm-item`(或组件根 article 带该类)
- 有数据时可见级别/标题/状态/「处理」
- 无 console error
- [ ] **Step 2: 媒体页**
打开 `/media`(及 `?view=live`):
- 直播卡:LIVE 角标 + 播放 + 更多
- 视频卡:时长 + 播放 + 下载
- tab 切换正常
- [ ] **Step 3: 回归抽查**
`/monitor` 地图工具条仍在;无需改 Devices。
- [ ] **Step 4: 记录结果**
若失败:修组件 class/emit 后补 commit;通过则本 plan 完成。
---
## 约束(执行时)
- 工作目录:`/Users/qingyuan/CodeProject/laic-frontend`
- 不改后台;不 `git add -A`
- 跳过全量单测;以静态核对 + 浏览器冒烟为准
- 中文 STATUS / commit message
Loading…
Cancel
Save