EventStream
event-stream事件流 · 高频机器事件的连续时间线(审计流/CI 阶段/告警流) · 语义色承载「哪些不对劲」 + detail 折叠 + meta 右对齐 + overridden 单独成行标记「曾被拦但已放行」 + live 新条目淡入一次(首帧不闪·不做循环动效) + maxHeight 内滚 · 时间不做格式化(时区精度属调用方领域知识) · 区别 Timeline(稀疏人工编排)/ThreadList(可进入会话)/QueueLane(积压非时序)
用法
治理审计流
语义色承载「哪些被拦下了」,点击标题展开依据;已放行的条目保留放行说明,审计可追溯。
- 09:12:01会话开始 · 已注入任务契约0.9ms
- 09:12:03读取 pages/list/index.js0.3ms
- 09:12:09写入 pages/list/index.js0.4ms
- 09:12:31派发第 3 个子任务被拦截1.1ms
- 09:13:02写入知识库条目需确认0.8ms已放行:本次确实需要记录
- 09:14:20验收命令执行完毕 · 退出码 012.4s
tsx
<EventStream
items={events}
maxHeight={320}
onItemClick={(e) => openDetail(e.id)}
/>CI 流水线
同一组件换一组数据即可作流水线阶段流;耗时放 meta 列,等宽数字天然对齐。
- 14:02:11拉取代码 · main@a1b2c3d3.2s
- 14:02:19依赖安装完成8.1s
- 14:03:40单元测试 194/194 通过81s
- 14:04:02类型检查有 2 处 any22ssrc/legacy/adapter.ts:44, :91
- 14:04:55端到端测试 3 项失败53s登录跳转超时 ×2 / 支付回调断言不符 ×1
tsx
<EventStream items={pipelineSteps} defaultExpanded />实时追加(live)
新到的条目淡入一次即静止。事件流常年开着,任何循环动画都是噪音。
- 09:12:01会话开始 · 已注入任务契约0.9ms
- 09:12:03读取 pages/list/index.js0.3ms
- 09:12:09写入 pages/list/index.js0.4ms
tsx
<EventStream items={items} live maxHeight={280} />时间轴置右
嵌在左侧主内容右边的窄栏时,把轴放右侧更贴合视线落点。
- 14:02:11拉取代码 · main@a1b2c3d3.2s
- 14:02:19依赖安装完成8.1s
- 14:03:40单元测试 194/194 通过81s
- 14:04:02类型检查有 2 处 any22s
- 14:04:55端到端测试 3 项失败53s
tsx
<EventStream items={events} side="right" />什么时候用
- 审计流 / 治理拦截记录
- CI 流水线阶段
- 日志与告警流
- 任何「持续追加、需要快速扫视异常」的序列
什么时候不用
| 场景 | 用什么 | 为什么 |
|---|---|---|
| 会话 / 话题列表 | ThreadList | 每项是可进入的对话,强调「读到哪了」 |
| 里程碑叙事 | Timeline | 稀疏、人工编排、每点都有分量 |
| 按优先级排队的任务 | QueueLane | 强调积压与等待,不是时间序 |
用法
tsx
import { EventStream } from "@hulianui/ui";
<EventStream
items={[
{ id: 1, ts: "09:12:01", tone: "success", title: "构建通过", meta: "2.1s" },
{ id: 2, ts: "09:12:44", tone: "danger", title: "越权写入被拦",
detail: "目标超出允许范围", meta: "1.3ms" },
]}
maxHeight={320}
onItemClick={(e) => openDetail(e.id)}
/>Props
| 名称 | 类型 | 默认 | 说明 |
|---|---|---|---|
items | EventStreamItem[] | — | 顺序即展示顺序,组件不排序 |
maxHeight | number | string | — | 给了才出现内部滚动 |
emptyText | ReactNode | "暂无事件" | 空态文案 |
onItemClick | (item) => void | — | 给了才有可点击态与键盘可达 |
live | boolean | false | 新条目淡入一次 |
side | "left" | "right" | "left" | 时间轴所在侧 |
defaultExpanded | boolean | false | 是否默认展开 detail |
EventStreamItem
| 名称 | 类型 | 说明 |
|---|---|---|
id | string | number | 稳定唯一;live 靠它判断新条目 |
ts | ReactNode | 时间显示串 |
tone | "neutral" | "info" | "success" | "warning" | "danger" | 语义色,缺省 neutral |
title | ReactNode | 一行说清发生了什么 |
detail | ReactNode | 折叠区内容 |
meta | ReactNode | 右对齐尾部信息(耗时 / 编号) |
overridden | ReactNode | 已被人工放行时的说明 |
设计取舍
时间不做格式化。 组件收到什么显示什么。时区、精度、相对还是绝对,都是调用方的领域知识;组件擅自 toLocaleString 只会在跨时区场景制造错误。
`live` 只淡入一次,不做循环动效。 事件流常年开着,任何持续动画都是噪音。
首帧不闪。 live 模式初次挂载时全部视为「已见」,否则整屏一起动,反而看不出哪条是新的。
`overridden` 单独成行而非改色。 「曾被拦但放行了」与「从未被拦」必须视觉可区分——这是审计场景的刚需,把它并进 tone 会丢掉这层信息。
Playground
- 09:12:01会话开始 · 已注入任务契约0.9ms
- 09:12:03读取 pages/list/index.js0.3ms
- 09:12:09写入 pages/list/index.js0.4ms
- 09:12:31派发第 3 个子任务被拦截1.1ms
- 09:13:02写入知识库条目需确认0.8ms已放行:本次确实需要记录
- 09:14:20验收命令执行完毕 · 退出码 012.4s
<EventStream
items={events}
onItemClick={(e) => openDetail(e.id)}
/>