315 lines
9.0 KiB
Markdown
315 lines
9.0 KiB
Markdown
# 前端布局指南
|
||
|
||
本项目后台页面默认遵循“单屏工作区”布局规范。目标不是让页面永远不溢出,而是确保在常见桌面视口下:
|
||
|
||
- 页面主结构能在一屏内看清
|
||
- 用户能同时看到页头、摘要区和主工作区
|
||
- 超出的内容在模块内部滚动,而不是把整页纵向撑爆
|
||
|
||
当前推荐参考实现:
|
||
|
||
- [frontend/src/admin/pages/PlainResourcePages.tsx](/home/ray/dev/linkong/planet/frontend/src/admin/pages/PlainResourcePages.tsx)
|
||
- [frontend/src/admin/pages/DataList.tsx](/home/ray/dev/linkong/planet/frontend/src/admin/pages/DataList.tsx)
|
||
- [frontend/src/index.css](/home/ray/dev/linkong/planet/frontend/src/index.css)
|
||
|
||
## 核心原则
|
||
|
||
### 1. 页面优先保证一屏工作区
|
||
|
||
管理页默认采用:
|
||
|
||
- 页头:标题、说明、主要操作
|
||
- 主工作区:统计卡、表格、图表、列表、标签页
|
||
|
||
推荐结构:
|
||
|
||
```tsx
|
||
<AdminLayout>
|
||
<div className="page-shell">
|
||
<div className="page-shell__header">...</div>
|
||
<div className="page-shell__body">...</div>
|
||
</div>
|
||
</AdminLayout>
|
||
```
|
||
|
||
页面总高度应被限制在 `AdminLayout` 内容区内,而不是继续让整个页面自然向下增长。
|
||
|
||
### 2. 滚动优先发生在模块内部
|
||
|
||
如果表格、日志、长列表、图表明细超出空间:
|
||
|
||
- 让卡片内部滚动
|
||
- 让表格内部滚动
|
||
- 让标签页内容区内部滚动
|
||
|
||
不要默认依赖整个页面滚动去“解决”空间问题。
|
||
|
||
### 3. 主工作区必须拿到主要空间
|
||
|
||
页面里最重要的模块必须是视觉和空间上的主角。通常应保证:
|
||
|
||
- 页头始终可见
|
||
- 摘要区高度被控制
|
||
- 主表格 / 主图表 / 主分析区占据 50% 以上可视高度
|
||
|
||
如果一个页面有多个大模块,优先顺序是:
|
||
|
||
1. 先压缩说明区和摘要区
|
||
2. 再把次级模块收进标签页或切换视图
|
||
3. 最后才考虑继续增加整页滚动
|
||
|
||
### 4. 小屏幕和高缩放必须进入紧凑模式
|
||
|
||
在窗口高度较低、宽度较窄、或系统缩放较高时,应主动切换紧凑布局,例如:
|
||
|
||
- 缩小卡片 padding
|
||
- 缩小表头和单元格间距
|
||
- 将摘要区改为更紧凑的单行/横向滚动布局
|
||
- 将次级模块移入标签页、抽屉、折叠区
|
||
|
||
紧凑模式的目标是保持可用,不是单纯把文字和控件一股脑缩小。
|
||
|
||
### 5. overflow 责任必须明确
|
||
|
||
页面中的大块内容必须明确:
|
||
|
||
- 谁负责占满剩余高度
|
||
- 谁负责裁剪
|
||
- 谁负责滚动
|
||
|
||
常见要求:
|
||
|
||
- 父容器链路需要 `min-height: 0`
|
||
- 工作区容器通常需要 `display: flex`
|
||
- 真正的滚动节点要显式 `overflow: auto`
|
||
|
||
### 6. 卡片不能被压到不可读
|
||
|
||
历史上我们反复踩到的问题不是“没有滚动条”,而是:
|
||
|
||
- 卡片被 `flex` 压缩得只剩一小条可视区域
|
||
- 文字能渲染,但读不完整
|
||
- 内容其实存在,却被 `overflow: hidden` 裁掉
|
||
|
||
因此后续约束是:
|
||
|
||
- 先保证卡片有可读的最小高度
|
||
- 如果继续压缩会影响阅读,就切换成内部滚动
|
||
- 不要为了“保持一屏”而把正文、表格、描述区压成无法阅读的条状区域
|
||
|
||
### 7. Tabs 不是天然安全的布局容器
|
||
|
||
历史上 Tabs 相关回归非常多,典型问题包括:
|
||
|
||
- 隐藏 tab pane 因为自定义 `display: flex` 而重新露出来
|
||
- 所有 tab 被强行套用同一套高度/overflow 规则
|
||
- 表格 tab 能工作,但 markdown / help / diagnostics tab 被压坏
|
||
|
||
因此约束是:
|
||
|
||
- `Tabs` 里的每类内容都要单独定义自己的布局策略
|
||
- 表格 tab 可以是“固定高度 + 内部滚动”
|
||
- 文档/Markdown tab 更适合“tab pane 自身滚动 + 内容正常文档流”
|
||
- 如果覆盖组件库样式,必须同时检查 hidden 状态是否仍然成立
|
||
|
||
### 8. 摘要区优先进入紧凑模式,而不是挤压正文
|
||
|
||
历史经验表明,最容易被误处理的是顶部摘要卡:
|
||
|
||
- 它们经常为了“都放下”被强行压窄
|
||
- 然后正文、表格、AI 结果区一起失去主空间
|
||
|
||
后续统一约束:
|
||
|
||
- 小屏或高缩放时,摘要卡优先:
|
||
- 降低 padding
|
||
- 改成横向滚动
|
||
- 改成更紧凑的网格
|
||
- 不要优先牺牲主工作区的可视面积
|
||
|
||
### 9. 长文档类内容优先保证阅读体验
|
||
|
||
像下面这些内容,不能直接套用“表格工作区”的逻辑:
|
||
|
||
- AI 简报
|
||
- 运行日志
|
||
- 原始 JSON
|
||
- 帮助说明
|
||
- 多段描述性文本
|
||
|
||
这些区域应该优先满足:
|
||
|
||
- 标题和元信息稳定可见
|
||
- 正文有明确的最小可读高度
|
||
- 正文滚动策略单独定义
|
||
- 支持 Markdown 表格、分隔线、引用、代码块等结构
|
||
|
||
### 10. 高度关键路径要少包一层
|
||
|
||
历史上不少滚动问题不是组件本身错,而是多包了一层之后:
|
||
|
||
- 高度链路断掉
|
||
- `min-height: 0` 没传下去
|
||
- `overflow` 责任被吃掉
|
||
|
||
因此:
|
||
|
||
- 对高度关键区域,优先使用最直接的 DOM 结构
|
||
- 使用 `Space`、额外包装 `div`、第三方布局容器时,要确认它们不会改变滚动和高度语义
|
||
- 如果一个区域已经出现“内容明明有,但只剩一条缝”,优先怀疑中间包装层
|
||
|
||
## 历史坑位总结
|
||
|
||
从 Earth、Playground、BGP、DataSources 这些页面的 bugfix 可以归纳出几类高频坑:
|
||
|
||
### 1. 用 `overflow: hidden` 掩盖布局问题
|
||
|
||
表面上看页面“整齐了”,实际上会导致:
|
||
|
||
- 内容被裁掉
|
||
- tab 内容只剩一条缝
|
||
- 面板明明渲染成功,但用户看不见
|
||
|
||
正确做法:
|
||
|
||
- 让真正的内容节点滚动
|
||
- 不要让上层容器无差别裁剪所有子内容
|
||
|
||
### 2. 把所有 tab 当成同一种内容
|
||
|
||
表格、Markdown、帮助卡、日志流的空间需求完全不同。
|
||
|
||
正确做法:
|
||
|
||
- 表格:固定工作区 + 内部滚动
|
||
- 文档:普通流式内容 + pane 级滚动
|
||
- 侧边说明:内容驱动高度,不强行拉满
|
||
|
||
### 3. 只做视觉缩小,不做空间重分配
|
||
|
||
这会导致:
|
||
|
||
- 卡片文字被截断
|
||
- 表格只剩 1 到 2 行
|
||
- 按钮和筛选区挤成一团
|
||
|
||
正确做法:
|
||
|
||
- 紧凑模式优先重排
|
||
- 横向滚动摘要区
|
||
- 折叠/收纳次级模块
|
||
|
||
### 4. 父容器高度链不完整
|
||
|
||
这是最常见的内部滚动失效原因。
|
||
|
||
检查顺序:
|
||
|
||
1. 外层是否真的有确定高度
|
||
2. flex 父容器是否带了 `min-height: 0`
|
||
3. 真正滚动节点是否明确 `overflow: auto`
|
||
4. 中间包装层是否偷偷改了布局语义
|
||
|
||
当前 Admin 和 Docs 根 shell 依赖 `html` / `body` / `#root` 的 `height: 100%`
|
||
链路。不要在这些嵌入式工作区根容器上重新写精确 `100vh` / `100vw`;
|
||
弹窗、浮层和窄屏安全边界可以继续使用 `calc(100vh - ...)` 作为最大尺寸约束。
|
||
|
||
### 5. UI 状态和显示状态不同步
|
||
|
||
Earth 相关改动里反复出现:
|
||
|
||
- 图层隐藏了,但 hover/lock 还在
|
||
- tooltip 还在显示旧对象
|
||
- legend 没跟着切换
|
||
|
||
这类约束同样适用于后台页面:
|
||
|
||
- 被隐藏、卸载、切换出视图的内容,不应继续保留活跃交互状态
|
||
|
||
## 推荐实现模式
|
||
|
||
### 页面骨架
|
||
|
||
优先复用项目里已有的通用结构:
|
||
|
||
- `.dashboard-content-inner`
|
||
- `.page-shell`
|
||
- `.page-shell__header`
|
||
- `.page-shell__body`
|
||
- `.table-scroll-region`
|
||
|
||
不要每个页面都重新发明一套完全不同的高度和滚动语义。
|
||
|
||
### 表格工作区
|
||
|
||
推荐模式:
|
||
|
||
```tsx
|
||
<Card>
|
||
<div className="table-scroll-region" ref={tableRegionRef}>
|
||
<Table
|
||
pagination={false}
|
||
scroll={{ x: 1200, y: tableHeight }}
|
||
/>
|
||
</div>
|
||
</Card>
|
||
```
|
||
|
||
要求:
|
||
|
||
- 表格尽量在卡片内部滚动
|
||
- `scroll.y` 应来自实际可用高度估算,而不是完全静态的魔法数字
|
||
- 父容器链路要保证 header、body、content 的 overflow 都在表格内部闭合
|
||
|
||
### 多模块页面
|
||
|
||
如果一个页面同时有:
|
||
|
||
- 摘要卡
|
||
- 表格
|
||
- 异常明细
|
||
- 最近事件
|
||
|
||
不建议简单纵向堆叠全部模块。优先使用:
|
||
|
||
- 顶部摘要 + 底部单一主工作区
|
||
- 标签页切换多个次级数据视图
|
||
- 左右分栏,并保证每栏内部独立滚动
|
||
|
||
## 不推荐的做法
|
||
|
||
以下模式默认视为不符合本项目页面规范:
|
||
|
||
- 依赖整页纵向滚动来显示主要工作区
|
||
- 一个页面纵向堆 3 到 4 个大卡片,每个都想完整展示
|
||
- 表格没有内部滚动,导致缩放后只能看到 1 到 2 行数据
|
||
- 父容器缺少 `min-height: 0`,导致内部滚动失效
|
||
- 只做视觉缩小,不处理真正的空间分配
|
||
|
||
## 页面验收检查清单
|
||
|
||
提交前至少检查:
|
||
|
||
- 页头、摘要区、主工作区能否同时出现
|
||
- 主工作区是否拿到了页面中最多的高度
|
||
- 表格或明细溢出时,滚动条是否出现在模块内部
|
||
- 卡片是否被压缩到文字显示不完整;如果会,是否已经切换为内部滚动
|
||
- 浏览器缩放到 `125%` / `150%` 时是否仍可用
|
||
- 低高度窗口下是否还保有合理的可见内容行数
|
||
- Tabs、Card、Table 在 overflow 时是否仍可操作
|
||
- 非表格 tab(Markdown、帮助说明、日志)是否有独立且合理的滚动策略
|
||
|
||
## 落地顺序
|
||
|
||
后续新增或重构后台页时,优先按这个顺序设计:
|
||
|
||
1. 先定义主工作区
|
||
2. 再确定哪些模块必须常驻可见
|
||
3. 最后再做样式和视觉层次
|
||
|
||
简单说:
|
||
|
||
- 先保证空间分配正确
|
||
- 再处理滚动边界
|
||
- 最后再做美化
|