feat: 优化前端布局为流动式响应式设计
- 移除 Dashboard max-width 限制,充分利用屏幕空间 - Summary Cards 和 Target Cards 改为 Flexbox wrap 流动布局 - 卡片固定宽度 280px,根据页面宽度自动调整单行数量 - 添加平滑过渡动画效果(0.3s ease) - 移除移动端媒体查询覆盖规则 - 同步更新 card-dashboard 规格文档
This commit is contained in:
24
README.md
24
README.md
@@ -170,13 +170,13 @@ targets:
|
||||
|
||||
## API 端点
|
||||
|
||||
| 端点 | 说明 |
|
||||
| ----------------------------------------------------- | --------------------------------------- |
|
||||
| `GET /health` | 健康检查 |
|
||||
| `GET /api/summary` | 总览统计(total/up/down/lastCheckTime) |
|
||||
| `GET /api/targets` | 目标列表及最新状态、分组和采样数据 |
|
||||
| `GET /api/targets/:id/history?from=ISO&to=ISO&page=1&pageSize=20` | 指定目标的拨测记录(时间范围 + 分页) |
|
||||
| `GET /api/targets/:id/trend?from=ISO&to=ISO` | 指定目标的按小时聚合趋势 |
|
||||
| 端点 | 说明 |
|
||||
| ----------------------------------------------------------------- | --------------------------------------- |
|
||||
| `GET /health` | 健康检查 |
|
||||
| `GET /api/summary` | 总览统计(total/up/down/lastCheckTime) |
|
||||
| `GET /api/targets` | 目标列表及最新状态、分组和采样数据 |
|
||||
| `GET /api/targets/:id/history?from=ISO&to=ISO&page=1&pageSize=20` | 指定目标的拨测记录(时间范围 + 分页) |
|
||||
| `GET /api/targets/:id/trend?from=ISO&to=ISO` | 指定目标的按小时聚合趋势 |
|
||||
|
||||
### 响应字段
|
||||
|
||||
@@ -204,11 +204,11 @@ API 错误返回 `ApiErrorResponse` 格式:
|
||||
{ "error": "描述信息", "status": 400 }
|
||||
```
|
||||
|
||||
| 状态码 | 触发场景 |
|
||||
|--------|---------|
|
||||
| 400 | 参数格式错误(无效 ID、from/to 缺失或格式错误、page/pageSize 非正整数) |
|
||||
| 404 | 目标不存在、API 路由未匹配 |
|
||||
| 405 | 非 GET 方法请求 API 路由 |
|
||||
| 状态码 | 触发场景 |
|
||||
| ------ | ----------------------------------------------------------------------- |
|
||||
| 400 | 参数格式错误(无效 ID、from/to 缺失或格式错误、page/pageSize 非正整数) |
|
||||
| 404 | 目标不存在、API 路由未匹配 |
|
||||
| 405 | 非 GET 方法请求 API 路由 |
|
||||
|
||||
## 代码质量
|
||||
|
||||
|
||||
@@ -20,15 +20,27 @@ Dashboard SHALL 按分组展示所有拨测目标,每个分组包含带统计
|
||||
- **THEN** 分组标题 SHALL 显示 "默认分组"
|
||||
|
||||
### Requirement: 响应式卡片网格
|
||||
Dashboard SHALL 使用固定宽度的卡片配合响应式网格布局。
|
||||
Dashboard SHALL 使用固定宽度的卡片配合 Flexbox 流动布局,容器无最大宽度限制。
|
||||
|
||||
#### Scenario: Dashboard 容器占满宽度
|
||||
- **WHEN** 用户打开 Dashboard 页面
|
||||
- **THEN** Dashboard 容器 SHALL 占满浏览器宽度,不设置 max-width 限制
|
||||
|
||||
#### Scenario: 卡片固定宽度
|
||||
- **WHEN** 页面渲染卡片
|
||||
- **THEN** 每个卡片 SHALL 固定宽度 280px
|
||||
- **WHEN** 页面渲染卡片(包括 Summary Cards 和 Target Cards)
|
||||
- **THEN** 每个卡片 SHALL 固定宽度 280px,使用 `flex-shrink: 0` 防止收缩
|
||||
|
||||
#### Scenario: 响应式列数
|
||||
#### Scenario: 流动式布局
|
||||
- **WHEN** 视口宽度变化
|
||||
- **THEN** 卡片网格 SHALL 自动调整列数,使用 CSS Grid auto-fill 适配可用空间
|
||||
- **THEN** 卡片网格 SHALL 使用 Flexbox wrap 自动换行,根据可用宽度调整单行卡片数量
|
||||
|
||||
#### Scenario: 卡片左对齐
|
||||
- **WHEN** 页面渲染卡片网格
|
||||
- **THEN** 卡片 SHALL 左对齐排列,右侧自然留白
|
||||
|
||||
#### Scenario: 统一间距
|
||||
- **WHEN** 页面渲染 Summary Cards 和 Target Cards
|
||||
- **THEN** 两种卡片网格 SHALL 使用相同的 gap 间距(16px)
|
||||
|
||||
### Requirement: 目标卡片内容
|
||||
每个目标卡片 SHALL 展示目标名称、当前状态、类型标签、状态条和迷你耗时趋势线。
|
||||
@@ -61,3 +73,14 @@ Dashboard SHALL 使用固定宽度的卡片配合响应式网格布局。
|
||||
#### Scenario: 卡片点击打开详情
|
||||
- **WHEN** 用户点击某个目标卡片
|
||||
- **THEN** 系统 SHALL 打开该目标的详情模态框
|
||||
|
||||
### Requirement: 平滑过渡动画
|
||||
卡片 SHALL 具有平滑的交互过渡动画效果。
|
||||
|
||||
#### Scenario: 卡片悬停动画
|
||||
- **WHEN** 鼠标悬停在卡片上
|
||||
- **THEN** 卡片 SHALL 平滑过渡显示上浮效果(阴影加深、轻微上移),过渡时长 0.3s
|
||||
|
||||
#### Scenario: 布局变化过渡
|
||||
- **WHEN** 视口宽度变化导致卡片重新排列
|
||||
- **THEN** 卡片位置变化 SHALL 有平滑的过渡动画
|
||||
|
||||
@@ -22,9 +22,8 @@ body {
|
||||
}
|
||||
|
||||
.dashboard {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 32px 24px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.dashboard-header {
|
||||
@@ -54,18 +53,28 @@ body {
|
||||
}
|
||||
|
||||
.summary-cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 16px;
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.summary-card {
|
||||
width: 280px;
|
||||
flex-shrink: 0;
|
||||
padding: 20px;
|
||||
border: 1px solid rgba(49, 83, 126, 0.12);
|
||||
border-radius: 16px;
|
||||
background: rgba(255, 255, 255, 0.85);
|
||||
box-shadow: 0 4px 16px rgba(34, 57, 91, 0.08);
|
||||
transition:
|
||||
transform 0.3s ease,
|
||||
box-shadow 0.3s ease;
|
||||
}
|
||||
|
||||
.summary-card:hover {
|
||||
box-shadow: 0 8px 32px rgba(34, 57, 91, 0.12);
|
||||
transform: translateY(-4px);
|
||||
}
|
||||
|
||||
.card-value {
|
||||
@@ -118,12 +127,14 @@ body {
|
||||
}
|
||||
|
||||
.card-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.target-card {
|
||||
width: 280px;
|
||||
flex-shrink: 0;
|
||||
padding: 16px;
|
||||
border: 1px solid rgba(49, 83, 126, 0.12);
|
||||
border-radius: 12px;
|
||||
@@ -131,8 +142,8 @@ body {
|
||||
box-shadow: 0 2px 8px rgba(34, 57, 91, 0.06);
|
||||
cursor: pointer;
|
||||
transition:
|
||||
box-shadow 0.15s,
|
||||
transform 0.15s;
|
||||
box-shadow 0.3s ease,
|
||||
transform 0.3s ease;
|
||||
}
|
||||
|
||||
.target-card:hover {
|
||||
@@ -566,18 +577,6 @@ body {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.summary-cards {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
.card-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.target-card {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
width: 95vw;
|
||||
max-height: 90vh;
|
||||
|
||||
Reference in New Issue
Block a user