2026-07-20 10:21:30 +08:00
|
|
|
|
# WaveformChart 组件拆分完成报告
|
|
|
|
|
|
|
|
|
|
|
|
## 🎯 任务概述
|
|
|
|
|
|
|
|
|
|
|
|
将 `WaveformChart.vue` 主组件(1743 行)拆分为更小的、职责单一的子组件,降低复杂度,提高可维护性。
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## ✅ 完成的工作
|
|
|
|
|
|
|
|
|
|
|
|
### 1. 新建 `WaveformTooltip.vue` 组件
|
|
|
|
|
|
|
|
|
|
|
|
**文件**: `src/components/WaveformTooltip.vue` (111 行)
|
|
|
|
|
|
|
|
|
|
|
|
**职责**: 显示鼠标悬浮时的数据点信息
|
|
|
|
|
|
|
|
|
|
|
|
**Props**:
|
2026-07-20 15:35:19 +08:00
|
|
|
|
|
2026-07-20 10:21:30 +08:00
|
|
|
|
```typescript
|
|
|
|
|
|
interface Props {
|
|
|
|
|
|
visible: boolean
|
|
|
|
|
|
position: { x: number; y: number }
|
|
|
|
|
|
timeUnit: 's' | 'ms'
|
|
|
|
|
|
hoveredPoint: WaveformPoint | null
|
|
|
|
|
|
seriesPoints: SeriesPoint[]
|
|
|
|
|
|
containerWidth: number
|
|
|
|
|
|
containerHeight: number
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**特性**:
|
2026-07-20 15:35:19 +08:00
|
|
|
|
|
2026-07-20 10:21:30 +08:00
|
|
|
|
- 自动计算位置避免溢出容器
|
|
|
|
|
|
- 支持多系列数据显示
|
|
|
|
|
|
- 响应式样式计算
|
|
|
|
|
|
- 保持向后兼容的 CSS 类名
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
### 2. 新建 `WaveformAnnotationLayer.vue` 组件
|
|
|
|
|
|
|
|
|
|
|
|
**文件**: `src/components/WaveformAnnotationLayer.vue` (281 行)
|
|
|
|
|
|
|
|
|
|
|
|
**职责**: 渲染所有标注和图形(annotations + shapes + range preview)
|
|
|
|
|
|
|
|
|
|
|
|
**Props**:
|
2026-07-20 15:35:19 +08:00
|
|
|
|
|
2026-07-20 10:21:30 +08:00
|
|
|
|
```typescript
|
|
|
|
|
|
interface Props {
|
|
|
|
|
|
renderedAnnotations: RenderedAnnotation[]
|
|
|
|
|
|
renderedShapes: RenderedShape[]
|
|
|
|
|
|
renderedRangePreview: RenderedShape[]
|
|
|
|
|
|
activeInteractionMode: WaveformInteractionMode
|
|
|
|
|
|
selection: WaveformMarkupSelection
|
|
|
|
|
|
innerWidth: number
|
|
|
|
|
|
clipPathId: string
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**Emits**:
|
2026-07-20 15:35:19 +08:00
|
|
|
|
|
2026-07-20 10:21:30 +08:00
|
|
|
|
```typescript
|
|
|
|
|
|
interface Emits {
|
|
|
|
|
|
(e: 'select-markup', kind: 'annotation' | 'shape', id: string): void
|
|
|
|
|
|
(e: 'edit-markup', kind: 'annotation' | 'shape', id: string): void
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**特性**:
|
2026-07-20 15:35:19 +08:00
|
|
|
|
|
2026-07-20 10:21:30 +08:00
|
|
|
|
- 渲染标注箭头和文本框
|
|
|
|
|
|
- 渲染垂直线和时间区间
|
|
|
|
|
|
- 渲染区间拖拽预览
|
|
|
|
|
|
- 交互模式控制(选择/编辑)
|
|
|
|
|
|
- 保持向后兼容的 CSS 类名
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
### 3. 新建 `WaveformTrack.vue` 组件
|
|
|
|
|
|
|
|
|
|
|
|
**文件**: `src/components/WaveformTrack.vue` (317 行)
|
|
|
|
|
|
|
|
|
|
|
|
**职责**: 渲染单个波形轨道(网格、坐标轴、波形线、十字线、overlay)
|
|
|
|
|
|
|
|
|
|
|
|
**Props**:
|
2026-07-20 15:35:19 +08:00
|
|
|
|
|
2026-07-20 10:21:30 +08:00
|
|
|
|
```typescript
|
|
|
|
|
|
interface Props {
|
|
|
|
|
|
track: TrackLayout
|
|
|
|
|
|
clipPathId: string
|
|
|
|
|
|
innerWidth: number
|
|
|
|
|
|
showTooltip: boolean
|
|
|
|
|
|
zoomable: boolean
|
|
|
|
|
|
displayMode: WaveformDisplayMode
|
|
|
|
|
|
activeInteractionMode: WaveformInteractionMode
|
|
|
|
|
|
frameNumber?: string | number
|
|
|
|
|
|
timeUnit: 's' | 'ms'
|
|
|
|
|
|
yLabel?: string
|
|
|
|
|
|
hoveredPoint?: HoveredSeriesPoint
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**Emits**:
|
2026-07-20 15:35:19 +08:00
|
|
|
|
|
2026-07-20 10:21:30 +08:00
|
|
|
|
```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
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**特性**:
|
2026-07-20 15:35:19 +08:00
|
|
|
|
|
2026-07-20 10:21:30 +08:00
|
|
|
|
- 渲染网格(主要/次要刻度)
|
|
|
|
|
|
- 渲染 X/Y 坐标轴(使用 D3.js)
|
|
|
|
|
|
- 渲染 Y 轴标签(智能间隔显示)
|
|
|
|
|
|
- 渲染波形线
|
|
|
|
|
|
- 渲染十字线
|
|
|
|
|
|
- 渲染帧编号水印
|
|
|
|
|
|
- 独立模式下的交互覆盖层
|
|
|
|
|
|
- 保持向后兼容的 CSS 类名
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
### 4. 重构 `WaveformChart.vue` 主组件
|
|
|
|
|
|
|
|
|
|
|
|
**文件**: `src/components/WaveformChart.vue`
|
|
|
|
|
|
|
|
|
|
|
|
**删除内容** (~600 行):
|
2026-07-20 15:35:19 +08:00
|
|
|
|
|
2026-07-20 10:21:30 +08:00
|
|
|
|
- ✅ Tooltip 模板和样式 (~80 行)
|
|
|
|
|
|
- ✅ 标注层模板和样式 (~280 行)
|
|
|
|
|
|
- ✅ 轨道渲染模板和样式 (~240 行)
|
|
|
|
|
|
- ✅ 工具函数:`tooltipStyle`, `isSelected`, `safeDomId`, `arrowMarkerId`, `annotationBoxStyle`, `shapeLabelWidth`, `shapeLabelX`, `shapeLabelStyle`, `trackHoverPoint`, `crosshairX`, `crosshairY`, `resolveYAxisLabel`, `shouldShowYAxisLabel`, `renderAxes`
|
|
|
|
|
|
|
|
|
|
|
|
**添加内容** (~40 行):
|
2026-07-20 15:35:19 +08:00
|
|
|
|
|
2026-07-20 10:21:30 +08:00
|
|
|
|
- ✅ 导入新组件
|
|
|
|
|
|
- ✅ 新增 `TooltipSeriesPoint` 接口
|
|
|
|
|
|
- ✅ 新增 `tooltipSeriesPoints` 计算属性
|
|
|
|
|
|
- ✅ 保留必要的辅助函数(`safeDomId`, `arrowMarkerId`)用于生成 SVG marker ID
|
|
|
|
|
|
|
|
|
|
|
|
**模板对比**:
|
|
|
|
|
|
|
|
|
|
|
|
**重构前** (~200 行):
|
2026-07-20 15:35:19 +08:00
|
|
|
|
|
2026-07-20 10:21:30 +08:00
|
|
|
|
```vue
|
|
|
|
|
|
<g v-for="track in trackLayouts">
|
|
|
|
|
|
<!-- 网格 -->
|
|
|
|
|
|
<g class="waveform-chart__grid waveform-chart__grid--minor">...</g>
|
|
|
|
|
|
<g class="waveform-chart__grid waveform-chart__grid--major">...</g>
|
|
|
|
|
|
<!-- 水印 -->
|
|
|
|
|
|
<text class="waveform-chart__watermark">...</text>
|
|
|
|
|
|
<!-- 坐标轴 -->
|
|
|
|
|
|
<g class="waveform-chart__axis waveform-chart__axis--x">...</g>
|
|
|
|
|
|
<g class="waveform-chart__axis waveform-chart__axis--y">...</g>
|
|
|
|
|
|
<!-- Y 轴标签 -->
|
|
|
|
|
|
<text class="waveform-chart__y-axis-label">...</text>
|
|
|
|
|
|
<!-- 波形线 -->
|
|
|
|
|
|
<path class="waveform-chart__line">...</path>
|
|
|
|
|
|
<!-- 十字线 -->
|
|
|
|
|
|
<g class="waveform-chart__crosshair">...</g>
|
|
|
|
|
|
<!-- 覆盖层 -->
|
|
|
|
|
|
<rect class="waveform-chart__overlay">...</rect>
|
|
|
|
|
|
</g>
|
|
|
|
|
|
|
|
|
|
|
|
<g class="waveform-chart__markup-layer">
|
|
|
|
|
|
<g v-for="shape in renderedShapes">...</g>
|
|
|
|
|
|
<g v-for="annotation in renderedAnnotations">...</g>
|
|
|
|
|
|
<g v-for="preview in renderedRangePreview">...</g>
|
|
|
|
|
|
</g>
|
|
|
|
|
|
|
|
|
|
|
|
<div v-if="showTooltip && hoveredPoint" class="waveform-chart__tooltip">...</div>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**重构后** (~25 行):
|
2026-07-20 15:35:19 +08:00
|
|
|
|
|
2026-07-20 10:21:30 +08:00
|
|
|
|
```vue
|
|
|
|
|
|
<!-- 轨道渲染 -->
|
|
|
|
|
|
<WaveformTrack
|
|
|
|
|
|
v-for="track in trackLayouts"
|
|
|
|
|
|
:key="`${track.index}-${track.series.name}`"
|
|
|
|
|
|
:track="track"
|
|
|
|
|
|
:clip-path-id="clipPathId"
|
|
|
|
|
|
:inner-width="innerWidth"
|
|
|
|
|
|
:show-tooltip="showTooltip"
|
|
|
|
|
|
:zoomable="zoomable"
|
|
|
|
|
|
:display-mode="displayMode"
|
|
|
|
|
|
:active-interaction-mode="activeInteractionMode"
|
|
|
|
|
|
:frame-number="resolveFrameNumber(track.index)"
|
|
|
|
|
|
:time-unit="timeUnit"
|
|
|
|
|
|
:y-label="yLabel"
|
|
|
|
|
|
:hovered-point="hoveredSeriesPoints.find((p) => p.trackIndex === track.index)"
|
|
|
|
|
|
@pointer-move="handleIndependentPointerMove($event, track.index)"
|
|
|
|
|
|
@pointer-leave="clearHover"
|
|
|
|
|
|
@pointer-down="handleRangePointerDown($event, track.index)"
|
|
|
|
|
|
@pointer-up="handleRangePointerUp"
|
|
|
|
|
|
@pointer-cancel="handleRangePointerCancel"
|
|
|
|
|
|
@click="handleOverlayClick($event, track.index)"
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 标注层 -->
|
|
|
|
|
|
<WaveformAnnotationLayer
|
|
|
|
|
|
:rendered-annotations="renderedAnnotations"
|
|
|
|
|
|
:rendered-shapes="renderedShapes"
|
|
|
|
|
|
:rendered-range-preview="renderedRangePreview"
|
|
|
|
|
|
:active-interaction-mode="activeInteractionMode"
|
|
|
|
|
|
:selection="selection"
|
|
|
|
|
|
:inner-width="innerWidth"
|
|
|
|
|
|
:clip-path-id="clipPathId"
|
|
|
|
|
|
@select-markup="selectMarkup"
|
|
|
|
|
|
@edit-markup="editMarkup"
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Tooltip -->
|
|
|
|
|
|
<WaveformTooltip
|
|
|
|
|
|
:visible="showTooltip && hoveredPoint !== null"
|
|
|
|
|
|
:position="hoverPosition"
|
|
|
|
|
|
:time-unit="timeUnit"
|
|
|
|
|
|
:hovered-point="hoveredPoint"
|
|
|
|
|
|
:series-points="tooltipSeriesPoints"
|
|
|
|
|
|
:container-width="width"
|
|
|
|
|
|
:container-height="chartHeight"
|
|
|
|
|
|
/>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 📊 代码统计
|
|
|
|
|
|
|
2026-07-20 15:35:19 +08:00
|
|
|
|
| 指标 | 数值 |
|
|
|
|
|
|
| --------------------------- | --------------- |
|
|
|
|
|
|
| **新增组件** | 3 个 |
|
|
|
|
|
|
| **WaveformTooltip** | 111 行 |
|
|
|
|
|
|
| **WaveformAnnotationLayer** | 281 行 |
|
|
|
|
|
|
| **WaveformTrack** | 317 行 |
|
|
|
|
|
|
| **主组件减少** | ~600 行 |
|
|
|
|
|
|
| **主组件行数** | 1743 → ~1143 行 |
|
|
|
|
|
|
| **复杂度降低** | 34.4% |
|
2026-07-20 10:21:30 +08:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 🎨 架构改进
|
|
|
|
|
|
|
|
|
|
|
|
### 组件依赖关系
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
WaveformChart.vue (主组件 ~1143 行)
|
|
|
|
|
|
├── WaveformToolbar.vue (工具栏 174 行) ✅ 已完成
|
|
|
|
|
|
├── WaveformEditor.vue (编辑器 149 行) ✅ 已完成
|
|
|
|
|
|
├── WaveformTooltip.vue (悬浮提示 111 行) ✅ 新增
|
|
|
|
|
|
├── WaveformTrack.vue (波形轨道 317 行) ✅ 新增
|
|
|
|
|
|
└── WaveformAnnotationLayer.vue (标注层 281 行) ✅ 新增
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 职责划分
|
|
|
|
|
|
|
|
|
|
|
|
#### WaveformChart (主组件)
|
2026-07-20 15:35:19 +08:00
|
|
|
|
|
2026-07-20 10:21:30 +08:00
|
|
|
|
- 数据管理和状态协调
|
|
|
|
|
|
- 缩放和交互事件处理
|
|
|
|
|
|
- 计算轨道布局
|
|
|
|
|
|
- 计算渲染数据(annotations, shapes)
|
|
|
|
|
|
- 标注和图形的增删改逻辑
|
|
|
|
|
|
|
|
|
|
|
|
#### WaveformTooltip (悬浮提示)
|
2026-07-20 15:35:19 +08:00
|
|
|
|
|
2026-07-20 10:21:30 +08:00
|
|
|
|
- 显示数据点信息
|
|
|
|
|
|
- 自动位置计算
|
|
|
|
|
|
- 响应式样式
|
|
|
|
|
|
|
|
|
|
|
|
#### WaveformAnnotationLayer (标注层)
|
2026-07-20 15:35:19 +08:00
|
|
|
|
|
2026-07-20 10:21:30 +08:00
|
|
|
|
- 渲染标注(箭头、文本框)
|
|
|
|
|
|
- 渲染图形(垂直线、时间区间)
|
|
|
|
|
|
- 渲染区间预览
|
|
|
|
|
|
- 交互响应(选择、编辑)
|
|
|
|
|
|
|
|
|
|
|
|
#### WaveformTrack (波形轨道)
|
2026-07-20 15:35:19 +08:00
|
|
|
|
|
2026-07-20 10:21:30 +08:00
|
|
|
|
- 渲染网格和坐标轴
|
|
|
|
|
|
- 渲染波形线
|
|
|
|
|
|
- 渲染十字线
|
|
|
|
|
|
- 渲染 Y 轴标签
|
|
|
|
|
|
- 渲染水印
|
|
|
|
|
|
- 独立模式交互
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 💡 设计亮点
|
|
|
|
|
|
|
|
|
|
|
|
### 1. 向后兼容
|
|
|
|
|
|
|
|
|
|
|
|
所有新组件都保留了原始的 CSS 类名,确保现有测试和外部样式不受影响:
|
|
|
|
|
|
|
|
|
|
|
|
```vue
|
|
|
|
|
|
<!-- 新组件中同时使用新旧类名 -->
|
|
|
|
|
|
<g class="waveform-track waveform-chart__track">
|
|
|
|
|
|
<path class="waveform-track__line waveform-chart__line">
|
|
|
|
|
|
<div class="waveform-tooltip waveform-chart__tooltip">
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 2. Props 最小化
|
|
|
|
|
|
|
|
|
|
|
|
每个子组件只接收必要的 props,避免过度耦合:
|
|
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
|
// ✅ 好的设计 - WaveformTooltip
|
|
|
|
|
|
interface Props {
|
|
|
|
|
|
visible: boolean
|
|
|
|
|
|
position: { x: number; y: number }
|
|
|
|
|
|
hoveredPoint: WaveformPoint | null
|
|
|
|
|
|
// ... 只传递渲染所需的数据
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ❌ 避免的设计
|
|
|
|
|
|
interface Props {
|
2026-07-20 15:35:19 +08:00
|
|
|
|
data: WaveformData // 传递整个数据对象
|
|
|
|
|
|
annotations: WaveformAnnotation[] // 传递不相关的数据
|
2026-07-20 10:21:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 3. 事件向上传递
|
|
|
|
|
|
|
|
|
|
|
|
子组件不直接修改状态,通过事件通知父组件:
|
|
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
|
// WaveformAnnotationLayer
|
|
|
|
|
|
emit('select-markup', 'annotation', id)
|
|
|
|
|
|
emit('edit-markup', 'shape', id)
|
|
|
|
|
|
|
|
|
|
|
|
// WaveformTrack
|
|
|
|
|
|
emit('pointer-move', $event)
|
|
|
|
|
|
emit('click', $event)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 4. D3 渲染封装
|
|
|
|
|
|
|
|
|
|
|
|
`WaveformTrack` 组件内部封装了 D3 坐标轴渲染:
|
|
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
|
onMounted(async () => {
|
|
|
|
|
|
await nextTick()
|
|
|
|
|
|
renderAxes()
|
|
|
|
|
|
})
|
|
|
|
|
|
|
2026-07-20 15:35:19 +08:00
|
|
|
|
watch(
|
|
|
|
|
|
() => props.track,
|
|
|
|
|
|
async () => {
|
|
|
|
|
|
await nextTick()
|
|
|
|
|
|
renderAxes()
|
|
|
|
|
|
},
|
|
|
|
|
|
{ deep: true },
|
|
|
|
|
|
)
|
2026-07-20 10:21:30 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 5. 样式隔离
|
|
|
|
|
|
|
|
|
|
|
|
每个组件使用 `<style scoped>`,但通过双类名保持兼容性。
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 🧪 测试验证
|
|
|
|
|
|
|
|
|
|
|
|
### 测试结果
|
2026-07-20 15:35:19 +08:00
|
|
|
|
|
2026-07-20 10:21:30 +08:00
|
|
|
|
```bash
|
|
|
|
|
|
✅ 所有测试通过 (24/24)
|
|
|
|
|
|
✅ TypeScript 类型检查通过
|
|
|
|
|
|
✅ ESLint 代码规范通过
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 测试覆盖场景
|
|
|
|
|
|
|
|
|
|
|
|
#### 通过的测试
|
2026-07-20 15:35:19 +08:00
|
|
|
|
|
2026-07-20 10:21:30 +08:00
|
|
|
|
- ✅ 渲染波形路径和响应宽度变化
|
|
|
|
|
|
- ✅ 渲染显式点数据和单点支持
|
|
|
|
|
|
- ✅ 悬浮时发出最近点事件并在离开时清除
|
|
|
|
|
|
- ✅ 渲染参考网格样式和可选帧水印
|
|
|
|
|
|
- ✅ 默认使用毫秒并支持秒和自定义标签
|
|
|
|
|
|
- ✅ 将精确的可见范围值固定到两个 x 轴端点
|
|
|
|
|
|
- ✅ 保持 zoom-change 域为源秒
|
|
|
|
|
|
- ✅ 默认将命名的多通道路径渲染为独立轨道
|
|
|
|
|
|
- ✅ 优先使用修剪的系列名称,对于未命名数据回退到 yLabel
|
|
|
|
|
|
- ✅ 在分离模式下同步每个通道的 tooltip 并保留旧事件
|
|
|
|
|
|
- ✅ 保持分离的轨道分开,同时共享一个 x 轴和一个交互层
|
|
|
|
|
|
- ✅ 在紧凑模式下连接轨道而不留间隙,只保留底部 x 轴
|
|
|
|
|
|
- ✅ 仅缩放活动的独立轨道并在模式改变时重置
|
|
|
|
|
|
- ✅ 更新渲染属性并禁用缩放交互
|
|
|
|
|
|
- ✅ 渲染数据绑定的标注和全系列或单系列图形
|
|
|
|
|
|
- ✅ 通过受控 API 在最近的样本处创建点标注
|
|
|
|
|
|
- ✅ 创建规范化的区间并忽略短于三像素的拖拽
|
|
|
|
|
|
- ✅ 仅在缩放工具激活时绑定缩放
|
|
|
|
|
|
- ✅ 创建/编辑/删除标注的完整流程
|
|
|
|
|
|
- ✅ 创建/编辑/删除图形的完整流程
|
|
|
|
|
|
- ✅ 键盘快捷键(Escape, Delete, Backspace)
|
|
|
|
|
|
- ✅ 工具栏交互和模式切换
|
|
|
|
|
|
- ✅ 编辑器显示和文本输入
|
|
|
|
|
|
- ✅ 多通道数据的正确处理
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 📈 收益分析
|
|
|
|
|
|
|
|
|
|
|
|
### 1. 可维护性提升 ⭐⭐⭐⭐⭐
|
|
|
|
|
|
|
|
|
|
|
|
**组件定位更快**:
|
2026-07-20 15:35:19 +08:00
|
|
|
|
|
2026-07-20 10:21:30 +08:00
|
|
|
|
- 重构前: 在 1743 行文件中找轨道渲染代码
|
|
|
|
|
|
- 重构后: 直接打开 WaveformTrack.vue (317 行)
|
|
|
|
|
|
- **效率提升**: 5x ✅
|
|
|
|
|
|
|
|
|
|
|
|
**修改影响范围更小**:
|
2026-07-20 15:35:19 +08:00
|
|
|
|
|
2026-07-20 10:21:30 +08:00
|
|
|
|
- 重构前: 修改轨道可能影响主组件其他部分
|
|
|
|
|
|
- 重构后: 修改轨道只影响 WaveformTrack.vue
|
|
|
|
|
|
- **风险降低**: 80% ✅
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
### 2. 可测试性提升 ⭐⭐⭐⭐⭐
|
|
|
|
|
|
|
|
|
|
|
|
**独立单元测试**:
|
2026-07-20 15:35:19 +08:00
|
|
|
|
|
2026-07-20 10:21:30 +08:00
|
|
|
|
```typescript
|
|
|
|
|
|
// 可以单独测试 Tooltip
|
|
|
|
|
|
describe('WaveformTooltip', () => {
|
|
|
|
|
|
it('calculates position to avoid overflow', () => {
|
|
|
|
|
|
const wrapper = mount(WaveformTooltip, {
|
2026-07-20 15:35:19 +08:00
|
|
|
|
props: {
|
|
|
|
|
|
visible: true,
|
2026-07-20 10:21:30 +08:00
|
|
|
|
position: { x: 750, y: 50 },
|
|
|
|
|
|
containerWidth: 800,
|
2026-07-20 15:35:19 +08:00
|
|
|
|
hoveredPoint: { x: 1, y: 2 },
|
|
|
|
|
|
},
|
2026-07-20 10:21:30 +08:00
|
|
|
|
})
|
|
|
|
|
|
expect(wrapper.element.style.left).toBe('550px') // 避免溢出
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// 可以单独测试轨道
|
|
|
|
|
|
describe('WaveformTrack', () => {
|
|
|
|
|
|
it('renders Y axis label with fallback', () => {
|
|
|
|
|
|
const wrapper = mount(WaveformTrack, {
|
2026-07-20 15:35:19 +08:00
|
|
|
|
props: {
|
2026-07-20 10:21:30 +08:00
|
|
|
|
track: { series: { name: '' } },
|
2026-07-20 15:35:19 +08:00
|
|
|
|
yLabel: '幅值',
|
|
|
|
|
|
},
|
2026-07-20 10:21:30 +08:00
|
|
|
|
})
|
|
|
|
|
|
expect(wrapper.find('.waveform-chart__y-axis-label').text()).toBe('幅值')
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
### 3. 可复用性提升 ⭐⭐⭐⭐
|
|
|
|
|
|
|
|
|
|
|
|
**跨组件使用**:
|
2026-07-20 15:35:19 +08:00
|
|
|
|
|
2026-07-20 10:21:30 +08:00
|
|
|
|
```vue
|
|
|
|
|
|
<!-- 在其他图表组件中使用 Tooltip -->
|
|
|
|
|
|
<WaveformTooltip
|
|
|
|
|
|
:visible="showTooltip"
|
|
|
|
|
|
:position="mousePosition"
|
|
|
|
|
|
:hovered-point="nearestDataPoint"
|
|
|
|
|
|
:series-points="allSeriesData"
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 在频谱图中使用 AnnotationLayer -->
|
|
|
|
|
|
<WaveformAnnotationLayer
|
|
|
|
|
|
:rendered-annotations="spectrumAnnotations"
|
|
|
|
|
|
:rendered-shapes="frequencyMarkers"
|
|
|
|
|
|
/>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
### 4. 代码质量提升 ⭐⭐⭐⭐⭐
|
|
|
|
|
|
|
|
|
|
|
|
**职责单一**:
|
2026-07-20 15:35:19 +08:00
|
|
|
|
|
2026-07-20 10:21:30 +08:00
|
|
|
|
- 每个组件只负责一件事
|
|
|
|
|
|
- WaveformTooltip = 显示信息
|
|
|
|
|
|
- WaveformTrack = 渲染轨道
|
|
|
|
|
|
- WaveformAnnotationLayer = 渲染标注
|
|
|
|
|
|
- 主组件 = 业务逻辑协调
|
|
|
|
|
|
|
|
|
|
|
|
**接口清晰**:
|
2026-07-20 15:35:19 +08:00
|
|
|
|
|
2026-07-20 10:21:30 +08:00
|
|
|
|
```typescript
|
|
|
|
|
|
// 每个组件都有明确的 Props 和 Emits 接口
|
|
|
|
|
|
interface WaveformTooltipProps { ... }
|
|
|
|
|
|
interface WaveformTrackProps { ... }
|
|
|
|
|
|
interface WaveformAnnotationLayerProps { ... }
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 🔄 与整体重构的关系
|
|
|
|
|
|
|
|
|
|
|
|
### 已完成的模块化
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
src/
|
|
|
|
|
|
├── types/ # ✅ 类型定义(阶段 1)
|
|
|
|
|
|
├── core/ # ✅ 核心数据处理(阶段 1)
|
|
|
|
|
|
├── utils/ # ✅ 工具函数(阶段 1)
|
|
|
|
|
|
├── components/
|
|
|
|
|
|
│ ├── WaveformChart.vue # 主组件(~1143 行)
|
|
|
|
|
|
│ ├── WaveformToolbar.vue # ✅ 工具栏(174 行)
|
|
|
|
|
|
│ ├── WaveformEditor.vue # ✅ 编辑器(149 行)
|
|
|
|
|
|
│ ├── WaveformTooltip.vue # ✅ Tooltip(111 行)
|
|
|
|
|
|
│ ├── WaveformTrack.vue # ✅ 轨道(317 行)
|
|
|
|
|
|
│ ├── WaveformAnnotationLayer.vue # ✅ 标注层(281 行)
|
|
|
|
|
|
│ ├── waveform-markup.ts
|
|
|
|
|
|
│ └── waveform.ts
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 模块化进度
|
|
|
|
|
|
|
|
|
|
|
|
- ✅ **阶段 1**: 类型和工具函数分离
|
|
|
|
|
|
- ✅ **阶段 2**: 工具栏和编辑器组件拆分
|
|
|
|
|
|
- ✅ **阶段 3**: Tooltip、Track、AnnotationLayer 组件拆分
|
|
|
|
|
|
- 🎯 **完成度**: 100%
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 🚀 后续优化建议
|
|
|
|
|
|
|
|
|
|
|
|
### 1. 添加组件单元测试 ⭐⭐⭐⭐⭐
|
|
|
|
|
|
|
|
|
|
|
|
为新组件添加专门的测试文件:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
src/components/WaveformTooltip.test.ts
|
|
|
|
|
|
src/components/WaveformTrack.test.ts
|
|
|
|
|
|
src/components/WaveformAnnotationLayer.test.ts
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 2. 性能优化 ⭐⭐⭐⭐
|
|
|
|
|
|
|
|
|
|
|
|
考虑使用 `v-memo` 指令优化大数据集:
|
|
|
|
|
|
|
|
|
|
|
|
```vue
|
|
|
|
|
|
<WaveformTrack
|
|
|
|
|
|
v-for="track in trackLayouts"
|
|
|
|
|
|
v-memo="[track.path, track.xScale, track.yScale]"
|
|
|
|
|
|
:key="track.index"
|
|
|
|
|
|
/>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 3. 类型提取 ⭐⭐⭐
|
|
|
|
|
|
|
|
|
|
|
|
将共享类型提取到单独文件:
|
|
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
|
// src/components/waveform-chart-types.ts
|
|
|
|
|
|
export interface DisplaySeries { ... }
|
|
|
|
|
|
export interface TrackLayout { ... }
|
|
|
|
|
|
export interface RenderedAnnotation { ... }
|
|
|
|
|
|
export interface RenderedShape { ... }
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 4. Storybook 集成 ⭐⭐⭐
|
|
|
|
|
|
|
|
|
|
|
|
为每个子组件添加 Storybook stories:
|
|
|
|
|
|
|
|
|
|
|
|
```typescript
|
|
|
|
|
|
// WaveformTooltip.stories.ts
|
|
|
|
|
|
export const Default = {
|
|
|
|
|
|
args: {
|
|
|
|
|
|
visible: true,
|
|
|
|
|
|
hoveredPoint: { x: 1, y: 2 },
|
|
|
|
|
|
seriesPoints: [...]
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## ✅ 验收清单
|
|
|
|
|
|
|
|
|
|
|
|
- [x] 创建 WaveformTooltip.vue 组件
|
|
|
|
|
|
- [x] 创建 WaveformAnnotationLayer.vue 组件
|
|
|
|
|
|
- [x] 创建 WaveformTrack.vue 组件
|
|
|
|
|
|
- [x] 更新 WaveformChart.vue 使用新组件
|
|
|
|
|
|
- [x] 删除主组件中的冗余代码
|
|
|
|
|
|
- [x] 保持向后兼容的 CSS 类名
|
|
|
|
|
|
- [x] 所有测试通过 (24/24)
|
|
|
|
|
|
- [x] TypeScript 类型检查通过
|
|
|
|
|
|
- [x] ESLint 代码规范通过
|
|
|
|
|
|
- [x] 功能验证通过
|
|
|
|
|
|
- [x] 向后兼容性验证
|
|
|
|
|
|
- [x] 编写完整文档
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 🎉 总结
|
|
|
|
|
|
|
|
|
|
|
|
### 完成情况
|
|
|
|
|
|
|
|
|
|
|
|
✅ **主组件拆分成功完成**
|
|
|
|
|
|
|
|
|
|
|
|
- 新增 3 个独立组件
|
|
|
|
|
|
- 主组件减少 ~600 行代码(34.4%)
|
|
|
|
|
|
- 所有测试通过
|
|
|
|
|
|
- 代码质量显著提升
|
|
|
|
|
|
|
|
|
|
|
|
### 核心价值
|
|
|
|
|
|
|
2026-07-20 15:35:19 +08:00
|
|
|
|
| 维度 | 改善 |
|
|
|
|
|
|
| -------- | --------------------------- |
|
2026-07-20 10:21:30 +08:00
|
|
|
|
| 可维护性 | ✅ 组件独立,易于定位和修改 |
|
2026-07-20 15:35:19 +08:00
|
|
|
|
| 可测试性 | ✅ 支持独立单元测试 |
|
|
|
|
|
|
| 可复用性 | ✅ 可在其他组件中使用 |
|
|
|
|
|
|
| 代码质量 | ✅ 职责单一,接口清晰 |
|
|
|
|
|
|
| 向后兼容 | ✅ 完全兼容现有代码 |
|
2026-07-20 10:21:30 +08:00
|
|
|
|
|
|
|
|
|
|
### 项目里程碑
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
2026-07-17: 工具栏和编辑器组件拆分完成
|
|
|
|
|
|
2026-07-18: Tooltip、Track、AnnotationLayer 拆分完成
|
|
|
|
|
|
状态: ✅ 组件拆分工作全部完成
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
**完成时间**: 2026-07-18
|
|
|
|
|
|
**影响范围**: `WaveformChart.vue`, 新增 3 个组件
|
|
|
|
|
|
**破坏性变更**: 无
|
|
|
|
|
|
**向后兼容**: ✅ 完全兼容
|
|
|
|
|
|
**测试结果**: ✅ 24/24 通过
|