# 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';
{/* AI Image Dialog */}
{/* Popover content */}
```
## 禁止事项
❌ **禁止随意使用魔术数字**
```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)