917 lines
43 KiB
Markdown
917 lines
43 KiB
Markdown
# 管理后台与可视化设计文档
|
||
|
||
## 一、设计原则
|
||
|
||
### 1.1 核心理念
|
||
|
||
**克制、高效、数据为先**
|
||
|
||
- 不做花哨的动效,所有设计都服务于「快速定位问题」
|
||
- 深色主题为主,长时间盯着不累眼
|
||
- 信息分层明确,重要的信息一眼看到,次要的信息收起来
|
||
- 操作路径短:3 次点击以内能到达任何核心页面
|
||
|
||
### 1.2 技术选型
|
||
|
||
| 层级 | 技术 | 说明 |
|
||
|------|------|------|
|
||
| 框架 | React 18 | 生态成熟,社区活跃 |
|
||
| 构建 | Vite 5 | 快,开发体验好 |
|
||
| 语言 | TypeScript | 类型安全,减少 bug |
|
||
| UI 库 | Ant Design 5 | 后台管理组件最全,深色主题好 |
|
||
| 路由 | React Router v6 | 官方推荐,功能完整 |
|
||
| 状态管理 | Zustand | 轻量,比 Redux 简单太多 |
|
||
| 数据请求 | TanStack Query (React Query) | 缓存、重发、分页都有了 |
|
||
| 图表 | ECharts 5 | 功能强大,国内用得多 |
|
||
| HTTP 客户端 | Axios | 拦截器、取消请求都方便 |
|
||
| 样式 | CSS Modules + Less | 按需定制主题 |
|
||
| 代码规范 | ESLint + Prettier | 统一代码风格 |
|
||
|
||
### 1.3 为什么选 Ant Design
|
||
|
||
- **组件最丰富**:表格、表单、弹窗、树形...后台需要的都有
|
||
- **深色主题好**:AntD 5 原生支持 ConfigProvider 切换主题
|
||
- **ProComponents**:ProTable、ProForm 等高级组件,开发效率翻倍
|
||
- **生态成熟**:遇到问题搜一下就有答案
|
||
- **设计语言统一**:有自己的设计规范,不用从零定
|
||
|
||
### 1.3 页面清单
|
||
|
||
| 页面 | 路径 | 优先级 | 说明 |
|
||
|------|------|--------|------|
|
||
| 项目列表 | `/manage/` | P0 | 项目管理(已有) |
|
||
| 项目概览 | `#/overview` | P0 | 项目总览、关键指标 |
|
||
| 错误列表 | `#/errors` | P0 | 错误 Issue 列表、详情 |
|
||
| 错误详情 | `#/errors/:id` | P0 | 单个错误的详细分析 |
|
||
| 性能分析 | `#/performance` | P1 | Web Vitals、性能趋势 |
|
||
| 网络分析 | `#/network` | P2 | 接口请求统计 |
|
||
| 行为分析 | `#/behavior` | P2 | PV、用户行为 |
|
||
| 告警中心 | `#/alerts` | P1 | 告警规则、告警历史 |
|
||
| 日志查询 | `#/logs` | P1 | Loki 原始日志查询 |
|
||
| 接入指南 | `#/integration` | P0 | SDK 接入文档(已有) |
|
||
| 项目设置 | `#/settings` | P1 | 项目配置、成员、采样率 |
|
||
|
||
---
|
||
|
||
## 二、设计系统
|
||
|
||
### 2.1 配色系统
|
||
|
||
**主色调**:蓝色系(专业、可信赖)
|
||
|
||
| 用途 | 颜色 | 色值 |
|
||
|------|------|------|
|
||
| 品牌主色 | 亮蓝 | `#0090f9` |
|
||
| 品牌主色(悬停) | 蓝 | `#1da1f2` |
|
||
| 成功 | 绿 | `#26a641` |
|
||
| 警告 | 黄 | `#e9c46a` |
|
||
| 错误 | 红 | `#e63946` |
|
||
| 信息 | 紫 | `#a855f7` |
|
||
|
||
**中性色(深色主题)**:
|
||
|
||
| 用途 | 色值 | 说明 |
|
||
|------|------|------|
|
||
| 背景(最深) | `#0d1117` | 页面底色 |
|
||
| 背景(卡片) | `#161b22` | 卡片、弹窗 |
|
||
| 背景(悬浮) | `#21262d` | hover、选中 |
|
||
| 边框 | `#30363d` | 分割线、边框 |
|
||
| 文字(主) | `#e6edf3` | 标题、正文 |
|
||
| 文字(次) | `#8b949e` | 辅助说明、时间 |
|
||
| 文字(弱) | `#6e7681` | 占位符、disabled |
|
||
|
||
### 2.2 字体与间距
|
||
|
||
**字体栈**:
|
||
```css
|
||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||
```
|
||
|
||
**字号阶梯**:
|
||
|
||
| 用途 | 大小 | 字重 |
|
||
|------|------|------|
|
||
| 页面大标题 | 24px | 600 |
|
||
| 区块标题 | 18px | 600 |
|
||
| 卡片标题 | 16px | 600 |
|
||
| 正文 | 14px | 400 |
|
||
| 辅助文字 | 12px | 400 |
|
||
|
||
**间距阶梯**(4px 基准):
|
||
`4px / 8px / 12px / 16px / 20px / 24px / 32px / 48px`
|
||
|
||
### 2.3 圆角与阴影
|
||
|
||
| 元素 | 圆角 | 阴影 |
|
||
|------|------|------|
|
||
| 按钮 | 6px | 无 |
|
||
| 输入框 | 6px | 无 |
|
||
| 卡片 | 8px | 无(用边框区分) |
|
||
| 弹窗 | 12px | `0 8px 24px rgba(0,0,0,0.4)` |
|
||
| 下拉菜单 | 8px | `0 4px 12px rgba(0,0,0,0.3)` |
|
||
|
||
---
|
||
|
||
## 三、整体布局
|
||
|
||
### 3.1 布局结构
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ Top Bar(64px) │
|
||
│ ┌──────┐ ┌──────────────┐ ┌─────────────┐ │
|
||
│ │ Logo │ │ 项目选择器 ▼ │ │ 🔔 👤 设置 │ │
|
||
│ └──────┘ └──────────────┘ └─────────────┘ │
|
||
├────────┬────────────────────────────────────────────────────────┤
|
||
│ │ │
|
||
│ Side │ Content Area │
|
||
│ Nav │ ┌──────────────────────────────────────────────────┐ │
|
||
│(64px │ │ Page Header(标题 + 操作 + 筛选) │ │
|
||
│ 展开 │ └──────────────────────────────────────────────────┘ │
|
||
│ 220px)│ ┌──────────────────────────────────────────────────┐ │
|
||
│ │ │ Metric Cards(指标卡片行) │ │
|
||
│ │ └──────────────────────────────────────────────────┘ │
|
||
│ │ ┌──────────────────────────────────────────────────┐ │
|
||
│ │ │ Charts / Tables(主内容区) │ │
|
||
│ │ │ │ │
|
||
│ │ │ │ │
|
||
│ │ └──────────────────────────────────────────────────┘ │
|
||
│ │ │
|
||
└────────┴────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 3.2 侧边栏(可折叠)
|
||
|
||
**收起态(64px)**:只显示图标
|
||
```
|
||
📊 ← 概览
|
||
🐛 ← 错误
|
||
⚡ ← 性能
|
||
🌐 ← 网络
|
||
📈 ← 看板
|
||
🔔 ← 告警
|
||
⚙️ ← 设置
|
||
```
|
||
|
||
**展开态(220px)**:图标 + 文字 + 角标
|
||
```
|
||
📊 概览
|
||
🐛 错误 12 ← 未解决错误数
|
||
⚡ 性能 3 ← 有性能告警
|
||
🌐 网络
|
||
📈 看板
|
||
🔔 告警 5 ← 未读告警
|
||
⚙️ 设置
|
||
```
|
||
|
||
### 3.3 顶部栏
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ ◀▶ 🪲 Light-Sentry [▼ 前端项目 - my-app ] 🔔 ⚙️ │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
- 左侧:折叠按钮 + Logo + 产品名
|
||
- 中间:项目选择器(下拉,可搜索)
|
||
- 右侧:告警铃(有未读红点) + 设置
|
||
|
||
### 3.4 页面头(每个页面都有)
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ 错误 Issues [时间范围: 24h ▼] [搜索] │
|
||
│ 12 个未解决 · 3 个今日新增 [导出] [刷新] │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## 四、核心页面详细设计
|
||
|
||
### 4.1 概览页(Overview)
|
||
|
||
**定位**:一屏看完项目健康状况,有问题快速跳转到对应页面
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ 概览 [24h ▼] [环境: 全部 ▼] │
|
||
├─────────────────────────────────────────────────────────────────┤
|
||
│ │
|
||
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
|
||
│ │ 错误数 │ │ 错误率 │ │ 影响用户│ │ LCP P95 │ │ CLS P95 │ │
|
||
│ │ 128 │ │ 2.3% │ │ 1,234 │ │ 3.2s │ │ 0.15 │ │
|
||
│ │ ↑15% │ │ ↑0.8% │ │ ↑12% │ │ 🔴 差 │ │ 🟡 中 │ │
|
||
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
|
||
│ │
|
||
│ ┌──────────────────────────┐ ┌───────────────────────────────┐ │
|
||
│ │ 错误趋势(24h) │ │ Top 5 错误 │ │
|
||
│ │ ╱╲ │ │ 1. TypeError: Cannot read... │ │
|
||
│ │ ╱ ╲ ╱╲ │ │ 2. AxiosError: 500 Inter... │ │
|
||
│ │ ╱╲╱ ╲╱ ╲ │ │ 3. ReferenceError: x is ... │ │
|
||
│ │ │ │ 4. ChunkLoadError: Loadin... │ │
|
||
│ │ │ │ 5. TypeError: Cannot set... │ │
|
||
│ └──────────────────────────┘ └───────────────────────────────┘ │
|
||
│ │
|
||
│ ┌──────────────────────────┐ ┌───────────────────────────────┐ │
|
||
│ │ 性能趋势(LCP/FID/CLS) │ │ 新错误(今日新增) │ │
|
||
│ │ 🟢 LCP 2.5s │ │ • TypeError: Cannot read... │ │
|
||
│ │ 🟡 FID 120ms │ │ • AxiosError: Network Er... │ │
|
||
│ │ 🟢 CLS 0.08 │ │ • ReferenceError: foo i... │ │
|
||
│ │ │ │ │ │
|
||
│ └──────────────────────────┘ └───────────────────────────────┘ │
|
||
│ │
|
||
│ ┌───────────────────────────────────────────────────────────┐ │
|
||
│ │ 实时错误流(最近 5 分钟) │ │
|
||
│ │ 14:32:15 TypeError: Cannot read property 'foo' │ │
|
||
│ │ 14:32:10 AxiosError: Request failed with 500 │ │
|
||
│ │ 14:32:05 ReferenceError: x is not defined │ │
|
||
│ │ ... │ │
|
||
│ └───────────────────────────────────────────────────────────┘ │
|
||
│ │
|
||
└─────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
**设计要点**:
|
||
- 5 个核心指标卡片放在最上面,一眼看完
|
||
- 指标卡片底部的小数字是「同比昨日」,红涨绿跌
|
||
- 左图右表,趋势 + Top 排行
|
||
- 底部实时流,让你感知系统状态
|
||
|
||
### 4.2 错误列表页(Errors)
|
||
|
||
**定位**:浏览和管理错误 Issue,找到要处理的问题
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ 错误 Issues [24h ▼] [🔍 搜索错误消息] │
|
||
│ 128 个错误 · 12 个未解决 · 3 个今日新增 [状态: 全部 ▼] [级别: ▼] │
|
||
├─────────────────────────────────────────────────────────────────┤
|
||
│ │
|
||
│ ┌───┬─────────────────────┬──────┬───────┬───────┬──────────┐ │
|
||
│ │ ✓ │ 错误消息 │ 级别 │ 次数 │ 用户 │ 最后出现 │ │
|
||
│ ├───┼─────────────────────┼──────┼───────┼───────┼──────────┤ │
|
||
│ │ 🔴│Cannot read property│error │ 456 │ 123 │ 2 分钟前 │ │
|
||
│ │ │ 'foo' of undefined│ │ │ │ │ │
|
||
│ ├───┼─────────────────────┼──────┼───────┼───────┼──────────┤ │
|
||
│ │ 🔴│AxiosError: Request │error │ 234 │ 89 │ 5 分钟前 │ │
|
||
│ │ │ failed with 500 │ │ │ │ │ │
|
||
│ ├───┼─────────────────────┼──────┼───────┼───────┼──────────┤ │
|
||
│ │ 🟡│ReferenceError: x is│warning│ 123 │ 45 │ 12 分钟前 │ │
|
||
│ │ │ not defined │ │ │ │ │ │
|
||
│ └───┴─────────────────────┴──────┴───────┴───────┴──────────┘ │
|
||
│ │
|
||
│ ◀ 1 2 3 4 ... 12 ▶ 每页 20 条 ▼ │
|
||
│ │
|
||
└─────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
**筛选器**:
|
||
- 时间范围(15m / 1h / 6h / 24h / 7d / 30d / 自定义)
|
||
- 状态(全部 / 未解决 / 已解决 / 已忽略)
|
||
- 级别(全部 / fatal / error / warning / info)
|
||
- 搜索(错误消息、指纹、文件路径)
|
||
- 环境
|
||
- 版本(release)
|
||
|
||
**列表交互**:
|
||
- 点击行 → 进入错误详情
|
||
- 行前 checkbox → 批量操作(标记已解决、忽略)
|
||
- hover 行 → 显示快捷操作(查看、已解决、忽略)
|
||
|
||
### 4.3 错误详情页
|
||
|
||
**定位**:深入分析单个错误,定位根因
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ ← 返回列表 [标记已解决] [忽略] [分配给 ▼] │
|
||
├─────────────────────────────────────────────────────────────────┤
|
||
│ │
|
||
│ 🔴 TypeError: Cannot read property 'foo' of undefined │
|
||
│ Uncaught exception · active · 来自浏览器 JS │
|
||
│ │
|
||
│ 📊 概览 📝 堆栈 📋 Breadcrumbs 👥 用户 🌐 分布 ⏱️ 趋势 │
|
||
│ ───── │
|
||
│ │
|
||
│ ┌──────────────┬──────────────┬──────────────┬──────────────┐ │
|
||
│ │ 总次数 │ 影响用户 │ 首次出现 │ 最后出现 │ │
|
||
│ │ 456 │ 123 │ 3 天前 │ 2 分钟前 │ │
|
||
│ └──────────────┴──────────────┴──────────────┴──────────────┘ │
|
||
│ │
|
||
│ ┌───────────────────────────────────────────────────────────┐ │
|
||
│ │ 发生趋势(7 天) │ │
|
||
│ │ ╱╲ │ │
|
||
│ │ ╱ ╲ ╱╲ │ │
|
||
│ │ ╱╲╱ ╲╱ ╲ │ │
|
||
│ └───────────────────────────────────────────────────────────┘ │
|
||
│ │
|
||
│ ┌───────────────────────────────────────────────────────────┐ │
|
||
│ │ 堆栈跟踪 │ │
|
||
│ │ 📄 app.js:123 onClick │ │
|
||
│ │ 📄 utils.js:45 handleClick │ │
|
||
│ │ 📄 index.js:8 main │ │
|
||
│ └───────────────────────────────────────────────────────────┘ │
|
||
│ │
|
||
│ ┌───────────────────────────────────────────────────────────┐ │
|
||
│ │ 最近出现(最新 5 条) │ │
|
||
│ │ 14:32:15 user_123 Chrome 120 macOS 点击按钮时 │ │
|
||
│ │ 14:31:45 user_456 Safari 17 iOS 加载页面时 │ │
|
||
│ │ ... │ │
|
||
│ └───────────────────────────────────────────────────────────┘ │
|
||
│ │
|
||
└─────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
**Tab 内容**:
|
||
- **概览**:核心数据 + 趋势图 + 最近出现
|
||
- **堆栈**:完整堆栈跟踪,可折叠,点击跳源码(如果配置了 sourcemap)
|
||
- **Breadcrumbs**:用户操作路径,时间线展示
|
||
- **用户**:受影响的用户列表
|
||
- **分布**:浏览器、OS、设备、地区分布
|
||
- **趋势**:更长时间的趋势图
|
||
|
||
### 4.4 性能分析页
|
||
|
||
**定位**:看页面性能好不好,哪里慢
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ 性能分析 [24h ▼] [页面: 全部 ▼] │
|
||
│ Web Vitals 指标 [浏览器: 全部 ▼] │
|
||
├─────────────────────────────────────────────────────────────────┤
|
||
│ │
|
||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌───────────┐ │
|
||
│ │ LCP │ │ FID │ │ CLS │ │ FCP │ │
|
||
│ │ 3.2s 🔴 │ │ 120ms 🟡 │ │ 0.15 🟢 │ │ 1.8s 🟡 │ │
|
||
│ │ P95: 4.5s │ │ P95: 200ms │ │ P95: 0.25 │ │ P95: 2.5s│ │
|
||
│ └─────────────┘ └─────────────┘ └─────────────┘ └───────────┘ │
|
||
│ │
|
||
│ ┌───────────────────────────────────────────────────────────┐ │
|
||
│ │ 性能趋势(P50 / P75 / P95 / P99) │ │
|
||
│ │ ╭────────────────────────────────────────────────────╮ │ │
|
||
│ │ │ ▲ P99 ── P95 ─ ─ P75 ┄ ┄ P50 │ │ │
|
||
│ │ │ │╲ │ │ │
|
||
│ │ │ │ ╲──────╮ │ │ │
|
||
│ │ │ │ ╲╱╲ │ │ │
|
||
│ │ │ │ │ │ │
|
||
│ │ │ ╰───────────────────────────────────────────────╯ │ │
|
||
│ └───────────────────────────────────────────────────────────┘ │
|
||
│ │
|
||
│ ┌───────────────────────────────────────────────────────────┐ │
|
||
│ │ 页面性能排行 [按 LCP 排序 ▼] │ │
|
||
│ ├─────────────────────┬──────┬──────┬──────┬──────┬───────┤ │
|
||
│ │ 页面 │ LCP │ FID │ CLS │ 样本 │ 评级 │ │
|
||
│ ├─────────────────────┼──────┼──────┼──────┼──────┼───────┤ │
|
||
│ │ /home │ 2.8s │ 80ms │ 0.10 │ 1.2k │ 🟢 良 │ │
|
||
│ │ /product/:id │ 4.2s │ 150ms│ 0.20 │ 800 │ 🔴 差 │ │
|
||
│ │ /checkout │ 3.5s │ 120ms│ 0.15 │ 500 │ 🟡 中 │ │
|
||
│ │ ... │ │ │ │ │ │ │
|
||
│ └─────────────────────┴──────┴──────┴──────┴──────┴───────┘ │
|
||
│ │
|
||
└─────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 4.5 告警中心
|
||
|
||
**定位**:配置告警规则,查看告警历史
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ 告警中心 │
|
||
│ ┌─────────┐ │
|
||
│ │ 告警规则 │ 告警历史 │
|
||
│ └─────────┘ │
|
||
├─────────────────────────────────────────────────────────────────┤
|
||
│ │
|
||
│ [+ 新建告警规则] │
|
||
│ │
|
||
│ ┌───┬─────────────────────┬──────────┬───────┬────────┬─────┐ │
|
||
│ │ ✓ │ 规则名称 │ 类型 │ 级别 │ 状态 │ 操作 │ │
|
||
│ ├───┼─────────────────────┼──────────┼───────┼────────┼─────┤ │
|
||
│ │ 🔔│ 错误数量突增 │ 突增告警 │ 严重 │ ✅ 启用 │ ... │ │
|
||
│ ├───┼─────────────────────┼──────────┼───────┼────────┼─────┤ │
|
||
│ │ 🔔│ 新错误出现 │ 新错误 │ 警告 │ ✅ 启用 │ ... │ │
|
||
│ ├───┼─────────────────────┼──────────┼───────┼────────┼─────┤ │
|
||
│ │ 🔔│ LCP 超过 4s │ 阈值告警 │ 警告 │ ✅ 启用 │ ... │ │
|
||
│ └───┴─────────────────────┴──────────┴───────┴────────┴─────┘ │
|
||
│ │
|
||
└─────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## 五、组件设计
|
||
|
||
### 5.1 指标卡片
|
||
|
||
```
|
||
┌─────────────────┐
|
||
│ 错误总数 │ ← 标题(小字、灰色)
|
||
│ 128 │ ← 数值(大字、粗体)
|
||
│ ↑ 15% vs 昨日 │ ← 趋势(红涨绿跌)
|
||
│ ╱╲ │ ← 迷你趋势图(可选)
|
||
│ ╱ ╲ │
|
||
└─────────────────┘
|
||
```
|
||
|
||
**变体**:
|
||
- 标准:数字 + 趋势
|
||
- 带评级:数字 + 颜色评级(性能指标用)
|
||
- 极简:只有数字
|
||
|
||
### 5.2 状态标签
|
||
|
||
| 状态 | 颜色 | 示例 |
|
||
|------|------|------|
|
||
| Active(未解决) | 红底白字 | `active` |
|
||
| Resolved(已解决) | 绿底白字 | `resolved` |
|
||
| Ignored(已忽略) | 灰底灰字 | `ignored` |
|
||
| Firing(告警中) | 红底白字 | `FIRING` |
|
||
| Resolved(已恢复) | 绿底白字 | `RESOLVED` |
|
||
|
||
### 5.3 错误级别标记
|
||
|
||
| 级别 | 颜色 |
|
||
|------|------|
|
||
| Fatal | 🔴 深红 |
|
||
| Error | 🔴 红 |
|
||
| Warning | 🟡 黄 |
|
||
| Info | 🔵 蓝 |
|
||
| Debug | ⚪ 灰 |
|
||
|
||
### 5.4 性能评级
|
||
|
||
| 评级 | 颜色 | LCP | FID | CLS |
|
||
|------|------|-----|-----|-----|
|
||
| Good 🟢 | 绿 | < 2.5s | < 100ms | < 0.1 |
|
||
| Needs Improvement 🟡 | 黄 | 2.5-4s | 100-300ms | 0.1-0.25 |
|
||
| Poor 🔴 | 红 | > 4s | > 300ms | > 0.25 |
|
||
|
||
---
|
||
|
||
## 六、图表设计
|
||
|
||
### 6.1 图表库选型
|
||
|
||
| 库 | 体积 | 功能 | 适合场景 |
|
||
|----|------|------|----------|
|
||
| Chart.js | ~60KB gzip | 基础图表够用 | 轻量、简单 |
|
||
| ECharts | ~150KB gzip | 功能强大 | 复杂图表、交互多 |
|
||
| uPlot | ~15KB gzip | 时序图表 | 极致轻量、性能好 |
|
||
|
||
**推荐方案**:
|
||
- 默认用 Chart.js(够用、轻量、生态好)
|
||
- 性能监控页面用 ECharts(需要更复杂的交互)
|
||
- 可按需加载,首屏只加载必要的
|
||
|
||
### 6.2 图表类型
|
||
|
||
| 图表 | 用途 | 页面 |
|
||
|------|------|------|
|
||
| 折线图 | 错误趋势、性能趋势 | 概览、错误、性能 |
|
||
| 柱状图 | Top N 排行、分布 | 错误、性能 |
|
||
| 饼图 / 环形图 | 占比、分布 | 概览、错误 |
|
||
| 面积图 | 堆叠趋势 | 性能 |
|
||
| 热力图 | 时间段分布 | 错误 |
|
||
|
||
### 6.3 与 Grafana 的关系
|
||
|
||
**管理后台解决的问题**:
|
||
- 错误 Issue 管理(状态、分配、标记)
|
||
- 项目配置和管理
|
||
- 告警规则配置
|
||
- 更友好的错误详情展示
|
||
|
||
**Grafana 解决的问题**:
|
||
- 灵活的自定义仪表盘
|
||
- 复杂的数据探索
|
||
- 多种数据源联合查询
|
||
- 告警(可复用 Grafana Alerting)
|
||
|
||
**分工原则**:
|
||
- 常用功能做进管理后台(开箱即用)
|
||
- 高级分析用 Grafana(灵活强大)
|
||
- 管理后台提供一键跳转到 Grafana 的链接
|
||
|
||
---
|
||
|
||
## 七、前端架构
|
||
|
||
### 7.1 目录结构
|
||
|
||
```
|
||
frontend/ # 前端工程(独立目录)
|
||
├── public/
|
||
│ └── favicon.ico
|
||
├── src/
|
||
│ ├── assets/ # 静态资源
|
||
│ │ ├── images/
|
||
│ │ └── icons/
|
||
│ ├── components/ # 通用组件
|
||
│ │ ├── layout/ # 布局组件
|
||
│ │ │ ├── MainLayout.tsx # 主布局(侧边栏+顶栏+内容)
|
||
│ │ │ ├── Sidebar.tsx # 侧边栏
|
||
│ │ │ ├── Header.tsx # 顶部栏
|
||
│ │ │ └── PageHeader.tsx # 页面头
|
||
│ │ ├── chart/ # 图表组件
|
||
│ │ │ ├── LineChart.tsx # 折线图
|
||
│ │ │ ├── BarChart.tsx # 柱状图
|
||
│ │ │ └── PieChart.tsx # 饼图
|
||
│ │ ├── metrics/ # 指标组件
|
||
│ │ │ └── MetricCard.tsx # 指标卡片
|
||
│ │ └── common/ # 其他通用组件
|
||
│ │ ├── StatusTag.tsx # 状态标签
|
||
│ │ └── CopyButton.tsx # 复制按钮
|
||
│ ├── pages/ # 页面
|
||
│ │ ├── overview/ # 概览页
|
||
│ │ │ └── index.tsx
|
||
│ │ ├── errors/ # 错误管理
|
||
│ │ │ ├── List.tsx # 错误列表
|
||
│ │ │ └── Detail.tsx # 错误详情
|
||
│ │ ├── performance/ # 性能分析
|
||
│ │ │ └── index.tsx
|
||
│ │ ├── network/ # 网络分析
|
||
│ │ │ └── index.tsx
|
||
│ │ ├── alerts/ # 告警中心
|
||
│ │ │ ├── Rules.tsx # 告警规则
|
||
│ │ │ └── History.tsx # 告警历史
|
||
│ │ ├── logs/ # 日志查询
|
||
│ │ │ └── index.tsx
|
||
│ │ ├── settings/ # 项目设置
|
||
│ │ │ └── index.tsx
|
||
│ │ ├── projects/ # 项目管理
|
||
│ │ │ └── List.tsx
|
||
│ │ └── integration/ # 接入指南
|
||
│ │ └── index.tsx
|
||
│ ├── store/ # 状态管理 (Zustand)
|
||
│ │ ├── index.ts # 导出
|
||
│ │ ├── useAppStore.ts # 全局状态(主题、侧边栏等)
|
||
│ │ └── useProjectStore.ts # 当前项目状态
|
||
│ ├── services/ # API 服务
|
||
│ │ ├── request.ts # Axios 封装
|
||
│ │ ├── project.ts # 项目相关 API
|
||
│ │ ├── error.ts # 错误相关 API
|
||
│ │ ├── performance.ts # 性能相关 API
|
||
│ │ └── alert.ts # 告警相关 API
|
||
│ ├── hooks/ # 自定义 Hooks
|
||
│ │ ├── useProject.ts # 当前项目 hook
|
||
│ │ ├── useTimeRange.ts # 时间范围 hook
|
||
│ │ └── useChartTheme.ts # 图表主题 hook
|
||
│ ├── router/ # 路由配置
|
||
│ │ ├── index.tsx # 路由入口
|
||
│ │ └── routes.ts # 路由表
|
||
│ ├── utils/ # 工具函数
|
||
│ │ ├── format.ts # 格式化(时间、数字等)
|
||
│ │ ├── date.ts # 日期工具
|
||
│ │ └── storage.ts # 本地存储
|
||
│ ├── types/ # TypeScript 类型定义
|
||
│ │ ├── api.ts # API 类型
|
||
│ │ ├── error.ts # 错误相关类型
|
||
│ │ ├── performance.ts # 性能相关类型
|
||
│ │ └── common.ts # 通用类型
|
||
│ ├── styles/ # 全局样式
|
||
│ │ ├── global.less
|
||
│ │ └── variables.less
|
||
│ ├── App.tsx # 根组件
|
||
│ └── main.tsx # 入口文件
|
||
├── .eslintrc.js # ESLint 配置
|
||
├── .prettierrc # Prettier 配置
|
||
├── tsconfig.json # TypeScript 配置
|
||
├── vite.config.ts # Vite 配置
|
||
└── package.json
|
||
```
|
||
|
||
### 7.2 路由设计
|
||
|
||
使用 React Router v6,嵌套路由 + 懒加载:
|
||
|
||
```typescript
|
||
// router/routes.ts
|
||
const routes = [
|
||
{
|
||
path: '/',
|
||
element: <MainLayout />,
|
||
children: [
|
||
{ index: true, element: <Navigate to="/overview" replace /> },
|
||
{ path: 'overview', lazy: () => import('@/pages/overview') },
|
||
{
|
||
path: 'errors',
|
||
children: [
|
||
{ index: true, lazy: () => import('@/pages/errors/List') },
|
||
{ path: ':id', lazy: () => import('@/pages/errors/Detail') },
|
||
],
|
||
},
|
||
{ path: 'performance', lazy: () => import('@/pages/performance') },
|
||
{ path: 'network', lazy: () => import('@/pages/network') },
|
||
{
|
||
path: 'alerts',
|
||
children: [
|
||
{ index: true, element: <Navigate to="rules" replace /> },
|
||
{ path: 'rules', lazy: () => import('@/pages/alerts/Rules') },
|
||
{ path: 'history', lazy: () => import('@/pages/alerts/History') },
|
||
],
|
||
},
|
||
{ path: 'logs', lazy: () => import('@/pages/logs') },
|
||
{ path: 'settings', lazy: () => import('@/pages/settings') },
|
||
{ path: 'integration', lazy: () => import('@/pages/integration') },
|
||
],
|
||
},
|
||
{ path: '/projects', element: <ProjectList /> },
|
||
{ path: '*', element: <NotFound /> },
|
||
];
|
||
```
|
||
|
||
### 7.3 状态管理(Zustand)
|
||
|
||
**为什么用 Zustand 而不是 Redux?**
|
||
- 比 Redux 简单太多,没有 reducer、action、dispatch 那些概念
|
||
- 体积小(~1KB),性能好
|
||
- TypeScript 支持好
|
||
- 支持 middleware(persist、devtools 等)
|
||
|
||
```typescript
|
||
// store/useAppStore.ts
|
||
import { create } from 'zustand';
|
||
|
||
interface AppState {
|
||
theme: 'dark' | 'light';
|
||
sidebarCollapsed: boolean;
|
||
currentProject: Project | null;
|
||
|
||
toggleTheme: () => void;
|
||
toggleSidebar: () => void;
|
||
setCurrentProject: (project: Project) => void;
|
||
}
|
||
|
||
export const useAppStore = create<AppState>((set) => ({
|
||
theme: 'dark',
|
||
sidebarCollapsed: false,
|
||
currentProject: null,
|
||
|
||
toggleTheme: () => set((s) => ({ theme: s.theme === 'dark' ? 'light' : 'dark' })),
|
||
toggleSidebar: () => set((s) => ({ sidebarCollapsed: !s.sidebarCollapsed })),
|
||
setCurrentProject: (project) => set({ currentProject: project }),
|
||
}));
|
||
```
|
||
|
||
### 7.4 数据请求(TanStack Query)
|
||
|
||
**为什么用 TanStack Query?**
|
||
- 自动缓存,相同请求不会重复发
|
||
- 后台自动刷新
|
||
- 分页、无限滚动都有封装
|
||
- 乐观更新、重试策略
|
||
- 和 Zustand 互补,不用把 API 数据放全局 store
|
||
|
||
```typescript
|
||
// hooks/useErrors.ts
|
||
import { useQuery } from '@tanstack/react-query';
|
||
import { getErrorList } from '@/services/error';
|
||
|
||
export function useErrorList(params: ErrorListParams) {
|
||
return useQuery({
|
||
queryKey: ['errors', params],
|
||
queryFn: () => getErrorList(params),
|
||
keepPreviousData: true,
|
||
staleTime: 30_000, // 30秒内认为是新鲜的
|
||
});
|
||
}
|
||
```
|
||
|
||
### 7.5 请求封装(Axios)
|
||
|
||
```typescript
|
||
// services/request.ts
|
||
import axios from 'axios';
|
||
import { message } from 'antd';
|
||
|
||
const request = axios.create({
|
||
baseURL: '/api',
|
||
timeout: 15000,
|
||
});
|
||
|
||
// 请求拦截器:加 token
|
||
request.interceptors.request.use((config) => {
|
||
const token = localStorage.getItem('token');
|
||
if (token) {
|
||
config.headers.Authorization = `Bearer ${token}`;
|
||
}
|
||
return config;
|
||
});
|
||
|
||
// 响应拦截器:统一处理错误
|
||
request.interceptors.response.use(
|
||
(res) => res.data,
|
||
(err) => {
|
||
const status = err.response?.status;
|
||
const message = err.response?.data?.message || err.message;
|
||
|
||
if (status === 401) {
|
||
// 登录过期,跳登录页
|
||
} else {
|
||
message.error(message);
|
||
}
|
||
|
||
return Promise.reject(err);
|
||
}
|
||
);
|
||
|
||
export default request;
|
||
```
|
||
|
||
### 7.6 主题配置(Ant Design 5 深色主题)
|
||
|
||
Ant Design 5 用 CSS-in-JS,通过 ConfigProvider 配置主题:
|
||
|
||
```typescript
|
||
// App.tsx
|
||
import { ConfigProvider, theme as antdTheme } from 'antd';
|
||
import { useAppStore } from '@/store';
|
||
|
||
function App() {
|
||
const { theme } = useAppStore();
|
||
|
||
return (
|
||
<ConfigProvider
|
||
theme={{
|
||
algorithm: theme === 'dark'
|
||
? antdTheme.darkAlgorithm
|
||
: antdTheme.defaultAlgorithm,
|
||
token: {
|
||
colorPrimary: '#0090f9',
|
||
borderRadius: 6,
|
||
},
|
||
}}
|
||
>
|
||
<Router />
|
||
</ConfigProvider>
|
||
);
|
||
}
|
||
```
|
||
|
||
**自定义暗色主题色板**(贴合 GitHub Dark 风格):
|
||
```typescript
|
||
{
|
||
colorBgLayout: '#0d1117', // 页面背景
|
||
colorBgContainer: '#161b22', // 卡片背景
|
||
colorBgElevated: '#21262d', // 悬浮背景
|
||
colorBorder: '#30363d', // 边框
|
||
colorText: '#e6edf3', // 主文字
|
||
colorTextSecondary: '#8b949e',// 次文字
|
||
colorTextTertiary: '#6e7681', // 弱文字
|
||
}
|
||
```
|
||
|
||
### 7.7 图表封装
|
||
|
||
基于 ECharts 封装通用图表组件,自动适配主题和容器大小:
|
||
|
||
```typescript
|
||
// components/chart/BaseChart.tsx
|
||
import { useEffect, useRef } from 'react';
|
||
import * as echarts from 'echarts';
|
||
import { useAppStore } from '@/store';
|
||
|
||
interface BaseChartProps {
|
||
option: echarts.EChartsOption;
|
||
height?: number | string;
|
||
className?: string;
|
||
}
|
||
|
||
export function BaseChart({ option, height = 300, className }: BaseChartProps) {
|
||
const chartRef = useRef<HTMLDivElement>(null);
|
||
const chartInstance = useRef<echarts.ECharts | null>(null);
|
||
const { theme } = useAppStore();
|
||
|
||
useEffect(() => {
|
||
if (!chartRef.current) return;
|
||
|
||
chartInstance.current = echarts.init(
|
||
chartRef.current,
|
||
theme === 'dark' ? 'dark' : null
|
||
);
|
||
|
||
const resizeObserver = new ResizeObserver(() => {
|
||
chartInstance.current?.resize();
|
||
});
|
||
resizeObserver.observe(chartRef.current);
|
||
|
||
return () => {
|
||
resizeObserver.disconnect();
|
||
chartInstance.current?.dispose();
|
||
};
|
||
}, [theme]);
|
||
|
||
useEffect(() => {
|
||
chartInstance.current?.setOption(option, true);
|
||
}, [option]);
|
||
|
||
return <div ref={chartRef} style={{ height }} className={className} />;
|
||
}
|
||
```
|
||
|
||
### 7.8 构建与部署
|
||
|
||
**开发环境**:
|
||
```bash
|
||
pnpm dev # 启动开发服务器
|
||
pnpm build # 生产构建
|
||
pnpm preview # 预览构建结果
|
||
pnpm lint # ESLint 检查
|
||
pnpm type-check # TypeScript 类型检查
|
||
```
|
||
|
||
**部署方案**:
|
||
- 构建产物:`dist/` 目录
|
||
- Nginx 直接托管静态文件
|
||
- API 请求通过 Nginx 反向代理到后端
|
||
- 和现有 `public/manage/` 共存,新前端走 `/manage/` 路径
|
||
|
||
```nginx
|
||
location /manage/ {
|
||
try_files $uri $uri/ /manage/index.html;
|
||
root /path/to/frontend/dist;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 八、交互设计原则
|
||
|
||
### 8.1 快速导航
|
||
|
||
- 全局搜索:`⌘K` 唤起,可搜项目、错误、页面
|
||
- 面包屑:永远知道自己在哪
|
||
- 相关跳转:错误详情里可跳转到对应页面的性能分析
|
||
|
||
### 8.2 数据加载
|
||
|
||
- 骨架屏:数据加载中显示骨架,不跳
|
||
- 增量加载:列表滚动加载
|
||
- 实时刷新:可开/关,默认 30 秒自动刷新
|
||
|
||
### 8.3 操作反馈
|
||
|
||
- 重要操作(删除、标记已解决)有确认弹窗
|
||
- 成功操作顶部弹 toast,3 秒自动消失
|
||
- 操作失败显示错误原因,可重试
|
||
|
||
---
|
||
|
||
## 九、API 设计
|
||
|
||
### 9.1 统计 API
|
||
|
||
```
|
||
# 概览数据
|
||
GET /api/projects/{id}/overview?time_range=24h
|
||
|
||
# 错误趋势
|
||
GET /api/projects/{id}/errors/trend?time_range=24h&interval=1h
|
||
|
||
# 错误列表
|
||
GET /api/projects/{id}/errors?page=1&page_size=20&level=error&status=active
|
||
|
||
# 错误详情
|
||
GET /api/projects/{id}/errors/{fingerprint}
|
||
|
||
# 性能指标
|
||
GET /api/projects/{id}/performance/metrics?time_range=24h&metric=lcp
|
||
|
||
# 告警规则
|
||
GET /api/projects/{id}/alerts/rules
|
||
POST /api/projects/{id}/alerts/rules
|
||
PUT /api/projects/{id}/alerts/rules/{rule_id}
|
||
DELETE /api/projects/{id}/alerts/rules/{rule_id}
|
||
|
||
# 告警历史
|
||
GET /api/projects/{id}/alerts/history?page=1&page_size=20
|
||
```
|
||
|
||
---
|
||
|
||
## 十、演进路线
|
||
|
||
### 10.1 当前状态
|
||
|
||
现有的 [index.html](file:///Users/weidingjian/Desktop/work/ai/light-sentry/public/manage/index.html) 是项目列表页,只有最基础的 CRUD 功能。
|
||
|
||
### 10.2 版本规划
|
||
|
||
**v1.1(下一个版本)**:
|
||
- 改造成侧边栏 + 内容区的布局
|
||
- 新增概览页(核心指标 + 错误趋势)
|
||
- 新增错误列表页(从聚合表读)
|
||
- 项目选择器移到顶部
|
||
|
||
**v1.2**:
|
||
- 错误详情页
|
||
- 性能分析页
|
||
- 告警规则管理
|
||
|
||
**v2.0**:
|
||
- 完整的错误管理工作流
|
||
- 性能深入分析(瀑布图、资源时序)
|
||
- 用户会话追踪
|