diff --git a/docs/superpowers/specs/2026-08-28-alarms-media-card-components-design.md b/docs/superpowers/specs/2026-08-28-alarms-media-card-components-design.md new file mode 100644 index 0000000..ee6d9c4 --- /dev/null +++ b/docs/superpowers/specs/2026-08-28-alarms-media-card-components-design.md @@ -0,0 +1,207 @@ +# Alarms + Media 薄卡片组件设计 + +## 背景 + +TDesign 表单/页签/标准列表迁移与 Devices 真分页已收口。仍有一批**强定制卡列表**沿用原型 `article + prototype.css`: + +- Alarms:线性 `alarm-item` +- Media:直播/原始视频网格卡 +- Monitor:侧栏 `device-card`(树形绑定机、多选) +- Tasks:航线库 `route-grid` + +本阶段只做 **Alarms + Media** 的本地薄展示组件,降低页面模板体积,为后续 Monitor 设备卡抽离铺路。 + +## 目标 + +1. 抽出 `AlarmItem.vue`、`MediaCard.vue` 两个展示组件。 +2. `AlarmsView` / `MediaView` 仅用组件替换 `v-for` 内的 `article` 循环体。 +3. **视觉与交互保持现状**:class 仍走 `prototype.css`,不引入 `t-list` / `t-card` 硬套。 +4. 数据加载、空态、映射、请求、tab 仍留在页面。 + +## 非目标 + +- 不改 Monitor 侧栏 / 地图 / 飞行控制 / LivePlayer +- 不改 Tasks 航线库卡与航线编辑器 +- 不迁 CSS 到 scoped(本阶段) +- 不改后端 API、分页策略、业务字段语义 +- 不做通用 `BusinessCard` 抽象 + +## 方案(已选 A) + +按视图各抽 **1 个列表项组件**: + +| 组件 | 用途 | +|------|------| +| `src/components/AlarmItem.vue` | 告警列表单行 | +| `src/components/MediaCard.vue` | 直播卡 / 原始视频卡(`variant` 区分) | + +不抽 `AlarmList` / `MediaGrid` 容器层(循环与空态仍在页面,避免无收益透传)。 + +## 组件契约 + +### AlarmItem + +**Props(展示用,与现映射字段对齐)** + +| prop | 类型 | 说明 | +|------|------|------| +| `severity` | string | 严重 / 警告 / 提示 | +| `severityClass` | string | `critical` / `medium` / `low` | +| `title` | string | 告警标题 | +| `device` | string | 设备标识文案 | +| `time` | string | 触发时间文案 | +| `status` | string | 待处理 / 已确认 / 已恢复 | +| `statusClass` | string | 状态样式 class | + +**Events** + +| event | payload | 说明 | +|-------|---------|------| +| `handle` | — | 点击「处理」;页面侧带上整条 alarm 调 API | + +根节点保持: + +```html +
+``` + +内部结构与现 `AlarmsView` 模板一致(severity 标签、title/device·time、status、`t-button` 处理)。 + +### MediaCard + +**Props** + +| prop | 类型 | 说明 | +|------|------|------| +| `variant` | `'live' \| 'video'` | 直播 / 原始视频 | +| `name` | string | 主标题 | +| `desc` | string | 副文案 | +| `visualClass` | string | 封面背景 class(如 `dock-camera`) | +| `source` | string? | live:画面来源角标 | +| `duration` | string? | video:时长角标 | + +**Events** + +| event | 何时 | +|-------|------| +| `play` | 点击播放按钮(live / video 共用) | +| `action` | live:更多;video:下载 | + +根节点: + +```html +
+
+
+
+``` + +- `variant === 'live'`:LIVE 角标 + `source` + 更多按钮(`#i-more`) +- `variant === 'video'`:`duration` + 下载按钮(`#i-download`) + +网格容器 class(`live-media-grid` / `media-grid`)仍在 `MediaView`,不进组件。 + +## 页面改造 + +### AlarmsView + +```html +
+ +
暂无告警
+
+``` + +- `load` / `handle` / 指标 computed / 侧栏 bars 不变 +- 可用小函数或直接逐 prop 绑定;**禁止**把 resolve API 塞进组件 + +### MediaView + +```html +
+ + … +
+ +
+ + … +
+``` + +- tabs、load、download、路由 `view` query 不变 + +## 样式 + +- 组件 **不加** 会改变视觉的 scoped 新规则 +- 继续依赖全局 `prototype.css` 中: + - `.alarm-item` 及相关 + - `.live-media-grid` / `.media-grid` / `.media-visual` / `.media-card-copy` / `.live-corner` / `.media-duration` 等 +- 若为消除 Vue 属性继承告警需 `inheritAttrs`,不得改 class 语义 + +## 文件清单 + +| 动作 | 路径 | +|------|------| +| 新增 | `src/components/AlarmItem.vue` | +| 新增 | `src/components/MediaCard.vue` | +| 修改 | `src/views/AlarmsView/AlarmsView.vue` | +| 修改 | `src/views/MediaView/MediaView.vue` | + +## 实现顺序 + +1. **A** — `AlarmItem` + `AlarmsView` 接入;单独 commit +2. **B** — `MediaCard` + `MediaView` 接入;单独 commit +3. **C** — 浏览器冒烟:告警列表/处理按钮;媒体直播卡/视频卡播放与操作 + +建议 commit message: + +- `refactor: 抽取 AlarmItem 告警列表项组件` +- `refactor: 抽取 MediaCard 媒体网格卡组件` + +## 验收标准 + +1. Alarms 页:列表渲染、空态、处理确认流与 resolve 成功刷新仍可用 +2. Media 页:直播/视频 tab、卡数量、播放/更多/下载行为与抽离前一致 +3. DOM:仍为 `article.alarm-item` / 媒体 `article > .media-visual + .media-card-copy` +4. 无控制台 error;不回归 Monitor / Devices / Tasks +5. 不修改 `prototype.css`(本阶段无必要删除/搬迁) + +## 风险与后续 + +| 风险 | 处理 | +|------|------| +| 漏绑 class 导致样式掉 | 对照现模板逐字段复制;冒烟看截图 | +| emit 丢上下文 | 页面 `@handle="handle(a)"` 闭包带 id | +| 过早抽象 | 明确禁止 BusinessCard;Monitor 另阶段 | + +**后续(不在本 spec)**:Monitor `DockDeviceCard`;Tasks `RouteCard`;可选 CSS 迁 scoped。 + +## 约束 + +- 工作目录:`/Users/qingyuan/CodeProject/laic-frontend` +- 不改后台 +- 不 `git add -A`;按文件精确 add + 中文 commit +- 跳过全量单测;以页面冒烟为准