# WaveformChart 组件拆分计划
## 目标
将 `WaveformChart.vue`(1743 行)拆分为更小的、职责单一的子组件:
1. **WaveformTooltip.vue** - 悬浮提示组件
2. **WaveformTrack.vue** - 单个波形轨道组件
3. **WaveformAnnotationLayer.vue** - 标注层组件
## 当前代码分析
### 主组件职责(过多)
- ✅ 数据管理和状态协调
- ✅ 缩放和交互事件处理
- 🔴 渲染波形轨道(网格、轴、波形线、十字线)
- 🔴 渲染标注和图形
- 🔴 渲染悬浮提示
- ✅ 工具栏管理(已拆分)
- ✅ 编辑器管理(已拆分)
### 模板结构(1055-1743 行)
```vue
```
## 拆分策略
### 组件 1: WaveformTooltip.vue (~80 行)
**职责**:显示鼠标悬浮时的数据点信息
**Props**:
```typescript
interface Props {
visible: boolean
position: { x: number; y: number }
timeUnit: 's' | 'ms'
hoveredPoint: WaveformPoint | null
seriesPoints: Array<{
trackIndex: number
name: string
color: string
unit?: string
point: WaveformPoint
}>
}
```
**提取内容**:
- 模板:1463-1478 行(16 行)
- 样式:1682-1742 行(61 行)
- 计算属性:`tooltipStyle`(471-477 行)
---
### 组件 2: WaveformTrack.vue (~300 行)
**职责**:渲染单个波形轨道(网格、坐标轴、波形线、十字线、overlay)
**Props**:
```typescript
interface Props {
track: TrackLayout
clipPathId: string
margin: { top: number; right: number; bottom: number; left: number }
innerWidth: number
showTooltip: boolean
zoomable: boolean
displayMode: WaveformDisplayMode
activeInteractionMode: WaveformInteractionMode
frameNumber?: string | number
timeUnit: 's' | 'ms'
hoveredPoint?: HoveredSeriesPoint // 用于显示十字线
}
```
**Emits**:
```typescript
interface Emits {
(e: 'pointer-move', event: PointerEvent): void
(e: 'pointer-leave'): void
(e: 'pointer-down', event: PointerEvent): void
(e: 'pointer-up', event: PointerEvent): void
(e: 'pointer-cancel', event: PointerEvent): void
(e: 'click', event: PointerEvent): void
}
```
**提取内容**:
- 模板:1099-1265 行(166 行)
- 相关函数:
- `shouldShowYAxisLabel` (247-262)
- `resolveYAxisLabel` (239-241)
- `resolveFrameNumber` (804-810)
- `crosshairX` (794-797)
- `crosshairY` (799-802)
- `trackHoverPoint` (790-792)
- 样式:部分轨道相关样式
**注意事项**:
- 轨道组件需要在父组件中接收 D3 渲染的坐标轴
- 或者在 `onMounted` 中自己调用 D3 渲染坐标轴
---
### 组件 3: WaveformAnnotationLayer.vue (~250 行)
**职责**:渲染所有标注和图形(annotations + shapes)
**Props**:
```typescript
interface Props {
renderedAnnotations: RenderedAnnotation[]
renderedShapes: RenderedShape[]
renderedRangePreview: RenderedShape[]
activeInteractionMode: WaveformInteractionMode
selection: WaveformMarkupSelection
innerWidth: number
clipPathId: string
}
```
**Emits**:
```typescript
interface Emits {
(e: 'select-markup', kind: 'annotation' | 'shape', id: string): void
(e: 'edit-markup', kind: 'annotation' | 'shape', id: string): void
}
```
**提取内容**:
- 模板:1285-1419 行(135 行)
- 相关函数:
- `isSelected` (491-493)
- `safeDomId` (503-505)
- `arrowMarkerId` (507-509)
- `annotationBoxStyle` (511-518)
- `shapeLabelWidth` (520-522)
- `shapeLabelX` (524-531)
- `shapeLabelStyle` (533-538)
- 样式:标注相关样式(1581-1660 行)
---
## 实施步骤
### 阶段 1: 创建 WaveformTooltip.vue ✅
1. 创建组件文件
2. 提取模板和样式
3. 实现计算属性 `tooltipStyle`
4. 更新主组件使用新组件
### 阶段 2: 创建 WaveformAnnotationLayer.vue ✅
1. 创建组件文件
2. 提取标注层模板
3. 提取相关工具函数
4. 提取样式
5. 更新主组件
### 阶段 3: 创建 WaveformTrack.vue ✅
1. 创建组件文件
2. 提取轨道渲染逻辑
3. 处理 D3 坐标轴渲染(使用 `ref` + `onMounted`)
4. 提取相关样式
5. 更新主组件
### 阶段 4: 验证和测试 ✅
1. 运行类型检查 `pnpm typecheck`
2. 运行代码规范检查 `pnpm lint`
3. 运行单元测试 `pnpm test`
4. 手动测试功能完整性
---
## 设计决策
### 1. 类型共享
将 `DisplaySeries`, `HoveredSeriesPoint`, `TrackLayout`, `RenderedAnnotation`, `RenderedShape` 等接口移到独立的类型文件中,供多个组件使用。
**创建** `src/components/waveform-chart-types.ts`
### 2. D3 渲染策略
对于 WaveformTrack 中的坐标轴渲染:
- **方案 A(推荐)**:在 Track 组件内部使用 `ref` + `onMounted` 调用 D3
- **方案 B**:父组件渲染后通知子组件
- **选择 A**:更符合组件封装原则
### 3. 事件冒泡
所有交互事件(click, pointer-move 等)通过 emit 向上传递,保持主组件的事件协调职责。
### 4. 样式隔离
每个组件使用 `