@tfdesign/b-end 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AI_READ_FIRST.md +131 -0
- package/LICENSE +21 -0
- package/README.md +353 -0
- package/package.json +67 -0
- package/scripts/check-tfds-contract.mjs +334 -0
- package/scripts/check-tfds-integration.mjs +263 -0
- package/scripts/postinstall-cursor-skill.mjs +382 -0
- package/scripts/setup.mjs +520 -0
- package/skills/tfds/CHECKLIST.md +205 -0
- package/skills/tfds/COMMON_FAILURES.md +238 -0
- package/skills/tfds/DESIGN_PRINCIPLES.md +477 -0
- package/skills/tfds/GLOBAL_DESIGN_RULES.md +636 -0
- package/skills/tfds/LAYOUT_RECIPES.md +140 -0
- package/skills/tfds/LAYOUT_RULES.md +1355 -0
- package/skills/tfds/PAGE_ARCHETYPES.md +201 -0
- package/skills/tfds/SKILL.md +188 -0
- package/skills/tfds/components.index.json +7305 -0
- package/skills/tfds/components.summary.json +1809 -0
- package/src/_b_end_runtime/components/AiSuggestionShared.jsx +166 -0
- package/src/_b_end_runtime/components/Avatar.jsx +325 -0
- package/src/_b_end_runtime/components/Avatar.tokens.js +76 -0
- package/src/_b_end_runtime/components/AvatarGridPreview.jsx +56 -0
- package/src/_b_end_runtime/components/AvatarGroup.jsx +80 -0
- package/src/_b_end_runtime/components/AvatarGroup.tokens.js +28 -0
- package/src/_b_end_runtime/components/Button.jsx +144 -0
- package/src/_b_end_runtime/components/Button.tokens.js +90 -0
- package/src/_b_end_runtime/components/Card.jsx +460 -0
- package/src/_b_end_runtime/components/Card.tokens.js +124 -0
- package/src/_b_end_runtime/components/CardPreview.jsx +51 -0
- package/src/_b_end_runtime/components/ChatBubble.jsx +384 -0
- package/src/_b_end_runtime/components/ChatBubble.tokens.js +60 -0
- package/src/_b_end_runtime/components/ChatBubblePreview.jsx +129 -0
- package/src/_b_end_runtime/components/ChatInput.jsx +1399 -0
- package/src/_b_end_runtime/components/ChatInput.tokens.js +75 -0
- package/src/_b_end_runtime/components/ChatMessage.jsx +2215 -0
- package/src/_b_end_runtime/components/ChatMessage.tokens.js +257 -0
- package/src/_b_end_runtime/components/ChatMessagePreview.jsx +388 -0
- package/src/_b_end_runtime/components/Checkbox.jsx +317 -0
- package/src/_b_end_runtime/components/Checkbox.tokens.js +59 -0
- package/src/_b_end_runtime/components/ConversationList.jsx +1264 -0
- package/src/_b_end_runtime/components/ConversationList.tokens.js +135 -0
- package/src/_b_end_runtime/components/ConversationListPreview.jsx +108 -0
- package/src/_b_end_runtime/components/CustomerServiceWorkspaceFrame.jsx +324 -0
- package/src/_b_end_runtime/components/CustomerServiceWorkspaceFrame.tokens.js +69 -0
- package/src/_b_end_runtime/components/DatePicker.jsx +739 -0
- package/src/_b_end_runtime/components/DatePicker.tokens.js +99 -0
- package/src/_b_end_runtime/components/Empty.jsx +141 -0
- package/src/_b_end_runtime/components/Empty.tokens.js +40 -0
- package/src/_b_end_runtime/components/Form.jsx +609 -0
- package/src/_b_end_runtime/components/Form.tokens.js +77 -0
- package/src/_b_end_runtime/components/FormFieldStack.jsx +123 -0
- package/src/_b_end_runtime/components/FormFieldStack.tokens.js +12 -0
- package/src/_b_end_runtime/components/FormTitle.jsx +119 -0
- package/src/_b_end_runtime/components/FormTitle.tokens.js +87 -0
- package/src/_b_end_runtime/components/FullScreenPage.jsx +97 -0
- package/src/_b_end_runtime/components/FullScreenPage.tokens.js +19 -0
- package/src/_b_end_runtime/components/Icon.jsx +172 -0
- package/src/_b_end_runtime/components/Icon.tokens.js +26 -0
- package/src/_b_end_runtime/components/IconGridPreview.jsx +277 -0
- package/src/_b_end_runtime/components/InfoDisplayPanel.jsx +620 -0
- package/src/_b_end_runtime/components/InfoDisplayPanel.tokens.js +71 -0
- package/src/_b_end_runtime/components/InfoDisplayPanelPreview.jsx +133 -0
- package/src/_b_end_runtime/components/Input.jsx +258 -0
- package/src/_b_end_runtime/components/Input.tokens.js +68 -0
- package/src/_b_end_runtime/components/InputNumber.jsx +242 -0
- package/src/_b_end_runtime/components/InputNumber.tokens.js +55 -0
- package/src/_b_end_runtime/components/Modal.jsx +155 -0
- package/src/_b_end_runtime/components/Modal.tokens.js +73 -0
- package/src/_b_end_runtime/components/NavBar.jsx +842 -0
- package/src/_b_end_runtime/components/NavBar.tokens.js +97 -0
- package/src/_b_end_runtime/components/NavBarPreview.jsx +11 -0
- package/src/_b_end_runtime/components/Radio.jsx +227 -0
- package/src/_b_end_runtime/components/Radio.tokens.js +59 -0
- package/src/_b_end_runtime/components/Select.jsx +766 -0
- package/src/_b_end_runtime/components/Select.tokens.js +99 -0
- package/src/_b_end_runtime/components/Sheet.jsx +132 -0
- package/src/_b_end_runtime/components/Sheet.tokens.js +61 -0
- package/src/_b_end_runtime/components/Slider.jsx +346 -0
- package/src/_b_end_runtime/components/Slider.tokens.js +47 -0
- package/src/_b_end_runtime/components/Switch.jsx +124 -0
- package/src/_b_end_runtime/components/Switch.tokens.js +38 -0
- package/src/_b_end_runtime/components/Table.jsx +1338 -0
- package/src/_b_end_runtime/components/Table.tokens.js +147 -0
- package/src/_b_end_runtime/components/TablePreview.jsx +599 -0
- package/src/_b_end_runtime/components/Tabs.jsx +149 -0
- package/src/_b_end_runtime/components/Tabs.tokens.js +102 -0
- package/src/_b_end_runtime/components/Tag.jsx +199 -0
- package/src/_b_end_runtime/components/Tag.tokens.js +171 -0
- package/src/_b_end_runtime/components/TagBar.jsx +1134 -0
- package/src/_b_end_runtime/components/TagBar.tokens.js +75 -0
- package/src/_b_end_runtime/components/TagGridPreview.jsx +23 -0
- package/src/_b_end_runtime/components/TagInput.jsx +382 -0
- package/src/_b_end_runtime/components/TagInput.tokens.js +52 -0
- package/src/_b_end_runtime/components/TextArea.jsx +363 -0
- package/src/_b_end_runtime/components/TextArea.tokens.js +65 -0
- package/src/_b_end_runtime/components/TimePicker.jsx +444 -0
- package/src/_b_end_runtime/components/TimePicker.tokens.js +77 -0
- package/src/_b_end_runtime/components/Toast.jsx +120 -0
- package/src/_b_end_runtime/components/Toast.tokens.js +146 -0
- package/src/_b_end_runtime/components/Tooltip.jsx +282 -0
- package/src/_b_end_runtime/components/Tooltip.tokens.js +48 -0
- package/src/_b_end_runtime/components/TooltipPreview.jsx +50 -0
- package/src/_b_end_runtime/components/Upload.jsx +455 -0
- package/src/_b_end_runtime/components/Upload.tokens.js +47 -0
- package/src/_b_end_runtime/components/avatar-assets/avatar-default.png +0 -0
- package/src/_b_end_runtime/components/avatar-group-assets/avatar-group-1.png +0 -0
- package/src/_b_end_runtime/components/avatar-group-assets/avatar-group-2.png +0 -0
- package/src/_b_end_runtime/components/avatar-group-assets/avatar-group-3.png +0 -0
- package/src/_b_end_runtime/components/avatar-group-assets/avatar-group-4.png +0 -0
- package/src/_b_end_runtime/components/avatar-group-assets/avatar-group-5.png +0 -0
- package/src/_b_end_runtime/components/empty-assets/administrator-1.svg +40 -0
- package/src/_b_end_runtime/components/empty-assets/administrator-2.svg +33 -0
- package/src/_b_end_runtime/components/empty-assets/construction.svg +33 -0
- package/src/_b_end_runtime/components/empty-assets/failure.svg +49 -0
- package/src/_b_end_runtime/components/empty-assets/idle.svg +34 -0
- package/src/_b_end_runtime/components/empty-assets/no-access.svg +36 -0
- package/src/_b_end_runtime/components/empty-assets/no-content.svg +77 -0
- package/src/_b_end_runtime/components/empty-assets/no-result.svg +61 -0
- package/src/_b_end_runtime/components/empty-assets/not-found.svg +46 -0
- package/src/_b_end_runtime/components/empty-assets/success.svg +38 -0
- package/src/_b_end_runtime/components/file-type-assets/batch-report.png +0 -0
- package/src/_b_end_runtime/components/file-type-assets/catcat.svg +21 -0
- package/src/_b_end_runtime/components/file-type-assets/code.png +0 -0
- package/src/_b_end_runtime/components/file-type-assets/conversation.png +0 -0
- package/src/_b_end_runtime/components/file-type-assets/document.png +0 -0
- package/src/_b_end_runtime/components/file-type-assets/feishu-card.png +0 -0
- package/src/_b_end_runtime/components/file-type-assets/feishu-sheet.png +0 -0
- package/src/_b_end_runtime/components/file-type-assets/feishu.png +0 -0
- package/src/_b_end_runtime/components/file-type-assets/image.png +0 -0
- package/src/_b_end_runtime/components/file-type-assets/index.js +105 -0
- package/src/_b_end_runtime/components/file-type-assets/knowledge.png +0 -0
- package/src/_b_end_runtime/components/file-type-assets/pdf.png +0 -0
- package/src/_b_end_runtime/components/file-type-assets/pe.png +0 -0
- package/src/_b_end_runtime/components/file-type-assets/strategy.png +0 -0
- package/src/_b_end_runtime/components/file-type-assets/table.png +0 -0
- package/src/_b_end_runtime/components/file-type-assets/webpage.png +0 -0
- package/src/_b_end_runtime/components/file-type-assets/xmind.png +0 -0
- package/src/_b_end_runtime/components/icons/icon-data.js +12496 -0
- package/src/_b_end_runtime/components/nav-bar-assets/bytehi-logo-mark.svg +21 -0
- package/src/_b_end_runtime/components/table-assets/avatar.png +0 -0
- package/src/_b_end_runtime/components/table-assets/button.png +0 -0
- package/src/_b_end_runtime/components/table-assets/icon-chevron-down.png +0 -0
- package/src/_b_end_runtime/components/table-cell-assets/avatar.png +0 -0
- package/src/_b_end_runtime/components/table-cell-assets/button.png +0 -0
- package/src/_b_end_runtime/components/table-cell-assets/checkbox.png +0 -0
- package/src/_b_end_runtime/components/table-cell-assets/icon-chevron-right.png +0 -0
- package/src/_b_end_runtime/components/table-cell-assets/icon.png +0 -0
- package/src/_b_end_runtime/components/table-cell-assets/semi-icons-handle.png +0 -0
- package/src/_b_end_runtime/components/table-cell-assets/semi-icons-tree-triangle-right.png +0 -0
- package/src/_b_end_runtime/components/table-cell-assets/switch.png +0 -0
- package/src/_b_end_runtime/components/tagShared.js +3 -0
- package/src/_b_end_runtime/components/team-avatar-assets/chengcheng-murphy.png +0 -0
- package/src/_b_end_runtime/components/team-avatar-assets/duan-ran.png +0 -0
- package/src/_b_end_runtime/components/team-avatar-assets/guo-zhezhi.png +0 -0
- package/src/_b_end_runtime/components/team-avatar-assets/li-siru.png +0 -0
- package/src/_b_end_runtime/components/team-avatar-assets/liu-delin.png +0 -0
- package/src/_b_end_runtime/components.js +3499 -0
- package/src/_b_end_runtime/index.js +9 -0
- package/src/_b_end_runtime/page-patterns/BasePageFramePattern.jsx +395 -0
- package/src/_b_end_runtime/page-patterns/ChatConversationPattern.jsx +989 -0
- package/src/_b_end_runtime/page-patterns/ChatHomePagePattern.jsx +281 -0
- package/src/_b_end_runtime/page-patterns/CopilotPagePattern.jsx +380 -0
- package/src/_b_end_runtime/page-patterns/CustomerServiceWorkspaceFramePattern.jsx +392 -0
- package/src/_b_end_runtime/page-patterns/IMConversationPattern.jsx +590 -0
- package/src/_b_end_runtime/page-patterns/McpManagementPage.jsx +237 -0
- package/src/_b_end_runtime/page-patterns/StrategyListPage.jsx +189 -0
- package/src/_b_end_runtime/page-patterns/TabTopBarListPage.jsx +594 -0
- package/src/_b_end_runtime/page-patterns/VariableManagementPage.jsx +87 -0
- package/src/_b_end_runtime/page-patterns/pageListShared.jsx +177 -0
- package/src/_b_end_runtime/patterns.js +428 -0
- package/src/_b_end_runtime/preview-registry.jsx +4719 -0
- package/src/_b_end_runtime/teamMembers.js +56 -0
- package/src/_b_end_runtime/tokens.js +500 -0
- package/src/index.d.ts +1073 -0
- package/src/index.js +52 -0
- package/theme.css +350 -0
|
@@ -0,0 +1,1355 @@
|
|
|
1
|
+
# TFDS 页面布局与宽高自适应规范
|
|
2
|
+
|
|
3
|
+
> **作用域**:所有 TFDS 业务页面的**外部框架**与**响应式行为**。本规范回答两个问题:
|
|
4
|
+
>
|
|
5
|
+
> 1. 页面骨架长什么样?(页框 / 内容区 / 卡片分层)
|
|
6
|
+
> 2. 浏览器窗口缩放/极宽/极窄时,页面要如何反应?(不溢出、不截断、不被撑歪)
|
|
7
|
+
>
|
|
8
|
+
> **优先级**(与 SKILL.md 一致):组件索引 > SKILL.md > 本文件 > GLOBAL_DESIGN_RULES.md > DESIGN_PRINCIPLES.md > 模型自身审美。
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 0.1 与 `LAYOUT_RECIPES.md` 的关系
|
|
13
|
+
|
|
14
|
+
生成整页时先读 `LAYOUT_RECIPES.md` 选择唯一主 recipe,再回到本文件拿骨架细节。
|
|
15
|
+
|
|
16
|
+
- `LAYOUT_RECIPES.md` 回答:这是 `base-management`、`chat-home`、`chat-conversation`、`copilot-workbench` 还是 `im-thread`?外层是灰底直排还是白色工作卡?
|
|
17
|
+
- 本文件回答:根容器、`main`、白卡、滚动层、双栏、多栏和响应式具体怎么写?
|
|
18
|
+
- 若 AI 入口页命中 `chat-home`,不得用本文件的通用白卡工作区规则覆盖入口页的“灰底直排”规则。
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## 0. B 端响应式策略(统一决策)
|
|
23
|
+
|
|
24
|
+
TFDS 是**桌面优先**的 B 端工具产品,响应式策略和 C 端不同:
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
| 视口宽度 | 行为 |
|
|
28
|
+
| ------------- | ---------------------------------- |
|
|
29
|
+
| ≥ **1280px** | 完整布局(首选目标) |
|
|
30
|
+
| 1024 - 1280px | 完整布局,必要时折叠次要侧栏 |
|
|
31
|
+
| 768 - 1024px | 双栏可降级为「侧栏抽屉化」;表格保留横向滚动 |
|
|
32
|
+
| < 768px | **不主动适配**(B 端工具页极少需要手机访问),允许整页横向滚动 |
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
⛔ **禁止**给 B 端页加 `@media (max-width: 480px)` 的移动端样式(除非用户明确要求)。
|
|
36
|
+
✅ B 端首要任务是**桌面宽屏可用**,不是 mobile-first。
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## 0.5 滚动模型决策树(生成页面**前**必判)
|
|
41
|
+
|
|
42
|
+
> **整页滚动**和**面板内滚动**是两种完全不同的 UX 模型,绝不通用。
|
|
43
|
+
> 选错一次,就会出现"调参数得滚下去看结果、写 prompt 得滚回去看输入"这种破坏对照工作流的问题。
|
|
44
|
+
|
|
45
|
+
**决策步骤**:先判断"用户在这一屏需要做的事是堆叠浏览,还是多面板对照工作"。
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
| 信号 | 用户的认知模型 | 滚动策略 | 用哪个骨架 |
|
|
49
|
+
| ------------------------------------------------------------------- | ----------- | ----------------------- | ------------------------------- |
|
|
50
|
+
| **页面使用 `NavBar` 作为站点级导航** | 平台壳 / 模块工作区 | **左侧固定,右侧内部滚 / 分区滚** | § 1.1 + § 12.6 |
|
|
51
|
+
| **多个独立工作区面板共存**(Prompt 编辑 + 参数 + 运行结果 / 输入 + 输出 / 代码 + 预览 / A/B 对比) | IDE / 调试器型 | **整页不滚**,每张面板独立全高、内部各自滚 | § 3.4 + § 12.7 三栏 Playground 骨架 |
|
|
52
|
+
| 列表 / 表格 / IM / 编辑器 单一主内容 | 单焦点工作 | 内容区内部滚 | § 12.1 / § 12.3 / § 12.5 |
|
|
53
|
+
| 双栏:左导航 + 右内容 | 浏览 + 操作 | 右侧内部滚 | § 3.1 / § 12.2 |
|
|
54
|
+
| **Dashboard / 信息聚合首页**(KPI + 入口卡 + 简介块,纯堆叠浏览,**没有**面板内编辑/输入/对照工作流) | 堆叠浏览 | **整页滚** | § 12.4(仅此场景) |
|
|
55
|
+
| 长表单 / 详情 / 长文档 | 通读 + 偶尔编辑 | 整页滚或单卡内滚 | § 12.5 + § 6.2 |
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
**Playground / 多面板触发关键词**(命中任一即走 §3.4 / §12.7,**不**走 §12.4):
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
playground / 调试 / 试验 / 实验 / IDE / 多面板 /
|
|
62
|
+
prompt editor / prompt workspace / runtime / run result /
|
|
63
|
+
模型参数 + 输入区 + 结果区 / 编辑器 + 预览 / 输入 + 输出 / A/B 对比
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
⚠️ **典型误判**:Model Playground、Prompt Editor、API Playground、规则调试器,**不是** Dashboard。它们看着像"多张卡片堆一起",但语义是 IDE,必须用 §3.4。
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## 0.8 学习与强化来源(最高优先级样例:基础页面框架 + 6 个页面示例)
|
|
71
|
+
|
|
72
|
+
> 本文件的目标不是让你“凭规则脑补布局”,而是让你**直接对齐一套可运行的 B 端样例**,避免出现“宽高没撑满、卡片不齐、滚动层错位、组件基线不对齐”这类基础 UI 问题。
|
|
73
|
+
|
|
74
|
+
当你不确定某个布局细节(外框间距、白卡对齐、双栏分割、表格如何撑满、哪里滚动)时,**不要凭感觉**,直接对照以下“规范源”学习:
|
|
75
|
+
|
|
76
|
+
- **基础页面框架(页面模式 / page-pattern)**:`@tf-designsystem/b-end` 的 `BasePageFramePattern`
|
|
77
|
+
- 源文件:`packages/tfds-b-end/src/_b_end_runtime/page-patterns/BasePageFramePattern.jsx`
|
|
78
|
+
- 作用:提供“左 NavBar + 右主区”的最基础骨架,并承载 6 个页面示例的切换。
|
|
79
|
+
- **页面示例 0~5(可复制的模板 / page-template)**:用于强化“列表页/双白卡/顶部胶囊 Tab/空状态”的对齐方式
|
|
80
|
+
- 示例 0:`StrategyListPage`(筛选 + 树形可展开列表 + 分页)
|
|
81
|
+
- 示例 1:`VariableManagementPage`(单白卡:标题/筛选/表格;表格撑满 + 内滚)
|
|
82
|
+
- 示例 2:`McpManagementPage`(双白卡:左侧可拖拽辅助大卡 + 右主白卡弹性撑满)
|
|
83
|
+
- 示例 3:`TabTopBarListPage`(顶部胶囊 Tab 标题栏 + 白卡内容区;右侧详情面板可收起)
|
|
84
|
+
- 示例 4:`NoAccessPage`(空状态 + 行动按钮;白卡居中对齐)
|
|
85
|
+
- 示例 5:`ConstructionPage`(建设中空状态;白卡居中对齐)
|
|
86
|
+
- **列表页共享结构(避免手搓不齐)**:`pageListShared.jsx`
|
|
87
|
+
- 源文件:`packages/tfds-b-end/src/_b_end_runtime/page-patterns/pageListShared.jsx`
|
|
88
|
+
- 作用:沉淀“标题栏 + 筛选栏 + 表格列宽”的统一排布与对齐节奏。
|
|
89
|
+
|
|
90
|
+
### 0.8.1 从样例提炼出的“页面撑满 + 对齐”硬规则(高频 UI Bug 的根因清单)
|
|
91
|
+
|
|
92
|
+
只要你遇到下面任意现象,就按这张表回到样例结构做对照修复:
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
| 现象(你看到的 UI 问题) | 99% 根因 | 直接修复到的目标结构(不要发明新结构) | 首选对照样例 |
|
|
96
|
+
| --------------------------- | -------------------------------------------- | ---------------------------------- | -------------------------------------------- |
|
|
97
|
+
| NavBar 跑到页面顶部 / 与内容上下堆叠 | 把 `NavBar` 当普通内容块,根容器仍写成 `flex-col` | §1.1 的左侧固定 app shell 骨架 | `BasePageFramePattern` + §12.6 |
|
|
98
|
+
| 页面没撑满高度,出现 body 滚动 / 顶底栏被滚走 | 根容器没用 `h-dvh + overflow-hidden`;滚动层放错了 | §1 根容器骨架(根不滚,滚动下沉到内容层) | `BasePageFramePattern` + `TabTopBarListPage` |
|
|
99
|
+
| 页面左右没撑满或两侧大空白 | `max-w-`* / `mx-auto` / `px-6` 误用在 `main` 外框 | §1.2 三层间距金字塔(`main p-4` + `gap-2`) | `LAYOUT_RULES §1.2` + `McpManagementPage` |
|
|
100
|
+
| 双栏不齐 / 左侧被压缩 / 右侧溢出 | 横向 flex 漏 `min-w-0`;固定侧没写 `shrink-0 w-[Npx]` | §3.1 / §3.2 双栏骨架 | `McpManagementPage` |
|
|
101
|
+
| 表格不撑满、下方空白或撑到很高 | 表格外层缺 `flex-1 min-h-0`(没给高度边界) | §4.1 表格滚动骨架(表格层内部滚) | `VariableManagementPage` |
|
|
102
|
+
| 卡片内容溢出把整页撑开 | 卡片正文层没写 `flex-1 min-h-0` 或滚动没下沉 | §3.4 / §3.5(标题固定,正文承担滚动) | `CreateQAContent`(BasePageFramePattern 内) |
|
|
103
|
+
| 组件基线不齐(标题、筛选、表格上沿对不齐) | 乱用 margin/padding;没有“外框/白卡/白卡内容”三层节奏 | 统一按:外框 `p-4`、白卡间 `gap-2`、白卡内 `p-6` | `pageListShared.jsx`(节奏参考) |
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
> ⚠️ 说明:样例文件里可能存在“为了 demo 嵌入 NavBar 框架而写的局部 margin/padding”。**你在生成新页面时,不要复制这些零散 margin**;只复制“骨架关系”(`flex-1 / min-h-0 / min-w-0 / overflow-`*)与“三层间距金字塔”的层级节奏。
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## 1. 页面根容器(最外层)
|
|
111
|
+
|
|
112
|
+
**所有**新生成的页面,最外层根容器**必须**满足:
|
|
113
|
+
|
|
114
|
+
```jsx
|
|
115
|
+
<div
|
|
116
|
+
className="
|
|
117
|
+
h-dvh /* 高度撑满视口(动态视口,比 h-screen 更适配移动端浏览器栏) */
|
|
118
|
+
w-full min-w-0 max-w-full /* 宽度自适应,禁止写死 */
|
|
119
|
+
flex flex-col /* 纵向布局,子项垂直排列 */
|
|
120
|
+
overflow-hidden /* 根容器永远不滚动!滚动交给内部某一层 */
|
|
121
|
+
"
|
|
122
|
+
style={{ background: 'var(--color-blueGrey-200)' }}
|
|
123
|
+
>
|
|
124
|
+
{/* 顶部固定区(如 Topbar) — shrink-0 */}
|
|
125
|
+
{/* 中部弹性区 — flex-1 min-h-0 + 内部滚动 */}
|
|
126
|
+
{/* 底部固定区(如 ChatInput / 工具条)— shrink-0 */}
|
|
127
|
+
</div>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
⛔ **禁止**:
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
| ❌ Bad | 后果 |
|
|
134
|
+
| -------------------------------------- | ---------------------------- |
|
|
135
|
+
| `<div className="min-h-screen">` | 浏览器 body 滚动 → 输入框被挤出视口、整页上下滚 |
|
|
136
|
+
| `<div className="w-[1440px]">` | 写死宽度 → 小屏出现横滚 |
|
|
137
|
+
| `<div className="overflow-auto">` 在最外层 | 滚动条出现在最外层,吸顶/吸底失效 |
|
|
138
|
+
| 漏写 `min-w-0` | 子项内容溢出会把页面撑出浏览器宽度 |
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
⚠️ **页面根容器无外圈圆角/描边**:灰底必须**贴浏览器边缘**展示,不要 `rounded-[12px]` 包整页。
|
|
142
|
+
|
|
143
|
+
## 1.1 NavBar 左侧固定 App Shell(高优先级例外)
|
|
144
|
+
|
|
145
|
+
> `NavBar` 不是普通内容模块,而是平台级站点导航锚点。
|
|
146
|
+
> **只要页面使用 `NavBar`,就默认进入 app shell 模式**:左侧固定导航 + 右侧弹性主工作区。
|
|
147
|
+
|
|
148
|
+
### NavBar 命中时的一级骨架铁律
|
|
149
|
+
|
|
150
|
+
- ✅ 页面根容器优先使用 `flex flex-row`,不要继续沿用通用 `flex-col` 页面壳
|
|
151
|
+
- ✅ `NavBar` 永远放在最左侧,并作为 `shrink-0` 固定区存在
|
|
152
|
+
- ✅ 右侧主内容区必须是 `flex-1 min-w-0 flex flex-col overflow-hidden`
|
|
153
|
+
- ✅ `NavBar` 属于平台壳层,不进入白卡,不进入顶部 header,不与主内容上下堆叠
|
|
154
|
+
- ✅ 只有用户**明确指定**“顶部导航”或“移动端降级”时,才允许偏离左侧固定模式
|
|
155
|
+
|
|
156
|
+
**标准骨架**:
|
|
157
|
+
|
|
158
|
+
```jsx
|
|
159
|
+
<div
|
|
160
|
+
className="h-dvh w-full min-w-0 flex flex-row overflow-hidden"
|
|
161
|
+
style={{ background: 'var(--color-blueGrey-200)' }}
|
|
162
|
+
>
|
|
163
|
+
<NavBar className="shrink-0" />
|
|
164
|
+
|
|
165
|
+
<div className="flex-1 min-w-0 flex flex-col overflow-hidden">
|
|
166
|
+
{/* 右侧页面 header / Topbar / 白卡工作区都放这里 */}
|
|
167
|
+
</div>
|
|
168
|
+
</div>
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
⛔ **禁止**:
|
|
172
|
+
|
|
173
|
+
- `<div className="flex flex-col"><NavBar /> <main>...</main></div>`
|
|
174
|
+
- `<section style={{ background: 'var(--color-surface)' }}><NavBar /></section>`
|
|
175
|
+
- 先搭一个纵向页面,再把 `NavBar` 当普通块塞到顶部
|
|
176
|
+
|
|
177
|
+
**生成口诀**:
|
|
178
|
+
|
|
179
|
+
> 用 `NavBar`,先搭壳;壳是横向,不是纵向。左边导航固定,右边主区弹性。
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## 1.2 灰底框架间距铁律(高频误用 · 与白卡内 padding 严格区分)
|
|
184
|
+
|
|
185
|
+
> **B 端最高频的宽度问题**:AI 把"白卡内 padding(24px)"当成"外框间距"用,导致卡片距浏览器边缘 24px 甚至更多,左右两侧出现大空白。
|
|
186
|
+
|
|
187
|
+
### 三层间距金字塔(只允许这三个值,不允许其他)
|
|
188
|
+
|
|
189
|
+
```
|
|
190
|
+
┌──────────────────────────────────────────────────────────┐
|
|
191
|
+
│ 浏览器可视区(灰底,background: var(--color-blueGrey-200)) │
|
|
192
|
+
│ ←── main wrapper: p-4(16px)—————————————————————— │
|
|
193
|
+
│ │ ┌──────────┐ ←─ gap-2(8px)→ ┌──────────┐ │ │
|
|
194
|
+
│ │ │ 白 卡 │ │ 白 卡 │ │ │
|
|
195
|
+
│ │ │ p-6 │ │ p-6 │ │ │
|
|
196
|
+
│ │ │ (24px内边距) │ (24px内边距) │ │
|
|
197
|
+
│ │ └──────────┘ └──────────┘ │ │
|
|
198
|
+
│ ←── main wrapper: p-4(16px)—————————————————————— │
|
|
199
|
+
└──────────────────────────────────────────────────────────┘
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
| 位置 | 值 | Tailwind | 含义 |
|
|
204
|
+
| ----------- | -------- | ------------------- | ----------------------------- |
|
|
205
|
+
| 灰底框架 → 白卡边缘 | **16px** | `p-4` / `px-4 py-4` | main 容器的 padding,白卡距浏览器边 16px |
|
|
206
|
+
| 白卡 ↔ 白卡 之间 | **8px** | `gap-2` | 多卡并列时的间距 |
|
|
207
|
+
| 白卡边缘 → 卡内内容 | **24px** | `p-6` | 白卡内部 padding,仅写在白卡 div 上 |
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
⛔ **禁止混淆(图同款误用)**:
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
| ❌ Bad | 后果 | ✅ Fix |
|
|
214
|
+
| ------------------------------------------------------------ | ------------------ | --------------------------------------------------------------------------------- |
|
|
215
|
+
| `<main className="... px-6 py-6">` | 白卡距浏览器边 24px,左右偏空 | `<main className="... p-4 gap-2">` |
|
|
216
|
+
| `<main className="... px-8">` | 白卡距浏览器边 32px,明显过宽 | `<main className="... p-4 gap-2">` |
|
|
217
|
+
| `<main className="... p-2">` | 白卡距浏览器边 8px,外框过窄 | `<main className="... p-4 gap-2">` |
|
|
218
|
+
| `<main>` 内再套 `<div className="max-w-[1440px] mx-auto px-8">` | 内容被居中收窄,左右出现大空白 | **禁止在 main 内套居中容器**,white cards 直接放 main 下 |
|
|
219
|
+
| `<main className="... p-4">` 但白卡自身再加 `mx-4` | 白卡有额外 margin,不紧贴框架 | 白卡 div 上**只写 `flex-1 min-h-0 min-w-0 flex flex-col overflow-hidden`,不加任何 margin** |
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
✅ **正确骨架(必须照此写)**:
|
|
223
|
+
|
|
224
|
+
```jsx
|
|
225
|
+
{/* main:p-4 撑出 16px 灰底框架,gap-2 是白卡之间的间距 */}
|
|
226
|
+
<main className="flex-1 min-h-0 flex flex-col p-4 gap-2 overflow-hidden w-full">
|
|
227
|
+
<header className="shrink-0 ...">...</header> {/* 页面 header,直接坐灰底 */}
|
|
228
|
+
|
|
229
|
+
{/* 白卡:不加 margin,flex-1 撑满剩余空间,内部 p-6 */}
|
|
230
|
+
<section
|
|
231
|
+
className="flex-1 min-h-0 flex flex-col overflow-hidden"
|
|
232
|
+
style={{ background: 'var(--color-surface)', borderRadius: 'var(--radius-lg)' }}
|
|
233
|
+
>
|
|
234
|
+
<div className="shrink-0 px-6 py-4">...</div> {/* 白卡内 header,p-6 */}
|
|
235
|
+
<div className="flex-1 min-h-0 overflow-y-auto px-6 pb-6">...</div> {/* 白卡内容,p-6 */}
|
|
236
|
+
</section>
|
|
237
|
+
</main>
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
⚠️ **宽度自检**:在生成完整页面后,检查代码中所有 `main` / `section` / 最外层白卡 div:
|
|
241
|
+
|
|
242
|
+
1. `main` 上的 padding 值**只能是 `p-4`(或 `px-4 pb-4`)**,不是 `p-2` / `p-6` / `p-8` / `px-[Npx]`
|
|
243
|
+
2. `main` 内**不允许**再套 `max-w-*` / `mx-auto` 的居中容器
|
|
244
|
+
3. 横向白卡 div 之间**只靠 `gap-2`**,白卡自身*不加 `mx-` 外边距**
|
|
245
|
+
4. 所有横向 `flex` 父容器补 `min-w-0`,纵向补 `min-h-0`——缺失会让某一层被内容撑开而不撑满
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## 1.5 白卡 vs 灰底裸区 决策(页面 header 专项 · 高频误用)
|
|
250
|
+
|
|
251
|
+
> TFDS 的视觉层次是「**灰底吸顶 + 白卡浮岛**」。白卡和灰底裸区**承担不同语义**,绝不通用。
|
|
252
|
+
|
|
253
|
+
### 1.5.1 白卡和灰底裸区分别承担什么
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
| 区域类型 | 视觉 | 语义 | 必须装什么 | ⛔ 不该装什么 |
|
|
257
|
+
| ---------------- | -------------------------------------------------------------------- | ------------------- | ---------------------------------------------------- | -------------------------------- |
|
|
258
|
+
| **白卡(工作区容器)** | `surface` 纯白底 + 12px 圆角,⛔**无 border / outline / ring**、⛔**无 shadow** | "用户在这里**做事**" | 列表 / 表格 / 对话流 / 参数表单 / 运行结果 / 编辑器 / 多步流程主体 | 页面标题 / 返回按钮 / 主次操作按钮 / 工具条 / 面包屑 |
|
|
259
|
+
| **灰底裸区(页面级元数据)** | 直接坐在 `blueGrey-200` 灰底上,无背景容器 | "用户**了解 / 切换**当前位置" | 页面标题 + 描述 + 主次操作(返回/导入/导出)/ 面包屑 / 顶部 Pill Tabs / 工具条 | 长内容 / 表格 / 对话流 / 表单字段 |
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
⚠️ **白卡描边专项铁律**(高频误用,详见 GLOBAL_DESIGN_RULES § 5.5):
|
|
263
|
+
|
|
264
|
+
- ⛔ **禁止**给最外层大白卡加 `border` / `outline` / `ring` / `borderColor`(任何描边都不允许)
|
|
265
|
+
- ✅ **保留**白卡**内部**所有 TFDS 组件自带的描边(Input / Select / TagInput / Tag / Tabs line / Table 行线 / 内嵌 grey Card 等——这些是控件功能边界,不是卡片层级边界)
|
|
266
|
+
- 判别口诀:背景色 `--color-surface`(白)→ **禁止** border;背景色 `--color-blueGrey-200`(灰,页面外框)→ **允许** border
|
|
267
|
+
|
|
268
|
+
**判别口诀**:问自己**这块内容用户是"做事"还是"看位置/切换/触发动作"**?前者包白卡,后者直接坐灰底。
|
|
269
|
+
|
|
270
|
+
### 1.5.2 标准结构(强约束)
|
|
271
|
+
|
|
272
|
+
```jsx
|
|
273
|
+
<div className="h-dvh w-full ..." style={{ background: 'var(--color-blueGrey-200)' }}>
|
|
274
|
+
<Topbar /> {/* 全局产品级顶导,独立组件,不在本节讨论 */}
|
|
275
|
+
|
|
276
|
+
<main className="flex-1 min-h-0 flex flex-col p-4 gap-2 overflow-hidden">
|
|
277
|
+
{/* ✅ 页面 header:直接坐灰底,无白卡背景 */}
|
|
278
|
+
<header className="shrink-0 px-2 pt-2 pb-1 flex items-start justify-between gap-3">
|
|
279
|
+
<div className="flex items-center gap-3 min-w-0">
|
|
280
|
+
<Button variant="outline-black" iconOnly icon={<Icon name="arrow-left-stroked" />} />
|
|
281
|
+
<FormTitle variant="form" title="Model Playground" titleSuffix={<Tag variant="success" size="m">TFDS</Tag>} description="保持原始工作区、中会话与右侧配置结果的位置关系。" showDescription />
|
|
282
|
+
</div>
|
|
283
|
+
<div className="shrink-0 flex items-center gap-2">
|
|
284
|
+
<Button variant="outline-black">查看源稿</Button>
|
|
285
|
+
<Button variant="primary">导入信息</Button>
|
|
286
|
+
</div>
|
|
287
|
+
</header>
|
|
288
|
+
|
|
289
|
+
{/* ✅ 内容主体:白卡承载真正的工作区 */}
|
|
290
|
+
<section
|
|
291
|
+
className="flex-1 min-h-0 flex flex-col overflow-hidden"
|
|
292
|
+
style={{ background: 'var(--color-surface)', borderRadius: 'var(--radius-lg)' }}
|
|
293
|
+
>
|
|
294
|
+
{/* Prompt Workspace / Conversation / Settings / Run Result */}
|
|
295
|
+
</section>
|
|
296
|
+
</main>
|
|
297
|
+
</div>
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### 1.5.3 Bad / Good 对照(图 1 同款误用)
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
| ❌ Bad | 后果 | ✅ Good |
|
|
304
|
+
| -------------------------------------------------------- | ------------------------- | -------------------------------------------------------------------------- |
|
|
305
|
+
| `<div bg=surface rounded-lg p-6>{标题 + 描述 + 操作按钮组}</div>` | 上下两张同等大小的白卡,主从混乱、气息感丢失 | `<header className="px-2 pt-2 flex justify-between">{...}</header>`(直接坐灰底) |
|
|
306
|
+
| 把"返回 + 面包屑 + 主操作"包白卡 | 工具条变成"内容卡",挤占真正内容区高度 | 工具条 `shrink-0` 直接坐灰底 |
|
|
307
|
+
| 把页面级 Pill Tabs(服务策略 / 批测&归因 / 运行数据)放进白卡 | Tabs 失去"页面级切换"语义,看着像卡内子页签 | Pill Tabs 直接坐灰底(参照 CopilotPagePattern 的 TopBarTabs) |
|
|
308
|
+
| 顶部"标题卡 + Tab 卡 + 内容卡"三张白卡叠加 | 页面变成乐高积木,没有视觉焦点 | 顶部一行灰底裸放 + 一张内容白卡 |
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
### 1.5.4 例外情况(极少数)
|
|
312
|
+
|
|
313
|
+
下面三种场景**允许**把 header 装进白卡,但要写注释说明理由,避免 AI 误学习:
|
|
314
|
+
|
|
315
|
+
1. **白卡内的子区块标题**:白卡内部分多个区块时,区块自己的小标题用 `<FormTitle variant="level-2" />` 写在卡内(这是卡内层级,不是页面级 header)
|
|
316
|
+
2. **沉浸式编辑器(§12.5 全白底页)**:整页白底,没有"灰底 vs 白卡"对比,header 直接坐白底(不是包白卡)
|
|
317
|
+
3. **Chat 列表项之类强制规整化的列表**:列表项用 Card 包,但列表项的 header 是卡内顶栏,不属于本节讨论
|
|
318
|
+
|
|
319
|
+
⛔ **禁止以"为了跟内容卡对齐 padding"为由把 header 包白卡**:直接给 header 写 `px-6` 跟内容卡 padding 对齐即可,不需要白卡背景。
|
|
320
|
+
|
|
321
|
+
### 1.5.5 AI 入口页(ChatHomePagePattern)背景与白卡边界(强约束)
|
|
322
|
+
|
|
323
|
+
AI 入口页的核心是「**从 0 发起任务**」:用户第一步是输入一句需求 / 搜索助手 / 从模板开始。它的视觉应该是**浅灰大背景承载整页氛围**,并在灰底上直接组织 Hero、筛选行与卡片网格。
|
|
324
|
+
⚠️ 这里最常见的误用是:为了“看起来更像后台”,在右侧内容区外又包了一整张白色大卡片容器,导致页面层级变成“灰底 → 大白卡 → 卡片”,整体像嵌套面板,入口页气质被破坏。
|
|
325
|
+
|
|
326
|
+
- ✅ **正确**:灰底为主,白卡只出现在**推荐卡片本身**(`<Card color="white" />`)
|
|
327
|
+
- ⛔ **禁止**:在右侧内容区外额外包“整张白色大卡片容器”(例如 `style={{ background: 'var(--color-surface)' }}` 的大 wrapper)
|
|
328
|
+
- ⛔ **禁止**:把 `Card color="grey"` 当作入口页模板卡默认值;若模板卡父级直接是灰底,必须用 `Card color="white"`。
|
|
329
|
+
- **代码扫描口诀**:同一首页里出现 `ChatInput` + 模板 `Card` 网格时,向上找最近三层父容器;如果有 `bg-surface` / `--color-surface` / `background: '#fff'` 且包住整块 Hero + 网格,它就是错误的大白卡 wrapper。
|
|
330
|
+
|
|
331
|
+
Bad / Good 对照:
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
| ❌ Bad(错误嵌套) | 后果 | ✅ Good(正确层级) |
|
|
335
|
+
| ---------------------------------- | --------------------------------- | ----------------------------- |
|
|
336
|
+
| 右侧内容区整体包一层 `bg=surface rounded-lg` | 灰底被“吞掉”,入口页像普通白卡管理页;页面层级变硬、主输入不突出 | Hero / 筛选行 / 网格直接坐灰底;仅卡片自身为白卡 |
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
推荐骨架(可直接照抄):
|
|
340
|
+
|
|
341
|
+
```jsx
|
|
342
|
+
<div className="h-dvh w-full min-w-0 flex overflow-hidden" style={{ background: 'var(--color-blueGrey-200)' }}>
|
|
343
|
+
<NavBar className="shrink-0" />
|
|
344
|
+
|
|
345
|
+
{/* 右侧内容区:整体仍在灰底上组织,不再外包一整张白卡 */}
|
|
346
|
+
<main className="flex-1 min-w-0 min-h-0 flex flex-col overflow-hidden">
|
|
347
|
+
{/* Hero(可用渐变区) */}
|
|
348
|
+
<section className="shrink-0" style={{ padding: '120px 40px 80px' }}>
|
|
349
|
+
<div className="w-full" style={{ maxWidth: 680, margin: '0 auto' }}>
|
|
350
|
+
<ChatInput variant="default" placeholder="描述你想完成的任务…" />
|
|
351
|
+
</div>
|
|
352
|
+
</section>
|
|
353
|
+
|
|
354
|
+
{/* 筛选行:Tabs 默认 SM */}
|
|
355
|
+
<section className="shrink-0 flex items-center justify-between gap-4" style={{ padding: '16px 40px' }}>
|
|
356
|
+
<Tabs variant="pill" size="sm" items={tabs} />
|
|
357
|
+
<Input prefix={<Icon name="search-md-stroked" size="sm" />} placeholder="搜索模板" />
|
|
358
|
+
</section>
|
|
359
|
+
|
|
360
|
+
{/* 网格:内部滚动;卡片自身是白卡 */}
|
|
361
|
+
<section className="flex-1 min-h-0 overflow-y-auto" style={{ padding: '0 40px 40px' }}>
|
|
362
|
+
<div className="grid gap-4" style={{ gridTemplateColumns: 'repeat(auto-fit, minmax(min(260px, 100%), 1fr))' }}>
|
|
363
|
+
<Card color="white" title="智能客服助手" />
|
|
364
|
+
</div>
|
|
365
|
+
</section>
|
|
366
|
+
</main>
|
|
367
|
+
</div>
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
---
|
|
371
|
+
|
|
372
|
+
## 2. 三段式骨架(顶 / 中 / 底)
|
|
373
|
+
|
|
374
|
+
最常见的业务页面结构:
|
|
375
|
+
|
|
376
|
+
```
|
|
377
|
+
┌─────────────────────────────┐ ← shrink-0 Topbar / 面包屑
|
|
378
|
+
├─────────────────────────────┤
|
|
379
|
+
│ │ ← flex-1 min-h-0
|
|
380
|
+
│ 内容区(自身内部滚动) │ overflow-y-auto
|
|
381
|
+
│ │
|
|
382
|
+
├─────────────────────────────┤
|
|
383
|
+
│ 输入框 / 全局工具条(可选) │ ← shrink-0
|
|
384
|
+
└─────────────────────────────┘
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
**Tailwind 实现**:
|
|
388
|
+
|
|
389
|
+
```jsx
|
|
390
|
+
<div
|
|
391
|
+
className="h-dvh w-full min-w-0 flex flex-col overflow-hidden"
|
|
392
|
+
style={{ background: 'var(--color-blueGrey-200)' }}
|
|
393
|
+
>
|
|
394
|
+
{/* 顶部:永远不参与滚动,shrink-0 让它保持自然高度 */}
|
|
395
|
+
<header className="shrink-0">{/* Topbar */}</header>
|
|
396
|
+
|
|
397
|
+
{/* 中部:flex-1 占满剩余空间;min-h-0 是 flex 子项必备,否则会被内容撑高 */}
|
|
398
|
+
<main className="flex-1 min-h-0 flex flex-col overflow-hidden">
|
|
399
|
+
{/* 真正的滚动层放这里 */}
|
|
400
|
+
<div className="flex-1 min-h-0 overflow-y-auto p-6">
|
|
401
|
+
{/* 业务内容 */}
|
|
402
|
+
</div>
|
|
403
|
+
</main>
|
|
404
|
+
|
|
405
|
+
{/* 底部:可选,吸底 */}
|
|
406
|
+
<footer className="shrink-0">{/* 可选:ChatInput 等 */}</footer>
|
|
407
|
+
</div>
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
**关键魔法**:
|
|
411
|
+
|
|
412
|
+
- ✅ `flex-1 min-h-0` 是 flex 子项**必须**配套写的两件套,缺一会导致内容把父容器撑高
|
|
413
|
+
- ✅ `overflow-y-auto` 只能加在「真正承载长内容」的那一层,不要每层都加
|
|
414
|
+
- ✅ 横向布局的所有 flex 父容器都要补 `min-w-0`,否则子项内容(特别是带 `text-overflow`/表格)会溢出
|
|
415
|
+
|
|
416
|
+
---
|
|
417
|
+
|
|
418
|
+
## 3. 双栏 / 多栏布局
|
|
419
|
+
|
|
420
|
+
### 3.1 左导航 + 右内容
|
|
421
|
+
|
|
422
|
+
```jsx
|
|
423
|
+
<main className="flex-1 min-h-0 flex flex-row min-w-0">
|
|
424
|
+
{/* 左侧:固定宽度,shrink-0 防止被压缩 */}
|
|
425
|
+
<aside className="shrink-0 w-[240px] overflow-y-auto">
|
|
426
|
+
{/* TagBar 或左侧导航 */}
|
|
427
|
+
</aside>
|
|
428
|
+
|
|
429
|
+
{/* 右侧:弹性宽度,min-w-0 让内部 truncate 等生效 */}
|
|
430
|
+
<section className="flex-1 min-h-0 min-w-0 flex flex-col overflow-hidden">
|
|
431
|
+
{/* 主内容 */}
|
|
432
|
+
</section>
|
|
433
|
+
</main>
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
⛔ **禁止反向写法**:左侧 `flex-1`、右侧 `w-[XXXpx]`(应当**固定的写死,弹性的 flex-1**)
|
|
437
|
+
|
|
438
|
+
### 3.2 双白卡分栏(McpManagementPage 形态)
|
|
439
|
+
|
|
440
|
+
灰底页面上**两张独立白卡**:
|
|
441
|
+
|
|
442
|
+
```jsx
|
|
443
|
+
const [leftCardWidth, setLeftCardWidth] = useState(260);
|
|
444
|
+
|
|
445
|
+
<div className="flex-1 min-h-0 flex flex-row gap-2 p-4 min-w-0">
|
|
446
|
+
{/* 左白卡:默认支持拖拽调宽 */}
|
|
447
|
+
<aside
|
|
448
|
+
className="shrink-0 flex flex-col overflow-visible"
|
|
449
|
+
style={{
|
|
450
|
+
width: `${leftCardWidth}px`,
|
|
451
|
+
background: 'var(--color-surface)',
|
|
452
|
+
borderRadius: 'var(--radius-lg)',
|
|
453
|
+
}}
|
|
454
|
+
>
|
|
455
|
+
<TagBar
|
|
456
|
+
tone="transparent"
|
|
457
|
+
className="!border-r-0"
|
|
458
|
+
style={{ background: 'transparent' }}
|
|
459
|
+
width={leftCardWidth}
|
|
460
|
+
onWidthChange={setLeftCardWidth}
|
|
461
|
+
minWidth={200}
|
|
462
|
+
maxWidth={360}
|
|
463
|
+
resizable
|
|
464
|
+
/>
|
|
465
|
+
</aside>
|
|
466
|
+
|
|
467
|
+
{/* 右白卡:弹性 */}
|
|
468
|
+
<div
|
|
469
|
+
className="flex-1 min-h-0 min-w-0 flex flex-col overflow-hidden"
|
|
470
|
+
style={{
|
|
471
|
+
background: 'var(--color-surface)',
|
|
472
|
+
borderRadius: 'var(--radius-lg)',
|
|
473
|
+
}}
|
|
474
|
+
>
|
|
475
|
+
{/* 主内容 */}
|
|
476
|
+
</div>
|
|
477
|
+
</div>
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
**默认规则(新铁律)**:
|
|
481
|
+
|
|
482
|
+
- ✅ 只要是这种**横向并列的大卡工作区**,辅助白卡默认就要支持宽度拖拽;不要先写成固定宽死板双栏
|
|
483
|
+
- ✅ 主白卡始终保持 `flex-1 min-w-0`,辅助白卡用 `width + onWidthChange + minWidth + maxWidth`
|
|
484
|
+
- ✅ 拖拽把手所在卡片外层必须 `overflow-visible`,不能把把手裁掉
|
|
485
|
+
- ⛔ KPI 小卡、入口网格卡、列表项卡片不适用本规则;本规则只针对“主工作区级别”的大卡容器
|
|
486
|
+
|
|
487
|
+
⚠️ 左右同时存在辅助大卡时,左右都应允许在各自边界内调宽;不要默认只保留一个不可调的固定侧栏。
|
|
488
|
+
|
|
489
|
+
### 3.3 网格卡片(功能入口、KPI、产品列表)
|
|
490
|
+
|
|
491
|
+
**响应式列宽**用 CSS Grid 的 `auto-fit + minmax`:
|
|
492
|
+
|
|
493
|
+
```jsx
|
|
494
|
+
<div
|
|
495
|
+
className="grid gap-4"
|
|
496
|
+
style={{ gridTemplateColumns: 'repeat(auto-fit, minmax(min(320px, 100%), 1fr))' }}
|
|
497
|
+
>
|
|
498
|
+
{items.map(item => <Card key={item.id}>{...}</Card>)}
|
|
499
|
+
</div>
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
**列宽断点参考**:
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
| 卡片类型 | minmax 第一个值 |
|
|
506
|
+
| ----------------- | ------------------ |
|
|
507
|
+
| 紧凑信息卡(KPI 数字、状态卡) | `min(240px, 100%)` |
|
|
508
|
+
| 中等卡(功能入口、配置卡) | `min(320px, 100%)` |
|
|
509
|
+
| 宽信息卡(统计图表、内容预览) | `min(420px, 100%)` |
|
|
510
|
+
|
|
511
|
+
|
|
512
|
+
⛔ **禁止**:写死 `grid-cols-3`、`grid-cols-4`(小屏会横滑或挤压)
|
|
513
|
+
|
|
514
|
+
### 3.4 多面板工作台 / Playground(最常被误用为 §12.4 网格工作台)
|
|
515
|
+
|
|
516
|
+
**适用**:Model Playground、Prompt Editor、API 调试器、规则编辑器、A/B 对比器——多个**独立工作区面板**并排或上下分块共享一屏。
|
|
517
|
+
|
|
518
|
+
**核心约束**(违反任一即视为 BUG):
|
|
519
|
+
|
|
520
|
+
1. ✅ **整页绝不滚**:根容器永远 `h-dvh + overflow-hidden`
|
|
521
|
+
2. ✅ **每张面板独立全高**:用 `flex-1 min-h-0` 让面板撑满剩余空间,不靠面板自身高度
|
|
522
|
+
3. ✅ **每张面板独立内部滚**:面板**头部** `shrink-0`、面板**正文** `flex-1 min-h-0 overflow-y-auto`
|
|
523
|
+
4. ✅ **上下叠的子面板**:父容器 `flex flex-col`;固定块 `shrink-0`,唯一弹性块 `flex-1 min-h-0`
|
|
524
|
+
5. ✅ **横向辅助大面板默认支持拖拽调宽**:如左导航工作区、右配置区、右结果区,不要默认写死为不可调固定宽
|
|
525
|
+
6. ⛔ **禁止**把 main 写成 `overflow-y-auto`(这是 §12.4 信息聚合页骨架,不适合工作台)
|
|
526
|
+
|
|
527
|
+
**最小骨架**(横向三栏,每栏独立滚):
|
|
528
|
+
|
|
529
|
+
```jsx
|
|
530
|
+
<main className="flex-1 min-h-0 flex flex-row p-4 gap-2 overflow-hidden min-w-0">
|
|
531
|
+
{/* 面板 1:弹性宽 + 全高 + 内部滚 */}
|
|
532
|
+
<section
|
|
533
|
+
className="flex-1 min-h-0 min-w-0 flex flex-col overflow-hidden"
|
|
534
|
+
style={{ background: 'var(--color-surface)', borderRadius: 'var(--radius-lg)' }}
|
|
535
|
+
>
|
|
536
|
+
<header className="shrink-0 px-6 py-4">
|
|
537
|
+
{/* ⚠️ 面板内 section 标题统一用 FormTitle variant="level-2"(白卡内分区,描述同行右侧) */}
|
|
538
|
+
<FormTitle variant="level-2" title="Prompt Workspace" showDescription={false} />
|
|
539
|
+
</header>
|
|
540
|
+
{/* ↓ 面板正文:标题不滚;System Prompt 这类主编辑器填满正文并在组件内部滚 */}
|
|
541
|
+
<div className="flex-1 min-h-0 overflow-hidden px-6 pb-6">
|
|
542
|
+
<TextArea variant="code" fillHeight resize="none" className="flex-1 min-h-0 w-full" />
|
|
543
|
+
</div>
|
|
544
|
+
</section>
|
|
545
|
+
|
|
546
|
+
{/* 面板 2:默认支持拖拽调宽;上下两个子面板独立滚 */}
|
|
547
|
+
<aside className="shrink-0 flex flex-col gap-2 overflow-visible" style={{ width: `${sidePanelWidth}px` }}>
|
|
548
|
+
{/* 子面板 A:高度由内容决定 → shrink-0 */}
|
|
549
|
+
<div
|
|
550
|
+
className="shrink-0 flex flex-col overflow-hidden"
|
|
551
|
+
style={{ background: 'var(--color-surface)', borderRadius: 'var(--radius-lg)' }}
|
|
552
|
+
>
|
|
553
|
+
<header className="shrink-0 px-6 py-4">
|
|
554
|
+
<FormTitle variant="level-2" title="Model Settings" showDescription={false} />
|
|
555
|
+
</header>
|
|
556
|
+
<div className="px-6 pb-6 flex flex-col gap-4">
|
|
557
|
+
{/* 参数表单 */}
|
|
558
|
+
</div>
|
|
559
|
+
</div>
|
|
560
|
+
|
|
561
|
+
{/* 子面板 B:占满剩余空间 → flex-1 min-h-0 + 内部滚 */}
|
|
562
|
+
<div
|
|
563
|
+
className="flex-1 min-h-0 flex flex-col overflow-hidden"
|
|
564
|
+
style={{ background: 'var(--color-surface)', borderRadius: 'var(--radius-lg)' }}
|
|
565
|
+
>
|
|
566
|
+
<header className="shrink-0 px-6 py-4">
|
|
567
|
+
<FormTitle variant="level-2" title="Run Result" showDescription={false} />
|
|
568
|
+
</header>
|
|
569
|
+
<div className="flex-1 min-h-0 overflow-y-auto px-6 pb-6">
|
|
570
|
+
{/* 运行结果 */}
|
|
571
|
+
</div>
|
|
572
|
+
</div>
|
|
573
|
+
</aside>
|
|
574
|
+
</main>
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
**宽度规则补充**:
|
|
578
|
+
|
|
579
|
+
- 默认初始宽度可以是 `280 / 320 / 360px` 一类语义值,但必须给出 `minWidth` 与 `maxWidth`
|
|
580
|
+
- 工作台里承担“辅助工作区”角色的横向大卡,默认都应可调宽;不要生成“左 280px 固定 + 右 360px 固定 + 中间硬挤”的僵硬三栏
|
|
581
|
+
- 主工作区面板继续优先 `flex-1 min-w-0`,由剩余空间自然伸缩
|
|
582
|
+
- 把手如果挂在卡片边缘,卡片外层必须 `overflow-visible`
|
|
583
|
+
|
|
584
|
+
⛔ **常见 BUG(用户截图同款)**:
|
|
585
|
+
|
|
586
|
+
|
|
587
|
+
| ❌ Bad | 后果 | ✅ Fix |
|
|
588
|
+
| ---------------------------------------- | ------------------------- | ------------------------------------------------------------------------------- |
|
|
589
|
+
| `<main className="... overflow-y-auto">` | 整页滚,下方卡片被挤出一屏,对照工作流断裂 | `<main className="flex-1 min-h-0 overflow-hidden">`,把 `overflow-y-auto` 下沉到面板正文 |
|
|
590
|
+
| 面板自己 `min-h-[600px]` / `h-[800px]` 写死高度 | 面板高度大于视口剩余空间,整页又被撑开 | 面板用 `flex-1 min-h-0`,让它**自动撑满剩余视口** |
|
|
591
|
+
| 上下子面板都不写 `min-h-0` | 子面板 A 内容多时直接撑死子面板 B,B 看不见 | 父容器 `flex-col`,固定 A `shrink-0`、弹性 B `flex-1 min-h-0` |
|
|
592
|
+
| 面板正文直接堆内容,没有独立滚动层 | 内容超长直接撑爆面板,整页跟着滚 | 面板正文加 `flex-1 min-h-0 overflow-y-auto` |
|
|
593
|
+
| 面板 header 不加 `shrink-0` | 内容多时 header 被压缩、标题被挤变形 | 面板 header 永远 `shrink-0` |
|
|
594
|
+
|
|
595
|
+
|
|
596
|
+
⚠️ **混合横向 + 纵向**:横向是 `flex-row`,每个直接子项要么 `shrink-0 w-[Npx]`、要么 `flex-1 min-w-0`;纵向是 `flex-col`,子项要么 `shrink-0`、要么 `flex-1 min-h-0`——**横纵两套规则各自独立适用**。
|
|
597
|
+
|
|
598
|
+
---
|
|
599
|
+
|
|
600
|
+
### 3.4.1 白卡 / 面板标题栏防挤压(禁止标题竖排)
|
|
601
|
+
|
|
602
|
+
> 标题是板块语义锚点,优先级高于右侧 Tabs、按钮、状态说明。标题区只能横向可读,绝不允许因为面板窄而出现一字一行的竖状标题。
|
|
603
|
+
|
|
604
|
+
**适用信号**:白卡 header、面板 header、`Prompt Workspace`、`System Prompt`、`Conversation`、`Model Settings`、`Run Result`、左右栏/三栏 Playground 的每张卡片标题。
|
|
605
|
+
|
|
606
|
+
**标准结构**:
|
|
607
|
+
|
|
608
|
+
```jsx
|
|
609
|
+
<header className="shrink-0 flex items-start justify-between gap-3 px-6 py-4">
|
|
610
|
+
<div className="min-w-[120px] flex-1">
|
|
611
|
+
<FormTitle variant="level-2" title="Prompt Workspace" showDescription={false} />
|
|
612
|
+
</div>
|
|
613
|
+
|
|
614
|
+
{/* Tabs / Button:操作区可以换行,不能挤压标题 */}
|
|
615
|
+
<div className="shrink-0 flex flex-wrap justify-end gap-2">
|
|
616
|
+
<Tabs variant="segment" size="sm" items={[...]} />
|
|
617
|
+
</div>
|
|
618
|
+
</header>
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
如果标题右侧有状态 / 数量 / 身份标签,Tag 不是操作区内容,必须贴在主标题右侧:
|
|
622
|
+
|
|
623
|
+
```jsx
|
|
624
|
+
<FormTitle
|
|
625
|
+
variant="level-2"
|
|
626
|
+
title="Conversation"
|
|
627
|
+
titleSuffix={<Tag variant="grey" size="m">0 条消息</Tag>}
|
|
628
|
+
/>
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
`description` 是低频补充信息,默认隐藏。只有板块需要解释业务规则、使用方式、风险/空状态原因,或需求明确说要展示副标题/描述时,才同时传 `description` 与 `showDescription`。
|
|
632
|
+
|
|
633
|
+
**决策规则**:
|
|
634
|
+
|
|
635
|
+
|
|
636
|
+
| 场景 | 标题策略 | 右侧内容策略 |
|
|
637
|
+
| ----------------- | ------------------------------------------------------- | -------------------------------- |
|
|
638
|
+
| 正常白卡(宽度 ≥ 320px) | `FormTitle variant="level-2"`,默认只显示主标题 | 右侧按钮/Tabs `shrink-0` |
|
|
639
|
+
| 有板块介绍/场景说明诉求 | `description="..." showDescription`,按 variant 放在标题下方或右侧 | 仍不能挤压标题 |
|
|
640
|
+
| 标题旁有状态/数量 Tag | 用 `titleSuffix={<Tag ... />}` 紧跟主标题,位于 description 前 | 不放进右侧操作区 |
|
|
641
|
+
| 窄面板(宽度 < 320px) | 保持默认无描述,只保留短标题 | Tabs/按钮移到下一行或减少选项文案 |
|
|
642
|
+
| 极窄侧栏(宽度 < 240px) | 标题仍横排,必要时缩短标题文案 | 不放长 Tabs;改为正文区切换或更多菜单 |
|
|
643
|
+
| 标题 + 多个 Tabs/按钮冲突 | 标题 `min-w-[120px] flex-1` 优先 | 操作区 `flex-wrap justify-end`,允许换行 |
|
|
644
|
+
|
|
645
|
+
|
|
646
|
+
⛔ **禁止**:
|
|
647
|
+
|
|
648
|
+
|
|
649
|
+
| ❌ Bad | 问题 | ✅ Fix |
|
|
650
|
+
| ----------------------------------------------------------- | ------------------- | -------------------------------------------------------------------------- |
|
|
651
|
+
| `<div className="w-4 break-all">原始信息只读</div>` | 标题一字一行,语义锚点失效 | `<FormTitle variant="level-2" title="原始信息" />` |
|
|
652
|
+
| `writing-mode: vertical-rl` / `text-orientation: upright` | B 端卡片标题不使用竖排文字 | 只用横排标题 |
|
|
653
|
+
| 标题容器 `w-[16px]` / `max-w-[24px]` | 把标题压成竖条 | 标题区 `min-w-[120px] flex-1` |
|
|
654
|
+
| 右侧 Tabs/按钮过多仍 `nowrap` | 挤压标题 | 操作区 `flex-wrap` 或迁移到正文顶部 |
|
|
655
|
+
| `<FormTitle ... /><Tag>0 条消息</Tag>` 放在 `justify-between` 右侧 | 数量标签被误当成操作区,和标题语义断开 | `<FormTitle titleSuffix={<Tag variant="grey" size="m">0 条消息</Tag>} ... />` |
|
|
656
|
+
|
|
657
|
+
|
|
658
|
+
---
|
|
659
|
+
|
|
660
|
+
### 3.5 卡片内容区弹性控件(TextArea / 代码块 / JSON / 列表)
|
|
661
|
+
|
|
662
|
+
> **标题稳定,内容滚动**:白卡里的 `FormTitle` / Tabs / 操作按钮属于 header,永远 `shrink-0`;高度变化只发生在正文 body。正文里的主控件如果承载长内容,必须填满 body,并在控件内部滚动。
|
|
663
|
+
|
|
664
|
+
**适用信号**:System Prompt、Prompt Workspace、模型高级参数 JSON、请求参数、代码参数、JSON 编辑、代码编辑、工单详情、规则描述、日志、长列表、运行结果、消息流。
|
|
665
|
+
|
|
666
|
+
**通用结构(必须逐层成立)**:
|
|
667
|
+
|
|
668
|
+
```jsx
|
|
669
|
+
<section className="flex-1 min-h-0 min-w-0 flex flex-col overflow-hidden">
|
|
670
|
+
{/* 标题 / tabs / 操作区:稳定,不参与滚动 */}
|
|
671
|
+
<header className="shrink-0 px-6 py-4">
|
|
672
|
+
<FormTitle variant="level-2" title="System Prompt" description="使用 TFDS TextArea 重构编辑区。" showDescription />
|
|
673
|
+
</header>
|
|
674
|
+
|
|
675
|
+
{/* 正文 body:占满剩余高度,不自己滚时就给子控件提供高度边界 */}
|
|
676
|
+
<div className="flex-1 min-h-0 overflow-hidden px-6 pb-6">
|
|
677
|
+
<TextArea
|
|
678
|
+
variant="code"
|
|
679
|
+
fillHeight
|
|
680
|
+
resize="none"
|
|
681
|
+
className="flex-1 min-h-0 w-full"
|
|
682
|
+
placeholder="请输入 System Prompt"
|
|
683
|
+
/>
|
|
684
|
+
</div>
|
|
685
|
+
</section>
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
**TextArea 专项**:
|
|
689
|
+
|
|
690
|
+
|
|
691
|
+
| 场景 | 正确高度策略 |
|
|
692
|
+
| ----------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
|
|
693
|
+
| 卡片/面板主编辑区(System Prompt、模型高级参数 JSON、请求参数、代码参数、JSON、代码、规则配置) | `variant="code" fillHeight resize="none" className="flex-1 min-h-0 w-full"`,带行号并填满正文区,长文本在 TextArea 内部滚动 |
|
|
694
|
+
| 卡片/面板主编辑区(工单详情、普通长文本) | `fillHeight resize="none" className="flex-1 min-h-0 w-full"`,填满正文区,长文本在 TextArea 内部滚动 |
|
|
695
|
+
| 普通表单备注 / 弹窗字段 | `minRows={3}` 或按语义选择 2-5 行,自然高度,不用 `fillHeight` |
|
|
696
|
+
| 模型 ID / URL / 单行起但可能很长 | `minRows={1}`,与 Input MD 对齐,必要时自然增高 |
|
|
697
|
+
|
|
698
|
+
|
|
699
|
+
⛔ **禁止**:
|
|
700
|
+
|
|
701
|
+
|
|
702
|
+
| ❌ Bad | 后果 | ✅ Fix |
|
|
703
|
+
| ---------------------------------------- | ----------------------- | ------------------------------------ |
|
|
704
|
+
| `<TextArea minRows={5} />` 直接放进高卡片 | TextArea 只占 5 行,下方大片空白 | 卡片主编辑区用 `fillHeight resize="none"` |
|
|
705
|
+
| body 写 `overflow-y-auto`,TextArea 自己自然高度 | 滚动发生在整块正文,输入框边框/标题关系不稳定 | body `overflow-hidden`,TextArea 内部滚 |
|
|
706
|
+
| header 放进 `overflow-y-auto` body | 滚动时标题被卷走 | header 拆出来并 `shrink-0` |
|
|
707
|
+
| 父容器漏 `min-h-0` | 子控件无法拿到剩余高度,会撑爆卡片 | section/body/子控件逐层补 `flex-1 min-h-0` |
|
|
708
|
+
|
|
709
|
+
|
|
710
|
+
**代码 / Prompt 输入框专项**:
|
|
711
|
+
|
|
712
|
+
- System Prompt、模型高级参数 JSON、请求参数、代码参数、JSON、代码、SQL、Markdown、Agent 指令、规则配置等需要逐行定位的输入,统一使用 `<TextArea variant="code" />`。
|
|
713
|
+
- `variant="code"` 自带左侧行号、等宽字体、禁用拼写检查;不要手搓行号列,也不要用原生 `<textarea>`。
|
|
714
|
+
- 如果它是卡片主编辑器,仍必须叠加 `fillHeight resize="none" className="flex-1 min-h-0 w-full"`。
|
|
715
|
+
- 纯数值模型参数(Temperature / Top-P / Max Tokens / 阈值 / 权重)不属于本类,必须使用 `InputNumber`。
|
|
716
|
+
|
|
717
|
+
**判断口诀**:这块控件是不是这张卡片的“主要工作区”?是 → 子控件填满 body 并内部滚;不是 → 按自身内容自然高度。
|
|
718
|
+
|
|
719
|
+
---
|
|
720
|
+
|
|
721
|
+
## 4. 表格 / 长列表的滚动规则
|
|
722
|
+
|
|
723
|
+
### 4.1 表格
|
|
724
|
+
|
|
725
|
+
```jsx
|
|
726
|
+
<div className="flex-1 min-h-0 flex flex-col overflow-hidden">
|
|
727
|
+
{/* 表头工具条:固定高度 */}
|
|
728
|
+
<div className="shrink-0 px-6 py-4 flex items-center justify-between">...</div>
|
|
729
|
+
|
|
730
|
+
{/* 表格本体:内部滚动(关键!) */}
|
|
731
|
+
<div className="flex-1 min-h-0 overflow-auto">
|
|
732
|
+
<Table ... />
|
|
733
|
+
</div>
|
|
734
|
+
|
|
735
|
+
{/* 分页:吸底 */}
|
|
736
|
+
<div
|
|
737
|
+
className="shrink-0 px-6 py-3"
|
|
738
|
+
style={{ borderTop: '1px solid var(--color-border-line-light)' }}
|
|
739
|
+
>
|
|
740
|
+
<Pagination ... />
|
|
741
|
+
</div>
|
|
742
|
+
</div>
|
|
743
|
+
```
|
|
744
|
+
|
|
745
|
+
**关键**:
|
|
746
|
+
|
|
747
|
+
- 表格父容器 `flex-1 min-h-0 overflow-auto`,让表格在数据多时**内部滚动**
|
|
748
|
+
- 横向太宽 → 由表格自身处理 `overflow-x: auto`
|
|
749
|
+
- ⛔ **禁止**:把表格直接放在没有高度约束的容器里(会撑成天高)
|
|
750
|
+
|
|
751
|
+
### 4.2 长列表(chat / log / feed)
|
|
752
|
+
|
|
753
|
+
最后一条消息距离输入框/底部至少 **16px** 安全间距:
|
|
754
|
+
|
|
755
|
+
```jsx
|
|
756
|
+
<div className="flex-1 min-h-0 overflow-y-auto pb-4">
|
|
757
|
+
{messages.map(...)}
|
|
758
|
+
</div>
|
|
759
|
+
```
|
|
760
|
+
|
|
761
|
+
---
|
|
762
|
+
|
|
763
|
+
## 5. 输入框吸底(IM / Chat 场景)
|
|
764
|
+
|
|
765
|
+
**ChatInput 与 IMConversationPattern 组合**:
|
|
766
|
+
|
|
767
|
+
```jsx
|
|
768
|
+
<main className="flex-1 min-h-0 flex flex-col overflow-hidden">
|
|
769
|
+
{/* 消息区:自身滚动 */}
|
|
770
|
+
<div className="flex-1 min-h-0 overflow-hidden">
|
|
771
|
+
<IMConversationPattern messages={messages} />
|
|
772
|
+
</div>
|
|
773
|
+
|
|
774
|
+
{/* 输入框:吸底,绝不进入 overflow 容器 */}
|
|
775
|
+
<div className="shrink-0 px-6 pb-4">
|
|
776
|
+
<ChatInput ... />
|
|
777
|
+
</div>
|
|
778
|
+
</main>
|
|
779
|
+
```
|
|
780
|
+
|
|
781
|
+
⛔ **禁止**:把 ChatInput 包进 `overflow-y-auto` 容器里(会被滚走)
|
|
782
|
+
|
|
783
|
+
---
|
|
784
|
+
|
|
785
|
+
## 6. 页面宽度策略
|
|
786
|
+
|
|
787
|
+
### 6.1 默认行为(全宽撑满,禁止收窄)
|
|
788
|
+
|
|
789
|
+
**B 端页面必须撑满浏览器全宽**——灰底贴边、白卡距边缘 16px、卡与卡之间 8px(详见 §1.2 三层间距金字塔)。
|
|
790
|
+
|
|
791
|
+
⛔ **禁止一切导致左右大空白的写法**:
|
|
792
|
+
|
|
793
|
+
|
|
794
|
+
| 禁止写法 | 后果 |
|
|
795
|
+
| ----------------------------------------------------------------- | ------------------ |
|
|
796
|
+
| 根容器 / main 加 `max-w-screen-xl` / `max-w-7xl` / `max-w-[1440px]` 等 | 页面被居中收窄,两侧死白/死灰大空白 |
|
|
797
|
+
| 根容器 / main 加 `mx-auto` | 页面居中,两侧出现空白 |
|
|
798
|
+
| `main` 内套 `<div className="max-w-* mx-auto px-*">` 居中容器 | 内容被套层收窄,白卡不撑满 |
|
|
799
|
+
| `main` padding 用 `px-6` / `px-8` 代替 `px-4` | 白卡距浏览器边缘过大,看着像居中留白 |
|
|
800
|
+
| 白卡 div 自身加 `mx-4` / `mx-6` 等外边距 | 白卡被横向缩进,不贴框架 |
|
|
801
|
+
|
|
802
|
+
|
|
803
|
+
✅ 让页面在所有宽度下**撑满浏览器**(B 端工具产品宽屏要利用率)
|
|
804
|
+
✅ 内容区按 § 3「双栏 / 网格 / 自适应」规则反应
|
|
805
|
+
|
|
806
|
+
---
|
|
807
|
+
|
|
808
|
+
## 6.3 表单控件宽度决策(修复“Input 不满宽”高频问题)
|
|
809
|
+
|
|
810
|
+
> 这是一个**非常高频**的 AI 生成错误:在白卡/面板正文里放了 `Input`,但输入框只占一小段宽度,看起来像“没对齐/没撑满/不专业”。
|
|
811
|
+
>
|
|
812
|
+
> 根因:`Input` 根容器默认是 `inline-flex`,如果你不显式给它宽度,它会按内容收缩。**筛选栏**需要这种收缩/固定宽,但**板块正文的主字段**不需要。
|
|
813
|
+
|
|
814
|
+
### 6.3.1 决策表(必须按此选)
|
|
815
|
+
|
|
816
|
+
|
|
817
|
+
| 场景 | 正确宽度策略 | 写法 |
|
|
818
|
+
| --------------------------------------- | ------------------ | -------------------------------------------------------------- |
|
|
819
|
+
| **筛选栏 / 工具条**(一行并排多个字段) | 固定宽(约 240/200/160) | `<Input style={{ width: 240 }} ... />`;或外层 `flex: '0 1 240px'` |
|
|
820
|
+
| **白卡正文 / 板块表单 / 单列编辑区**(主字段、名称、标题、单行配置) | **满宽** | `<Input className="w-full" ... />`(父容器需 `min-w-0`) |
|
|
821
|
+
| **双栏表单**(左/右两列各自为字段列) | 列内满宽 | 列容器 `flex-1 min-w-0`,字段 `w-full` |
|
|
822
|
+
|
|
823
|
+
|
|
824
|
+
### 6.3.2 满宽结构模板(推荐照抄)
|
|
825
|
+
|
|
826
|
+
```jsx
|
|
827
|
+
<div className="flex flex-col gap-4 min-w-0">
|
|
828
|
+
<FormTitle variant="level-2" title="Prompt Workspace" />
|
|
829
|
+
|
|
830
|
+
{/* 单列主字段:必须满宽 */}
|
|
831
|
+
<div className="flex flex-col gap-2 min-w-0">
|
|
832
|
+
<div className="text-sm font-medium text-blueGrey-800">标题</div>
|
|
833
|
+
<Input className="w-full" placeholder="请输入标题" />
|
|
834
|
+
</div>
|
|
835
|
+
</div>
|
|
836
|
+
```
|
|
837
|
+
|
|
838
|
+
> ⚠️ 自检:如果你看到 `Input` 出现在白卡正文里但没有 `w-full`(或没有明确 `width`),先按本节修复;不要用 margin/justify 去“摆正”,那只是掩盖问题。
|
|
839
|
+
|
|
840
|
+
### 6.2 例外:表单页可局部收窄
|
|
841
|
+
|
|
842
|
+
表单页/详情页**主标题区**可以**局部**加 `max-w-[1280px] mx-auto` 防止单行表单过长难读:
|
|
843
|
+
|
|
844
|
+
```jsx
|
|
845
|
+
<form className="max-w-[1280px] mx-auto px-6 py-8">
|
|
846
|
+
{/* 表单字段 */}
|
|
847
|
+
</form>
|
|
848
|
+
```
|
|
849
|
+
|
|
850
|
+
⛔ **禁止**:把整页根容器加 `max-w`,那会让灰底页面在宽屏左右出现死白条。
|
|
851
|
+
|
|
852
|
+
---
|
|
853
|
+
|
|
854
|
+
## 7. 白卡嵌套层级规则(避免白叠白 / 灰叠灰)
|
|
855
|
+
|
|
856
|
+
> 在动手嵌套白卡之前,先回 §1.5 检查"这块内容是不是页面级 header / 工具条"——是的话**直接坐灰底**,根本不需要白卡。本节只规范"已经确定要包白卡"的内容应该选什么颜色。
|
|
857
|
+
|
|
858
|
+
层级颜色按"父子互斥":
|
|
859
|
+
|
|
860
|
+
|
|
861
|
+
| 父容器 | 子卡片用 | 孙卡片用 |
|
|
862
|
+
| ---------------------------- | --------------------------- | --------------------------- |
|
|
863
|
+
| 灰底页面(`--color-blueGrey-200`) | **白底** `Card color="white"` | **灰底** `Card color="grey"` |
|
|
864
|
+
| 白底白卡 | **灰底** `Card color="grey"` | **白底** `Card color="white"` |
|
|
865
|
+
| 灰底 Card(二级灰) | **白底** `Card color="white"` | 一般不再嵌套 |
|
|
866
|
+
|
|
867
|
+
|
|
868
|
+
⚠️ **嵌套深度建议 ≤ 3 层**,过深会丢失视觉层级,应改为 Tab/折叠结构。
|
|
869
|
+
|
|
870
|
+
⛔ **禁止**:
|
|
871
|
+
|
|
872
|
+
- ❌ 白底白卡(直接糊在一起,看不出层级)
|
|
873
|
+
- ❌ 任何子层用阴影区分(白卡禁止阴影)
|
|
874
|
+
|
|
875
|
+
---
|
|
876
|
+
|
|
877
|
+
## 8. 弹窗 / 抽屉 / 浮层尺寸规则
|
|
878
|
+
|
|
879
|
+
|
|
880
|
+
| 场景 | 推荐尺寸 |
|
|
881
|
+
| --------------------- | ------------------------------------- |
|
|
882
|
+
| Confirm Dialog(确认/告警) | `width: 400-480px` |
|
|
883
|
+
| Form Dialog(短表单) | `width: 560-640px` |
|
|
884
|
+
| Form Drawer(长表单) | `width: 560px`(侧滑) |
|
|
885
|
+
| 详情抽屉 | `width: 720px` 或 `min(720px, 60vw)` |
|
|
886
|
+
| 复杂任务弹窗(含 Tab) | `width: 880-960px`、`max-height: 80vh` |
|
|
887
|
+
|
|
888
|
+
|
|
889
|
+
**通用约束**:
|
|
890
|
+
|
|
891
|
+
- ✅ 弹窗内必须 `max-height: 80vh` + 内部 `overflow-y-auto`,否则在小高度屏会被截断
|
|
892
|
+
- ✅ 抽屉宽度优先用 `min(WIDTH, 90vw)`,避免在窄屏完全遮挡
|
|
893
|
+
|
|
894
|
+
---
|
|
895
|
+
|
|
896
|
+
## 9. Z-index 规范(统一 token 化)
|
|
897
|
+
|
|
898
|
+
**只允许使用以下 6 档**,禁止 `z-[999]`、`z-[9999]`:
|
|
899
|
+
|
|
900
|
+
|
|
901
|
+
| 层级 | z-index | Tailwind | 用途 |
|
|
902
|
+
| ------------ | ------- | ---------- | ------------------------------------ |
|
|
903
|
+
| 默认 | 0 | `z-0` | 页面内容 |
|
|
904
|
+
| 吸顶 | 10 | `z-10` | Topbar / sticky 表头 |
|
|
905
|
+
| Dropdown | 20 | `z-20` | 下拉菜单、Tooltip(普通页面层) |
|
|
906
|
+
| 抽屉 | 1000 | `z-[1000]` | drawer overlay + panel(panel 用 1010) |
|
|
907
|
+
| 弹窗 | 2000 | `z-[2000]` | dialog overlay + panel(panel 用 2010) |
|
|
908
|
+
| Toast | 3000 | `z-[3000]` | 全局通知 |
|
|
909
|
+
| 弹窗内 Dropdown | 2050 | `z-[2050]` | 弹窗里的下拉/Tooltip(高于 panel) |
|
|
910
|
+
|
|
911
|
+
|
|
912
|
+
**层级设计原则**:
|
|
913
|
+
|
|
914
|
+
- 同类组件之间隔 1000:避免互相干扰
|
|
915
|
+
- overlay vs panel 隔 10:让 panel 永远在 overlay 之上
|
|
916
|
+
- 弹窗内 dropdown 在 panel 之上 +40:保证可见
|
|
917
|
+
|
|
918
|
+
---
|
|
919
|
+
|
|
920
|
+
## 10. 滚动条样式
|
|
921
|
+
|
|
922
|
+
- ⛔ **不要**自定义滚动条样式(`::-webkit-scrollbar`),保持系统默认
|
|
923
|
+
- ✅ 如果设计稿要求美化滚动条,必须使用 TFDS 提供的全局工具类(`tfds-scrollbar-thin`,由 `theme.css` 注入)
|
|
924
|
+
|
|
925
|
+
---
|
|
926
|
+
|
|
927
|
+
## 11. 自适应自检 Checklist(生成完整页后必查)
|
|
928
|
+
|
|
929
|
+
把浏览器从 **1920px → 1280px → 1024px → 768px** 缩,逐步检查:
|
|
930
|
+
|
|
931
|
+
|
|
932
|
+
| 检查项 | 失败信号 | 修复 |
|
|
933
|
+
| ----------------- | ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
934
|
+
| 横向溢出 | 出现底部横向滚动条 | 父容器加 `min-w-0`、子项允许收缩 |
|
|
935
|
+
| 整页滚动 | 页面整体上下滚(非内容区滚) | 根容器改 `h-dvh + overflow-hidden`,内容区改 `flex-1 min-h-0 overflow-y-auto` |
|
|
936
|
+
| 输入框被顶走 | ChatInput 滚出视口 | 输入框外层 `shrink-0`,不在 overflow 容器内 |
|
|
937
|
+
| 网格挤压 | 卡片缩到不可读 | grid 改 `auto-fit + minmax(min(XX, 100%), 1fr)` |
|
|
938
|
+
| 双栏断裂 | 左栏被压成 0 宽 | 左栏 `shrink-0 w-[XXXpx]`,右栏 `flex-1 min-w-0` |
|
|
939
|
+
| **白卡左右大空白** | 白卡与浏览器边缘间距明显大于 16px,看着像居中留白 | ① 检查 `main` padding 只能是 `p-4`(不是 `p-2` / `px-6` / `px-8`);② 删除 `main` 内的居中容器(`max-w-* mx-auto`);③ 检查白卡 div 没有 `mx-`* 外边距(详见 §1.2) |
|
|
940
|
+
| **内容区不撑全宽** | 某层容器宽度跟随内容,没撑满父容器 | 检查横向 `flex` 父容器有 `w-full` + `min-w-0`,各子项有 `flex-1 min-w-0`(弹性)或 `shrink-0 w-[Npx]`(固定),不能漏 `min-w-0` |
|
|
941
|
+
| 横向大卡不可调宽 | 左右大白卡都写死宽度,用户无法按工作流分配空间 | 按 §3.2 / §3.4 / §12.2 改造:辅助大卡提供宽度拖拽,主卡保持 `flex-1 min-w-0`,并补齐 `minWidth / maxWidth / overflow-visible` |
|
|
942
|
+
| 表格行错乱 | 表格高度无限撑开 | 表格父容器 `flex-1 min-h-0 overflow-auto` |
|
|
943
|
+
| 弹窗被裁 | 高度超出视口看不到底部 | 弹窗加 `max-height: 80vh + overflow-y-auto` |
|
|
944
|
+
| 文字溢出 | 长字符把容器撑爆 | 文本节点加 `truncate` 或 `break-words`,父容器加 `min-w-0` |
|
|
945
|
+
| 白叠白 | 白卡里直接放白 Card | 内层 Card 改 `color="grey"` |
|
|
946
|
+
| 整页有外圈圆角 | 灰底没贴浏览器边 | 移除根容器 `rounded` 与 `border` |
|
|
947
|
+
| Playground 多面板整页滚 | 调参数得滚下去看结果 / 写 prompt 得滚回去看输入 | 按 § 3.4 / § 12.7 改造:根容器 `h-dvh + overflow-hidden`、main `overflow-hidden`、面板正文 `flex-1 min-h-0 overflow-y-auto` |
|
|
948
|
+
| 子面板互相挤压(A 撑死 B) | 上下两块卡片其中一块完全看不见 / 高度不对 | 父 `flex-col gap-2`;定高块 `shrink-0`,弹性块 `flex-1 min-h-0` + 内部 `overflow-y-auto` |
|
|
949
|
+
| 面板标题被挤变形 | 内容多时面板 header 被挤压成单行 / 标题被压缩 | 面板 header 永远 `shrink-0`,标题区 `min-w-[120px] flex-1`,操作区 `shrink-0 flex-wrap`,面板正文 `flex-1 min-h-0 overflow-y-auto` |
|
|
950
|
+
| 标题竖排 / 逐字换行 | “原始信息只读”变成一字一行,或出现 `writing-mode` / `break-all` | 按 §3.4.1:删除窄宽度和竖排样式,标题使用 `<FormTitle />` 横排;窄面板 `showDescription={false}`,右侧 Tabs/按钮换行或移入正文 |
|
|
951
|
+
| TextArea 不撑满卡片 | System Prompt 只占 3-5 行,下方大片空白,长文本滚动不在控件内 | 主编辑区按 §3.5:header `shrink-0`,body `flex-1 min-h-0 overflow-hidden`,TextArea 写 `fillHeight resize="none" className="flex-1 min-h-0 w-full"` |
|
|
952
|
+
| 文字被卡片截断 | 标题描述 / 状态说明 / 字段说明右侧被切掉,既没换行也没省略号 | 按 GLOBAL_DESIGN_RULES §8:核心说明 `whitespace-normal break-words` 完整展示;表格/文件名/ID 等非核心长文本 `truncate/line-clamp` 时必须包 `<Tooltip content={fullText}>` |
|
|
953
|
+
| 页面 header 被错误包白卡 | 顶部出现一张和内容卡同等大小的"标题白卡",主从混乱、气息感丢失 | 按 § 1.5:页面 header(标题 + 描述 + 主次操作)**直接坐灰底**,只有真正的工作区内容才包白卡 |
|
|
954
|
+
| 白卡被灰描边圈死 | 多张白卡共屏时灰边互相干扰,气息感丢失,看着像旧式 admin 后台 | 删除最外层白卡上的 `border` / `outline` / `ring`——白卡靠"灰底 vs 白卡的明度差 + 12px 圆角"承担层级,⛔ 严禁加描边(详见 GLOBAL_DESIGN_RULES § 5.5) |
|
|
955
|
+
|
|
956
|
+
|
|
957
|
+
---
|
|
958
|
+
|
|
959
|
+
## 12. 黄金骨架代码(拷贝即用)
|
|
960
|
+
|
|
961
|
+
### 12.1 标准列表页(单白卡)
|
|
962
|
+
|
|
963
|
+
适用:变量管理、任务列表、配置列表等。
|
|
964
|
+
|
|
965
|
+
```jsx
|
|
966
|
+
<div
|
|
967
|
+
className="h-dvh w-full min-w-0 flex flex-col overflow-hidden"
|
|
968
|
+
style={{ background: 'var(--color-blueGrey-200)' }}
|
|
969
|
+
>
|
|
970
|
+
<Topbar />
|
|
971
|
+
|
|
972
|
+
<main className="flex-1 min-h-0 flex flex-col p-4 gap-2 overflow-hidden">
|
|
973
|
+
{/* 单一白卡 */}
|
|
974
|
+
<div
|
|
975
|
+
className="flex-1 min-h-0 flex flex-col overflow-hidden"
|
|
976
|
+
style={{ background: 'var(--color-surface)', borderRadius: 'var(--radius-lg)' }}
|
|
977
|
+
>
|
|
978
|
+
{/* 标题栏 */}
|
|
979
|
+
<div className="shrink-0 px-6 py-4">
|
|
980
|
+
<FormTitle variant="level-1" title="变量管理" showDescription={false} />
|
|
981
|
+
</div>
|
|
982
|
+
|
|
983
|
+
{/* 筛选栏 */}
|
|
984
|
+
<div
|
|
985
|
+
className="shrink-0 px-6 py-3 flex items-center gap-3"
|
|
986
|
+
style={{ borderTop: '1px solid var(--color-border-line-light)' }}
|
|
987
|
+
>
|
|
988
|
+
<Input placeholder="搜索变量名" style={{ width: 240 }} />
|
|
989
|
+
<Select placeholder="状态" style={{ width: 160 }} options={...} />
|
|
990
|
+
</div>
|
|
991
|
+
|
|
992
|
+
{/* 表格(内部滚动) */}
|
|
993
|
+
<div className="flex-1 min-h-0 overflow-auto">
|
|
994
|
+
<Table ... />
|
|
995
|
+
</div>
|
|
996
|
+
|
|
997
|
+
{/* 分页 */}
|
|
998
|
+
<div
|
|
999
|
+
className="shrink-0 px-6 py-3"
|
|
1000
|
+
style={{ borderTop: '1px solid var(--color-border-line-light)' }}
|
|
1001
|
+
>
|
|
1002
|
+
<Pagination ... />
|
|
1003
|
+
</div>
|
|
1004
|
+
</div>
|
|
1005
|
+
</main>
|
|
1006
|
+
</div>
|
|
1007
|
+
```
|
|
1008
|
+
|
|
1009
|
+
### 12.2 双白卡分栏页
|
|
1010
|
+
|
|
1011
|
+
适用:MCP 工具管理、知识库分类、左侧维度筛选 + 右主表。
|
|
1012
|
+
|
|
1013
|
+
```jsx
|
|
1014
|
+
const [leftCardWidth, setLeftCardWidth] = useState(260);
|
|
1015
|
+
|
|
1016
|
+
<div
|
|
1017
|
+
className="h-dvh w-full min-w-0 flex flex-col overflow-hidden"
|
|
1018
|
+
style={{ background: 'var(--color-blueGrey-200)' }}
|
|
1019
|
+
>
|
|
1020
|
+
<Topbar />
|
|
1021
|
+
|
|
1022
|
+
<main className="flex-1 min-h-0 flex flex-row p-4 gap-2 overflow-hidden min-w-0">
|
|
1023
|
+
{/* 左白卡:默认支持拖拽调宽 */}
|
|
1024
|
+
<aside
|
|
1025
|
+
className="shrink-0 flex flex-col overflow-visible"
|
|
1026
|
+
style={{ width: `${leftCardWidth}px`, background: 'var(--color-surface)', borderRadius: 'var(--radius-lg)' }}
|
|
1027
|
+
>
|
|
1028
|
+
<TagBar
|
|
1029
|
+
tone="transparent"
|
|
1030
|
+
width={leftCardWidth}
|
|
1031
|
+
onWidthChange={setLeftCardWidth}
|
|
1032
|
+
minWidth={200}
|
|
1033
|
+
maxWidth={360}
|
|
1034
|
+
resizable
|
|
1035
|
+
/>
|
|
1036
|
+
</aside>
|
|
1037
|
+
|
|
1038
|
+
{/* 右白卡:弹性 */}
|
|
1039
|
+
<section
|
|
1040
|
+
className="flex-1 min-h-0 min-w-0 flex flex-col overflow-hidden"
|
|
1041
|
+
style={{ background: 'var(--color-surface)', borderRadius: 'var(--radius-lg)' }}
|
|
1042
|
+
>
|
|
1043
|
+
{/* 主内容继续按列表页骨架 */}
|
|
1044
|
+
</section>
|
|
1045
|
+
</main>
|
|
1046
|
+
</div>
|
|
1047
|
+
```
|
|
1048
|
+
|
|
1049
|
+
### 12.3 IM 会话页
|
|
1050
|
+
|
|
1051
|
+
适用:客服会话、AI 对话、Copilot。背景必须先按场景判断:常规 IM / 客服进线 / 人工接待默认白色会话背景;AI 问答 / 执行进度 / Copilot / 流程跟进优先浅灰背景。气泡色随背景定:灰底用左灰右白,白底用左灰右绿或右 AI,禁止白底上叠无描边白气泡。
|
|
1052
|
+
|
|
1053
|
+
#### 12.3.1 左侧 ConversationList(可拖拽 & 可收起为纯头像)布局铁律
|
|
1054
|
+
|
|
1055
|
+
当页面存在多线程/多工单队列时,左侧用 `ConversationList` 承载对象切换;它支持拖拽调整宽度,并可收起为纯头像(宽度约 88px)。**收起时右侧主内容区必须自动撑宽**,这是 IM 工作台的硬体验门槛。
|
|
1056
|
+
|
|
1057
|
+
- ✅ 正确:外层用 **flex 两栏**,左栏 `shrink-0`,右栏 `flex-1 min-w-0`
|
|
1058
|
+
- ⛔ 禁止:把左栏写成固定宽度网格列(如 `grid-cols-[400px_1fr]`)或外层固定 `w-[400px]`,否则会出现“内容缩了但列不缩”的大空白,右侧无法自适应扩展
|
|
1059
|
+
- ✅ 若必须用 grid:左列必须是 `auto`(`grid-cols-[auto_1fr]`)
|
|
1060
|
+
|
|
1061
|
+
推荐骨架(直接照抄):
|
|
1062
|
+
|
|
1063
|
+
```jsx
|
|
1064
|
+
<main className="flex-1 min-h-0 flex overflow-hidden">
|
|
1065
|
+
<aside className="shrink-0 h-full min-h-0 overflow-hidden">
|
|
1066
|
+
<ConversationList />
|
|
1067
|
+
</aside>
|
|
1068
|
+
|
|
1069
|
+
<section className="flex-1 min-w-0 min-h-0 flex flex-col overflow-hidden">
|
|
1070
|
+
{/* 右侧主工作区:白卡/消息/表单/详情按各自骨架自滚动 */}
|
|
1071
|
+
</section>
|
|
1072
|
+
</main>
|
|
1073
|
+
```
|
|
1074
|
+
|
|
1075
|
+
```jsx
|
|
1076
|
+
<div
|
|
1077
|
+
className="h-dvh w-full min-w-0 flex flex-col overflow-hidden"
|
|
1078
|
+
style={{ background: 'var(--color-blueGrey-200)' }}
|
|
1079
|
+
>
|
|
1080
|
+
<Topbar />
|
|
1081
|
+
|
|
1082
|
+
<main className="flex-1 min-h-0 flex flex-col overflow-hidden">
|
|
1083
|
+
{/* 消息区:自身滚动 */}
|
|
1084
|
+
<div className="flex-1 min-h-0 overflow-hidden">
|
|
1085
|
+
<IMConversationPattern messages={messages} />
|
|
1086
|
+
</div>
|
|
1087
|
+
|
|
1088
|
+
{/* 输入框:吸底(不在滚动容器内) */}
|
|
1089
|
+
<div className="shrink-0 px-6 pb-4">
|
|
1090
|
+
<ChatInput value={value} onChange={setValue} onSend={handleSend} />
|
|
1091
|
+
</div>
|
|
1092
|
+
</main>
|
|
1093
|
+
</div>
|
|
1094
|
+
```
|
|
1095
|
+
|
|
1096
|
+
### 12.4 网格工作台 / 入口聚合首页(**仅** Dashboard 类)
|
|
1097
|
+
|
|
1098
|
+
适用:产品首页、功能入口、KPI 看板——**用户在这一屏只是堆叠浏览**,没有面板内编辑/输入/对照工作流。
|
|
1099
|
+
|
|
1100
|
+
⚠️ **何时不要用本骨架**(高频误用):
|
|
1101
|
+
|
|
1102
|
+
- ❌ Model Playground、Prompt Editor、API 调试器、规则编辑器 → 改用 § 3.4 / § 12.7
|
|
1103
|
+
- ❌ 任何"输入区 + 输出区 + 参数区"对照工作的页面 → 改用 § 3.4 / § 12.7
|
|
1104
|
+
|
|
1105
|
+
**判别口诀**:本骨架用 `<main className="... overflow-y-auto">` 让整页滚动;如果你的页面里**有任何一张卡片承担"工作区"角色(用户在面板内输入/编辑/调参/查看结果)**,整页滚就是错的,必须改用 § 3.4。
|
|
1106
|
+
|
|
1107
|
+
```jsx
|
|
1108
|
+
<div
|
|
1109
|
+
className="h-dvh w-full min-w-0 flex flex-col overflow-hidden"
|
|
1110
|
+
style={{ background: 'var(--color-blueGrey-200)' }}
|
|
1111
|
+
>
|
|
1112
|
+
<Topbar />
|
|
1113
|
+
|
|
1114
|
+
<main className="flex-1 min-h-0 overflow-y-auto">
|
|
1115
|
+
<div className="flex flex-col gap-2 p-4">
|
|
1116
|
+
|
|
1117
|
+
{/* KPI 区:自适应网格 */}
|
|
1118
|
+
<section
|
|
1119
|
+
className="grid gap-4"
|
|
1120
|
+
style={{
|
|
1121
|
+
gridTemplateColumns: 'repeat(auto-fit, minmax(min(240px, 100%), 1fr))',
|
|
1122
|
+
}}
|
|
1123
|
+
>
|
|
1124
|
+
{kpis.map(k => (
|
|
1125
|
+
<div
|
|
1126
|
+
key={k.id}
|
|
1127
|
+
className="flex flex-col gap-2"
|
|
1128
|
+
style={{
|
|
1129
|
+
background: 'var(--color-surface)',
|
|
1130
|
+
borderRadius: 'var(--radius-lg)',
|
|
1131
|
+
padding: 'var(--spacing-6)',
|
|
1132
|
+
}}
|
|
1133
|
+
>
|
|
1134
|
+
{/* KPI 卡内容 */}
|
|
1135
|
+
</div>
|
|
1136
|
+
))}
|
|
1137
|
+
</section>
|
|
1138
|
+
|
|
1139
|
+
{/* 功能入口区:自适应网格 */}
|
|
1140
|
+
<section
|
|
1141
|
+
className="grid gap-4"
|
|
1142
|
+
style={{
|
|
1143
|
+
gridTemplateColumns: 'repeat(auto-fit, minmax(min(320px, 100%), 1fr))',
|
|
1144
|
+
}}
|
|
1145
|
+
>
|
|
1146
|
+
{entries.map(e => <Card key={e.id} color="white">{...}</Card>)}
|
|
1147
|
+
</section>
|
|
1148
|
+
</div>
|
|
1149
|
+
</main>
|
|
1150
|
+
</div>
|
|
1151
|
+
```
|
|
1152
|
+
|
|
1153
|
+
### 12.5 沉浸式详情/编辑器页(纯白底,非灰底分区)
|
|
1154
|
+
|
|
1155
|
+
适用:富文本编辑器、文档详情、Prompt 编排器、长篇配置页。
|
|
1156
|
+
|
|
1157
|
+
⚠️ **不是**所有 B 端页都是「灰底+白卡」;编辑器型页面应**沉浸白底**:
|
|
1158
|
+
|
|
1159
|
+
```jsx
|
|
1160
|
+
<div
|
|
1161
|
+
className="h-dvh w-full min-w-0 flex flex-col overflow-hidden"
|
|
1162
|
+
style={{ background: 'var(--color-surface)' }} /* ← 白底,不是灰底 */
|
|
1163
|
+
>
|
|
1164
|
+
<Topbar />
|
|
1165
|
+
|
|
1166
|
+
{/* 编辑器主区,无白卡分隔 */}
|
|
1167
|
+
<main className="flex-1 min-h-0 flex flex-row overflow-hidden">
|
|
1168
|
+
{/* 左侧大纲(可选,浅灰底) */}
|
|
1169
|
+
<aside
|
|
1170
|
+
className="shrink-0 w-[280px] overflow-y-auto"
|
|
1171
|
+
style={{ background: 'var(--color-blueGrey-100)' }}
|
|
1172
|
+
>
|
|
1173
|
+
{/* 大纲 */}
|
|
1174
|
+
</aside>
|
|
1175
|
+
|
|
1176
|
+
{/* 中部内容:宽屏可加 max-w 防止单行过长 */}
|
|
1177
|
+
<section className="flex-1 min-w-0 overflow-y-auto">
|
|
1178
|
+
<article className="max-w-[960px] mx-auto px-8 py-10">
|
|
1179
|
+
{/* 富文本/编辑器 */}
|
|
1180
|
+
</article>
|
|
1181
|
+
</section>
|
|
1182
|
+
|
|
1183
|
+
{/* 右侧元数据面板(可选) */}
|
|
1184
|
+
<aside
|
|
1185
|
+
className="shrink-0 w-[320px] overflow-y-auto"
|
|
1186
|
+
style={{ borderLeft: '1px solid var(--color-border-line-light)' }}
|
|
1187
|
+
>
|
|
1188
|
+
{/* 元数据/属性 */}
|
|
1189
|
+
</aside>
|
|
1190
|
+
</main>
|
|
1191
|
+
</div>
|
|
1192
|
+
```
|
|
1193
|
+
|
|
1194
|
+
### 12.6 NavBar + Topbar 双层导航
|
|
1195
|
+
|
|
1196
|
+
适用:产品级三段交叉布局(左侧 NavBar 固定 + 顶 Topbar + 主区)。
|
|
1197
|
+
|
|
1198
|
+
**先决条件**:
|
|
1199
|
+
|
|
1200
|
+
- 命中 `NavBar` 时,优先套这一类 app shell 骨架,而不是通用纵向页面骨架
|
|
1201
|
+
- `NavBar` 始终在最左侧,`Topbar` 只能放在右侧主区顶部
|
|
1202
|
+
- `NavBar` 不进白卡;白卡只出现在右侧主区内部
|
|
1203
|
+
|
|
1204
|
+
```jsx
|
|
1205
|
+
<div
|
|
1206
|
+
className="h-dvh w-full min-w-0 flex flex-row overflow-hidden"
|
|
1207
|
+
style={{ background: 'var(--color-blueGrey-200)' }}
|
|
1208
|
+
>
|
|
1209
|
+
{/* 左侧 NavBar:固定宽,全高 */}
|
|
1210
|
+
<NavBar className="shrink-0" />
|
|
1211
|
+
|
|
1212
|
+
{/* 右侧主区:纵向三段 */}
|
|
1213
|
+
<div className="flex-1 min-w-0 flex flex-col overflow-hidden">
|
|
1214
|
+
{/* 顶 Topbar */}
|
|
1215
|
+
<header className="shrink-0">
|
|
1216
|
+
<Topbar />
|
|
1217
|
+
</header>
|
|
1218
|
+
|
|
1219
|
+
{/* 中部主内容 */}
|
|
1220
|
+
<main className="flex-1 min-h-0 flex flex-col p-4 gap-2 overflow-hidden">
|
|
1221
|
+
{/* 这里嵌套 § 12.1 / 12.2 / 12.4 任意骨架 */}
|
|
1222
|
+
</main>
|
|
1223
|
+
</div>
|
|
1224
|
+
</div>
|
|
1225
|
+
```
|
|
1226
|
+
|
|
1227
|
+
### 12.7 三栏 Playground / 多面板工作台(IDE 型)
|
|
1228
|
+
|
|
1229
|
+
适用:Model Playground、Prompt Editor、API 调试器、规则编辑器——多个独立工作区面板共享一屏,**整页绝不滚,每张面板独立全高 + 独立滚动**。
|
|
1230
|
+
|
|
1231
|
+
```jsx
|
|
1232
|
+
<div
|
|
1233
|
+
className="h-dvh w-full min-w-0 flex flex-col overflow-hidden"
|
|
1234
|
+
style={{ background: 'var(--color-blueGrey-200)' }}
|
|
1235
|
+
>
|
|
1236
|
+
<Topbar />
|
|
1237
|
+
|
|
1238
|
+
{/* ✅ 页面 header:直接坐灰底,绝不包白卡(参照 § 1.5) */}
|
|
1239
|
+
<header className="shrink-0 px-4 pt-3 pb-2 flex items-center justify-between gap-3">
|
|
1240
|
+
<div className="flex items-center gap-3 min-w-0">
|
|
1241
|
+
{/* 可选:返回按钮 / 面包屑 */}
|
|
1242
|
+
<FormTitle
|
|
1243
|
+
variant="form"
|
|
1244
|
+
title="Model Playground"
|
|
1245
|
+
titleSuffix={<Tag variant="success" size="m">TFDS</Tag>}
|
|
1246
|
+
description="保持原始工作台分区不变,使用 TFDS 组件重构交互与布局容器"
|
|
1247
|
+
showDescription
|
|
1248
|
+
/>
|
|
1249
|
+
</div>
|
|
1250
|
+
<div className="shrink-0 flex items-center gap-2">
|
|
1251
|
+
<Button variant="outline-black">查看源稿</Button>
|
|
1252
|
+
<Button variant="primary">导入信息</Button>
|
|
1253
|
+
</div>
|
|
1254
|
+
</header>
|
|
1255
|
+
|
|
1256
|
+
{/* 主区横向三栏:左 Workspace + 中 Conversation + 右 Settings/Result(上下叠) */}
|
|
1257
|
+
<main className="flex-1 min-h-0 flex flex-row px-4 pb-4 gap-2 overflow-hidden min-w-0">
|
|
1258
|
+
|
|
1259
|
+
{/* ── 左面板:Prompt Workspace ── */}
|
|
1260
|
+
<section
|
|
1261
|
+
className="flex-1 min-h-0 min-w-0 flex flex-col overflow-hidden"
|
|
1262
|
+
style={{ background: 'var(--color-surface)', borderRadius: 'var(--radius-lg)' }}
|
|
1263
|
+
>
|
|
1264
|
+
<header
|
|
1265
|
+
className="shrink-0 px-6 py-4 flex items-center justify-between"
|
|
1266
|
+
style={{ borderBottom: '1px solid var(--color-border-line-light)' }}
|
|
1267
|
+
>
|
|
1268
|
+
{/* ⚠️ 面板/卡内 section 标题永远用 FormTitle level-2,⛔ 不要手搓 div+h3 */}
|
|
1269
|
+
<FormTitle
|
|
1270
|
+
variant="level-2"
|
|
1271
|
+
title="Prompt Workspace"
|
|
1272
|
+
description="结构化信息支持组件化编辑。"
|
|
1273
|
+
/>
|
|
1274
|
+
{/* 内容区 / 白卡内 Tabs 默认 SM;仅平台顶部 header 可用 MD/LG */}
|
|
1275
|
+
<Tabs variant="segment" size="sm" items={[...]} />
|
|
1276
|
+
</header>
|
|
1277
|
+
{/* 面板正文:标题固定;主编辑控件撑满剩余高度并在控件内部滚 */}
|
|
1278
|
+
<div className="flex-1 min-h-0 overflow-hidden px-6 py-4 flex flex-col gap-4">
|
|
1279
|
+
<section className="flex-1 min-h-0 flex flex-col overflow-hidden">
|
|
1280
|
+
<header className="shrink-0 pb-3">
|
|
1281
|
+
<FormTitle variant="level-2" title="System Prompt" description="使用 TFDS TextArea 重构编辑区。" showDescription />
|
|
1282
|
+
</header>
|
|
1283
|
+
<TextArea variant="code" fillHeight resize="none" className="flex-1 min-h-0 w-full" />
|
|
1284
|
+
</section>
|
|
1285
|
+
</div>
|
|
1286
|
+
</section>
|
|
1287
|
+
|
|
1288
|
+
{/* ── 中面板:Conversation ── */}
|
|
1289
|
+
<section
|
|
1290
|
+
className="flex-1 min-h-0 min-w-0 flex flex-col overflow-hidden"
|
|
1291
|
+
style={{ background: 'var(--color-surface)', borderRadius: 'var(--radius-lg)' }}
|
|
1292
|
+
>
|
|
1293
|
+
<header className="shrink-0 px-6 py-4">
|
|
1294
|
+
<FormTitle
|
|
1295
|
+
variant="level-2"
|
|
1296
|
+
title="Conversation"
|
|
1297
|
+
titleSuffix={<Tag variant="grey" size="m">0 条消息</Tag>}
|
|
1298
|
+
description="消息区与底部输入区统一重构为 TFDS 会话交互风格。"
|
|
1299
|
+
/>
|
|
1300
|
+
</header>
|
|
1301
|
+
<div className="flex-1 min-h-0 overflow-y-auto px-6 pb-4">
|
|
1302
|
+
{/* IM/Chat 消息流 */}
|
|
1303
|
+
</div>
|
|
1304
|
+
</section>
|
|
1305
|
+
|
|
1306
|
+
{/* ── 右栏:上下两个子面板 ── */}
|
|
1307
|
+
<aside className="shrink-0 w-[360px] flex flex-col gap-2 overflow-hidden">
|
|
1308
|
+
{/* 上:Model Settings — 高度跟随表单内容(shrink-0) */}
|
|
1309
|
+
<div
|
|
1310
|
+
className="shrink-0 flex flex-col overflow-hidden"
|
|
1311
|
+
style={{ background: 'var(--color-surface)', borderRadius: 'var(--radius-lg)' }}
|
|
1312
|
+
>
|
|
1313
|
+
<header className="shrink-0 px-6 py-4">
|
|
1314
|
+
<FormTitle variant="level-2" title="Model Settings" showDescription={false} />
|
|
1315
|
+
</header>
|
|
1316
|
+
<div className="px-6 pb-6 flex flex-col gap-4">
|
|
1317
|
+
{/* InputNumber / Select / Switch 等参数 */}
|
|
1318
|
+
</div>
|
|
1319
|
+
</div>
|
|
1320
|
+
|
|
1321
|
+
{/* 下:Run Result — 占满剩余空间(flex-1 min-h-0),内部独立滚动 */}
|
|
1322
|
+
<div
|
|
1323
|
+
className="flex-1 min-h-0 flex flex-col overflow-hidden"
|
|
1324
|
+
style={{ background: 'var(--color-surface)', borderRadius: 'var(--radius-lg)' }}
|
|
1325
|
+
>
|
|
1326
|
+
<header className="shrink-0 px-6 py-4">
|
|
1327
|
+
<FormTitle variant="level-2" title="Run Result" showDescription={false} />
|
|
1328
|
+
</header>
|
|
1329
|
+
<div className="flex-1 min-h-0 overflow-y-auto px-6 pb-6">
|
|
1330
|
+
{/* 模型参数 / Input / Output / Assistant Tabs + JSON 展示 */}
|
|
1331
|
+
</div>
|
|
1332
|
+
</div>
|
|
1333
|
+
</aside>
|
|
1334
|
+
</main>
|
|
1335
|
+
</div>
|
|
1336
|
+
```
|
|
1337
|
+
|
|
1338
|
+
**自检要点**(按上到下逐层检查嵌套):
|
|
1339
|
+
|
|
1340
|
+
```
|
|
1341
|
+
h-dvh + overflow-hidden ← 根容器永不滚
|
|
1342
|
+
└─ main: flex-1 min-h-0 + overflow-hidden ← 主区永不滚
|
|
1343
|
+
├─ section(横向子项)
|
|
1344
|
+
│ └─ flex-1 min-h-0 min-w-0 + flex-col + overflow-hidden
|
|
1345
|
+
│ ├─ header: shrink-0
|
|
1346
|
+
│ └─ body: flex-1 min-h-0 overflow-y-auto ← 真正滚动层
|
|
1347
|
+
└─ aside(横向子项)
|
|
1348
|
+
└─ shrink-0 w-[Npx] + flex-col + gap
|
|
1349
|
+
├─ 子面板 A(高度跟随内容): shrink-0
|
|
1350
|
+
└─ 子面板 B(撑满剩余): flex-1 min-h-0 + overflow-hidden
|
|
1351
|
+
├─ header: shrink-0
|
|
1352
|
+
└─ body: flex-1 min-h-0 overflow-y-auto
|
|
1353
|
+
```
|
|
1354
|
+
|
|
1355
|
+
⛔ 如果上述任一层出现 `overflow-y-auto` 写在错误位置(如 main、section 头部、wrapper)或漏写 `min-h-0`,整页就会一起滚——这正是 Model Playground 高度问题的根因。
|