363 lines
9.9 KiB
Markdown
363 lines
9.9 KiB
Markdown
# Z-Index 层级管理规范
|
||
|
||
## 当前问题分析
|
||
|
||
经过代码审查,发现以下z-index混乱问题:
|
||
1. **unified-toolbar (1000)** 遮挡了弹窗和popover
|
||
2. **popup/popover (1000)** 与toolbar使用相同层级
|
||
3. **AI弹窗 (2000)** 被unified-toolbar遮挡
|
||
4. **多个组件使用1000** 导致层级冲突
|
||
5. **临时修复层出现9999、10000等随意值**
|
||
|
||
## Z-Index 层级规范
|
||
|
||
采用**分层设计**,每层预留100个单位空间:
|
||
|
||
```
|
||
┌─────────────────────────────────────────────┐
|
||
│ Layer 9: Critical Overlays (9000+) │ 全局最高层
|
||
├─────────────────────────────────────────────┤
|
||
│ Layer 8: Image Viewer (8000-8999) │ 图片查看器
|
||
├─────────────────────────────────────────────┤
|
||
│ Layer 7: Auth Dialogs (7000-7999) │ 认证弹窗
|
||
├─────────────────────────────────────────────┤
|
||
│ Layer 6: Notifications (6000-6999) │ 通知提示
|
||
├─────────────────────────────────────────────┤
|
||
│ Layer 5: Modals (5000-5999) │ 模态弹窗
|
||
├─────────────────────────────────────────────┤
|
||
│ Layer 4: Drawers/Panels (4000-4999) │ 抽屉/面板
|
||
├─────────────────────────────────────────────┤
|
||
│ Layer 3: Popovers (3000-3999) │ 弹出层
|
||
├─────────────────────────────────────────────┤
|
||
│ Layer 2: Toolbars (2000-2999) │ 工具栏
|
||
├─────────────────────────────────────────────┤
|
||
│ Layer 1: Canvas Elements (1000-1999) │ 画布元素
|
||
├─────────────────────────────────────────────┤
|
||
│ Layer 0: Base (0-999) │ 基础层
|
||
└─────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 详细层级定义
|
||
|
||
#### Layer 0: Base (0-999)
|
||
- **0-99**: 默认文档流
|
||
- **100-199**: 画布内部元素(selection, drag handlers)
|
||
- **200-299**: 预留
|
||
|
||
#### Layer 1: Canvas Elements (1000-1999)
|
||
- **1000-1099**: 画布装饰元素(网格、辅助线)
|
||
- **1100-1199**: 临时元素(resize handles, anchors)
|
||
- **1200-1299**: 预留
|
||
|
||
#### Layer 2: Toolbars (2000-2999)
|
||
- **2000**: unified-toolbar (主工具栏)
|
||
- **2010**: creation-toolbar
|
||
- **2020**: popup-toolbar
|
||
- **2030**: zoom-toolbar
|
||
- **2040**: app-toolbar
|
||
- **2050**: pencil-mode-toolbar
|
||
- **2100-2199**: 其他工具栏
|
||
|
||
#### Layer 3: Popovers (3000-3999)
|
||
- **3000**: 工具栏popover (freehand-panel, shape-picker, arrow-picker)
|
||
- **3010**: feedback-button popover
|
||
- **3020**: zoom-toolbar popover
|
||
- **3030**: app-toolbar popover
|
||
- **3100-3199**: 其他popover
|
||
- **3500**: Tooltip (始终在popover之上)
|
||
|
||
#### Layer 4: Drawers/Panels (4000-4999)
|
||
- **4000**: task-queue-panel
|
||
- **4010**: chat-drawer
|
||
- **4020**: generation-history drawer
|
||
- **4100-4199**: 其他侧边栏
|
||
|
||
#### Layer 5: Modals (5000-5999)
|
||
- **5000**: 普通Dialog (ttd-dialog)
|
||
- **5010**: clean-confirm
|
||
- **5020**: settings-dialog
|
||
- **5100**: AI Image Generation Dialog (react-rnd窗口)
|
||
- **5110**: AI Video Generation Dialog (react-rnd窗口)
|
||
- **5200-5299**: Dialog内部元素(header, footer等)
|
||
- **5500-5599**: Dialog内的popover/select
|
||
|
||
#### Layer 6: Notifications (6000-6999)
|
||
- **6000**: active-task-warning
|
||
- **6100**: Toast通知
|
||
- **6200**: 成功/错误提示
|
||
|
||
#### Layer 7: Auth Dialogs (7000-7999)
|
||
- **7000**: API key auth dialog
|
||
- **7100**: 登录/注册弹窗
|
||
|
||
#### Layer 8: Image Viewer (8000-8999)
|
||
- **8000**: Image viewer overlay
|
||
- **8010**: Image viewer toolbar
|
||
- **8020**: Image viewer close button
|
||
|
||
#### Layer 9: Critical Overlays (9000+)
|
||
- **9000**: Loading spinner (全屏)
|
||
- **9100**: 系统级错误提示
|
||
- **9999**: 开发调试层
|
||
|
||
## 实施规则
|
||
|
||
### 1. 使用CSS变量
|
||
```scss
|
||
// styles/variables.scss
|
||
$z-index: (
|
||
// Layer 0: Base
|
||
'canvas-internal': 100,
|
||
|
||
// Layer 1: Canvas Elements
|
||
'canvas-decoration': 1000,
|
||
'canvas-temporary': 1100,
|
||
|
||
// Layer 2: Toolbars
|
||
'unified-toolbar': 2000,
|
||
'creation-toolbar': 2010,
|
||
'popup-toolbar': 2020,
|
||
'zoom-toolbar': 2030,
|
||
'app-toolbar': 2040,
|
||
'pencil-toolbar': 2050,
|
||
|
||
// Layer 3: Popovers
|
||
'popover': 3000,
|
||
'popover-feedback': 3010,
|
||
'popover-zoom': 3020,
|
||
'popover-app': 3030,
|
||
'tooltip': 3500,
|
||
|
||
// Layer 4: Drawers
|
||
'task-queue-panel': 4000,
|
||
'chat-drawer': 4010,
|
||
'generation-history': 4020,
|
||
|
||
// Layer 5: Modals
|
||
'dialog': 5000,
|
||
'dialog-clean-confirm': 5010,
|
||
'dialog-settings': 5020,
|
||
'dialog-ai-image': 5100,
|
||
'dialog-ai-video': 5110,
|
||
'dialog-inner': 5200,
|
||
'dialog-popover': 5500,
|
||
|
||
// Layer 6: Notifications
|
||
'active-task-warning': 6000,
|
||
'toast': 6100,
|
||
'message': 6200,
|
||
|
||
// Layer 7: Auth
|
||
'auth-dialog': 7000,
|
||
|
||
// Layer 8: Image Viewer
|
||
'image-viewer': 8000,
|
||
'image-viewer-toolbar': 8010,
|
||
'image-viewer-close': 8020,
|
||
|
||
// Layer 9: Critical
|
||
'loading': 9000,
|
||
'system-error': 9100,
|
||
'debug': 9999,
|
||
);
|
||
|
||
// 辅助函数
|
||
@function z($layer) {
|
||
@return map-get($z-index, $layer);
|
||
}
|
||
```
|
||
|
||
### 2. TypeScript常量
|
||
```typescript
|
||
// constants/z-index.ts
|
||
export const Z_INDEX = {
|
||
// Layer 0: Base
|
||
CANVAS_INTERNAL: 100,
|
||
|
||
// Layer 1: Canvas Elements
|
||
CANVAS_DECORATION: 1000,
|
||
CANVAS_TEMPORARY: 1100,
|
||
|
||
// Layer 2: Toolbars
|
||
UNIFIED_TOOLBAR: 2000,
|
||
CREATION_TOOLBAR: 2010,
|
||
POPUP_TOOLBAR: 2020,
|
||
ZOOM_TOOLBAR: 2030,
|
||
APP_TOOLBAR: 2040,
|
||
PENCIL_TOOLBAR: 2050,
|
||
|
||
// Layer 3: Popovers
|
||
POPOVER: 3000,
|
||
POPOVER_FEEDBACK: 3010,
|
||
POPOVER_ZOOM: 3020,
|
||
POPOVER_APP: 3030,
|
||
TOOLTIP: 3500,
|
||
|
||
// Layer 4: Drawers
|
||
TASK_QUEUE_PANEL: 4000,
|
||
CHAT_DRAWER: 4010,
|
||
GENERATION_HISTORY: 4020,
|
||
|
||
// Layer 5: Modals
|
||
DIALOG: 5000,
|
||
DIALOG_CLEAN_CONFIRM: 5010,
|
||
DIALOG_SETTINGS: 5020,
|
||
DIALOG_AI_IMAGE: 5100,
|
||
DIALOG_AI_VIDEO: 5110,
|
||
DIALOG_INNER: 5200,
|
||
DIALOG_POPOVER: 5500,
|
||
|
||
// Layer 6: Notifications
|
||
ACTIVE_TASK_WARNING: 6000,
|
||
TOAST: 6100,
|
||
MESSAGE: 6200,
|
||
|
||
// Layer 7: Auth
|
||
AUTH_DIALOG: 7000,
|
||
|
||
// Layer 8: Image Viewer
|
||
IMAGE_VIEWER: 8000,
|
||
IMAGE_VIEWER_TOOLBAR: 8010,
|
||
IMAGE_VIEWER_CLOSE: 8020,
|
||
|
||
// Layer 9: Critical
|
||
LOADING: 9000,
|
||
SYSTEM_ERROR: 9100,
|
||
DEBUG: 9999,
|
||
} as const;
|
||
|
||
export type ZIndexKey = keyof typeof Z_INDEX;
|
||
```
|
||
|
||
### 3. 使用示例
|
||
|
||
#### SCSS中使用
|
||
```scss
|
||
@import 'styles/variables';
|
||
|
||
.unified-toolbar {
|
||
z-index: z('unified-toolbar'); // 2000
|
||
}
|
||
|
||
.my-popover {
|
||
z-index: z('popover'); // 3000
|
||
}
|
||
```
|
||
|
||
#### TSX中使用
|
||
```tsx
|
||
import { Z_INDEX } from '@/constants/z-index';
|
||
|
||
<Rnd style={{ zIndex: Z_INDEX.DIALOG_AI_IMAGE }}>
|
||
{/* AI Image Dialog */}
|
||
</Rnd>
|
||
|
||
<PopoverContent style={{ zIndex: Z_INDEX.POPOVER }}>
|
||
{/* Popover content */}
|
||
</PopoverContent>
|
||
```
|
||
|
||
## 禁止事项
|
||
|
||
❌ **禁止随意使用魔术数字**
|
||
```scss
|
||
// ❌ 错误
|
||
.my-component {
|
||
z-index: 9999; // 不要使用随意的大数字
|
||
}
|
||
|
||
.another {
|
||
z-index: 10001; // 临时修复会破坏层级体系
|
||
}
|
||
```
|
||
|
||
✅ **正确做法**
|
||
```scss
|
||
// ✅ 正确
|
||
@import 'styles/variables';
|
||
|
||
.my-component {
|
||
z-index: z('dialog'); // 使用预定义的层级
|
||
}
|
||
```
|
||
|
||
❌ **禁止在同一层级随意加减**
|
||
```scss
|
||
// ❌ 错误
|
||
.toolbar {
|
||
z-index: 2000;
|
||
}
|
||
.toolbar-fixed {
|
||
z-index: 2001; // 破坏了层级结构
|
||
}
|
||
```
|
||
|
||
✅ **正确做法**
|
||
```scss
|
||
// ✅ 正确 - 如果需要层内排序,使用DOM顺序或position
|
||
.toolbar {
|
||
z-index: z('unified-toolbar');
|
||
}
|
||
.toolbar-fixed {
|
||
z-index: z('unified-toolbar');
|
||
position: relative; // DOM顺序决定层级
|
||
}
|
||
```
|
||
|
||
## Code Review检查清单
|
||
|
||
在代码审查时,检查以下项目:
|
||
|
||
- [ ] 所有z-index使用都通过变量/常量引用
|
||
- [ ] 没有硬编码的魔术数字
|
||
- [ ] 新增组件的z-index符合层级规范
|
||
- [ ] 没有超过9999的z-index值
|
||
- [ ] Popover/Tooltip使用对应层级(3000+)
|
||
- [ ] Dialog/Modal使用对应层级(5000+)
|
||
- [ ] 临时修复已转换为规范用法
|
||
|
||
## 迁移计划
|
||
|
||
### Phase 1: 创建变量文件
|
||
- [ ] 创建 `styles/z-index.scss`
|
||
- [ ] 创建 `constants/z-index.ts`
|
||
|
||
### Phase 2: 修复关键冲突
|
||
- [ ] unified-toolbar: 1000 → 2000
|
||
- [ ] popovers: 1000 → 3000
|
||
- [ ] task-queue-panel: 999 → 4000
|
||
- [ ] AI dialogs: 2000 → 5100
|
||
|
||
### Phase 3: 全面迁移
|
||
- [ ] 替换所有SCSS中的硬编码z-index
|
||
- [ ] 替换所有TSX中的内联z-index
|
||
- [ ] 移除临时修复(9999, 10000等)
|
||
|
||
### Phase 4: 文档和测试
|
||
- [ ] 更新CODEBUDDY.md
|
||
- [ ] 添加层级可视化测试页面
|
||
- [ ] 团队培训
|
||
|
||
## 常见问题
|
||
|
||
**Q: 如果需要在两个预定义层级之间插入新层级怎么办?**
|
||
A: 每层预留了100个单位空间。例如在popover(3000)和drawer(4000)之间,可以使用3100-3999。
|
||
|
||
**Q: TDesign组件的z-index怎么处理?**
|
||
A: 通过组件的style或className属性覆盖,或使用TDesign的zIndex prop。
|
||
|
||
**Q: react-rnd的默认z-index是多少?**
|
||
A: 默认没有z-index,需要显式设置。统一使用`Z_INDEX.DIALOG_*`系列。
|
||
|
||
**Q: 如何调试z-index问题?**
|
||
A:
|
||
1. 使用浏览器DevTools的3D视图
|
||
2. 临时添加不同背景色区分层级
|
||
3. 使用 `debug: 9999` 层进行调试,完成后移除
|
||
|
||
## 参考资料
|
||
|
||
- [MDN: z-index](https://developer.mozilla.org/en-US/docs/Web/CSS/z-index)
|
||
- [CSS Stacking Context](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_positioned_layout/Understanding_z-index/Stacking_context)
|
||
- [React Portals](https://react.dev/reference/react-dom/createPortal)
|