feat(chart): add multi-axis overlay controls

This commit is contained in:
李启源
2026-07-20 15:35:19 +08:00
parent 0cab86f7b6
commit c9e4dae25f
44 changed files with 1882 additions and 523 deletions

View File

@@ -15,6 +15,7 @@
**职责**: 显示鼠标悬浮时的数据点信息
**Props**:
```typescript
interface Props {
visible: boolean
@@ -28,6 +29,7 @@ interface Props {
```
**特性**:
- 自动计算位置避免溢出容器
- 支持多系列数据显示
- 响应式样式计算
@@ -42,6 +44,7 @@ interface Props {
**职责**: 渲染所有标注和图形annotations + shapes + range preview
**Props**:
```typescript
interface Props {
renderedAnnotations: RenderedAnnotation[]
@@ -55,6 +58,7 @@ interface Props {
```
**Emits**:
```typescript
interface Emits {
(e: 'select-markup', kind: 'annotation' | 'shape', id: string): void
@@ -63,6 +67,7 @@ interface Emits {
```
**特性**:
- 渲染标注箭头和文本框
- 渲染垂直线和时间区间
- 渲染区间拖拽预览
@@ -78,6 +83,7 @@ interface Emits {
**职责**: 渲染单个波形轨道网格、坐标轴、波形线、十字线、overlay
**Props**:
```typescript
interface Props {
track: TrackLayout
@@ -95,6 +101,7 @@ interface Props {
```
**Emits**:
```typescript
interface Emits {
(e: 'pointer-move', event: PointerEvent): void
@@ -107,6 +114,7 @@ interface Emits {
```
**特性**:
- 渲染网格(主要/次要刻度)
- 渲染 X/Y 坐标轴(使用 D3.js
- 渲染 Y 轴标签(智能间隔显示)
@@ -123,12 +131,14 @@ interface Emits {
**文件**: `src/components/WaveformChart.vue`
**删除内容** (~600 行):
- ✅ Tooltip 模板和样式 (~80 行)
- ✅ 标注层模板和样式 (~280 行)
- ✅ 轨道渲染模板和样式 (~240 行)
- ✅ 工具函数:`tooltipStyle`, `isSelected`, `safeDomId`, `arrowMarkerId`, `annotationBoxStyle`, `shapeLabelWidth`, `shapeLabelX`, `shapeLabelStyle`, `trackHoverPoint`, `crosshairX`, `crosshairY`, `resolveYAxisLabel`, `shouldShowYAxisLabel`, `renderAxes`
**添加内容** (~40 行):
- ✅ 导入新组件
- ✅ 新增 `TooltipSeriesPoint` 接口
- ✅ 新增 `tooltipSeriesPoints` 计算属性
@@ -137,6 +147,7 @@ interface Emits {
**模板对比**:
**重构前** (~200 行):
```vue
<g v-for="track in trackLayouts">
<!-- 网格 -->
@@ -167,6 +178,7 @@ interface Emits {
```
**重构后** (~25 行):
```vue
<!-- 轨道渲染 -->
<WaveformTrack
@@ -220,15 +232,15 @@ interface Emits {
## 📊 代码统计
| 指标 | 数值 |
|------|------|
| **新增组件** | 3 个 |
| **WaveformTooltip** | 111 行 |
| **WaveformAnnotationLayer** | 281 行 |
| **WaveformTrack** | 317 行 |
| **主组件减少** | ~600 行 |
| **主组件行数** | 1743 → ~1143 行 |
| **复杂度降低** | 34.4% |
| 指标 | 数值 |
| --------------------------- | --------------- |
| **新增组件** | 3 个 |
| **WaveformTooltip** | 111 行 |
| **WaveformAnnotationLayer** | 281 行 |
| **WaveformTrack** | 317 行 |
| **主组件减少** | ~600 行 |
| **主组件行数** | 1743 → ~1143 行 |
| **复杂度降低** | 34.4% |
---
@@ -248,6 +260,7 @@ WaveformChart.vue (主组件 ~1143 行)
### 职责划分
#### WaveformChart (主组件)
- 数据管理和状态协调
- 缩放和交互事件处理
- 计算轨道布局
@@ -255,17 +268,20 @@ WaveformChart.vue (主组件 ~1143 行)
- 标注和图形的增删改逻辑
#### WaveformTooltip (悬浮提示)
- 显示数据点信息
- 自动位置计算
- 响应式样式
#### WaveformAnnotationLayer (标注层)
- 渲染标注(箭头、文本框)
- 渲染图形(垂直线、时间区间)
- 渲染区间预览
- 交互响应(选择、编辑)
#### WaveformTrack (波形轨道)
- 渲染网格和坐标轴
- 渲染波形线
- 渲染十字线
@@ -303,8 +319,8 @@ interface Props {
// ❌ 避免的设计
interface Props {
data: WaveformData // 传递整个数据对象
annotations: WaveformAnnotation[] // 传递不相关的数据
data: WaveformData // 传递整个数据对象
annotations: WaveformAnnotation[] // 传递不相关的数据
}
```
@@ -332,10 +348,14 @@ onMounted(async () => {
renderAxes()
})
watch(() => props.track, async () => {
await nextTick()
renderAxes()
}, { deep: true })
watch(
() => props.track,
async () => {
await nextTick()
renderAxes()
},
{ deep: true },
)
```
### 5. 样式隔离
@@ -347,6 +367,7 @@ watch(() => props.track, async () => {
## 🧪 测试验证
### 测试结果
```bash
✅ 所有测试通过 (24/24)
✅ TypeScript 类型检查通过
@@ -356,6 +377,7 @@ watch(() => props.track, async () => {
### 测试覆盖场景
#### 通过的测试
- ✅ 渲染波形路径和响应宽度变化
- ✅ 渲染显式点数据和单点支持
- ✅ 悬浮时发出最近点事件并在离开时清除
@@ -388,11 +410,13 @@ watch(() => props.track, async () => {
### 1. 可维护性提升 ⭐⭐⭐⭐⭐
**组件定位更快**:
- 重构前: 在 1743 行文件中找轨道渲染代码
- 重构后: 直接打开 WaveformTrack.vue (317 行)
- **效率提升**: 5x ✅
**修改影响范围更小**:
- 重构前: 修改轨道可能影响主组件其他部分
- 重构后: 修改轨道只影响 WaveformTrack.vue
- **风险降低**: 80% ✅
@@ -402,17 +426,18 @@ watch(() => props.track, async () => {
### 2. 可测试性提升 ⭐⭐⭐⭐⭐
**独立单元测试**:
```typescript
// 可以单独测试 Tooltip
describe('WaveformTooltip', () => {
it('calculates position to avoid overflow', () => {
const wrapper = mount(WaveformTooltip, {
props: {
visible: true,
props: {
visible: true,
position: { x: 750, y: 50 },
containerWidth: 800,
hoveredPoint: { x: 1, y: 2 }
}
hoveredPoint: { x: 1, y: 2 },
},
})
expect(wrapper.element.style.left).toBe('550px') // 避免溢出
})
@@ -422,10 +447,10 @@ describe('WaveformTooltip', () => {
describe('WaveformTrack', () => {
it('renders Y axis label with fallback', () => {
const wrapper = mount(WaveformTrack, {
props: {
props: {
track: { series: { name: '' } },
yLabel: '幅值'
}
yLabel: '幅值',
},
})
expect(wrapper.find('.waveform-chart__y-axis-label').text()).toBe('幅值')
})
@@ -437,6 +462,7 @@ describe('WaveformTrack', () => {
### 3. 可复用性提升 ⭐⭐⭐⭐
**跨组件使用**:
```vue
<!-- 在其他图表组件中使用 Tooltip -->
<WaveformTooltip
@@ -458,6 +484,7 @@ describe('WaveformTrack', () => {
### 4. 代码质量提升 ⭐⭐⭐⭐⭐
**职责单一**:
- 每个组件只负责一件事
- WaveformTooltip = 显示信息
- WaveformTrack = 渲染轨道
@@ -465,6 +492,7 @@ describe('WaveformTrack', () => {
- 主组件 = 业务逻辑协调
**接口清晰**:
```typescript
// 每个组件都有明确的 Props 和 Emits 接口
interface WaveformTooltipProps { ... }
@@ -586,13 +614,13 @@ export const Default = {
### 核心价值
| 维度 | 改善 |
|------|------|
| 维度 | 改善 |
| -------- | --------------------------- |
| 可维护性 | ✅ 组件独立,易于定位和修改 |
| 可测试性 | ✅ 支持独立单元测试 |
| 可复用性 | ✅ 可在其他组件中使用 |
| 代码质量 | ✅ 职责单一,接口清晰 |
| 向后兼容 | ✅ 完全兼容现有代码 |
| 可测试性 | ✅ 支持独立单元测试 |
| 可复用性 | ✅ 可在其他组件中使用 |
| 代码质量 | ✅ 职责单一,接口清晰 |
| 向后兼容 | ✅ 完全兼容现有代码 |
### 项目里程碑