From c9e4dae25f31373973cf4df899f77036ed1092fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=90=AF=E6=BA=90?= Date: Mon, 20 Jul 2026 15:35:19 +0800 Subject: [PATCH] feat(chart): add multi-axis overlay controls --- .claude/plan-system-refactoring.md | 58 ++-- .claude/plan.md | 40 ++- .gitignore | 1 + ARCHITECTURE.md | 5 +- CODE_REVIEW_FIXES.md | 75 +++-- CODE_REVIEW_FIXES_SUMMARY.md | 206 +++++++++++++ COMPONENT_REFACTORING.md | 84 ++++-- DAILY_SUMMARY_2026-07-18.md | 86 +++--- DIRECTORY_STRUCTURE_REFACTORING.md | 96 ++++--- README.md | 20 +- REFACTORING_ANALYSIS.md | 77 ++--- REFACTORING_COMPLETE.md | 77 +++-- REFACTORING_PHASE1_REPORT.md | 48 +++- SYSTEM_REFACTORING.md | 67 +++-- TOOLBAR_EDITOR_REFACTORING.md | 113 +++++--- Y_AXIS_LABEL_FIX.md | 35 ++- src/App.test.ts | 38 +++ src/App.vue | 43 ++- src/components/WaveformChart.test.ts | 286 +++++++++++++++---- src/components/WaveformChart.vue | 137 ++++++--- src/components/annotation/components.test.ts | 14 +- src/components/annotation/markup.test.ts | 77 +++-- src/components/annotation/markup.ts | 8 +- src/components/annotation/types.ts | 9 +- src/components/core/grid.test.ts | 23 +- src/components/core/grid.ts | 11 +- src/components/core/layout.test.ts | 98 +++++++ src/components/core/layout.ts | 219 +++++++++++++- src/components/core/title.ts | 6 +- src/components/core/types.ts | 18 ++ src/components/core/useWaveformData.ts | 5 +- src/components/data/types.ts | 1 + src/components/index.ts | 1 + src/components/rendering/WaveformTrack.vue | 133 +++++++-- src/components/waveform.ts | 1 + src/core/data.ts | 7 +- src/index.ts | 1 + src/types/chart.ts | 12 +- src/types/index.ts | 1 + src/utils/domain.test.ts | 4 +- src/utils/domain.ts | 5 +- src/utils/formatters.test.ts | 67 +++-- src/utils/formatters.ts | 86 ++++-- src/utils/index.ts | 6 + 44 files changed, 1882 insertions(+), 523 deletions(-) create mode 100644 CODE_REVIEW_FIXES_SUMMARY.md create mode 100644 src/components/core/layout.test.ts diff --git a/.claude/plan-system-refactoring.md b/.claude/plan-system-refactoring.md index e8adade..30997f2 100644 --- a/.claude/plan-system-refactoring.md +++ b/.claude/plan-system-refactoring.md @@ -7,6 +7,7 @@ ## 📊 当前结构分析 ### 当前组件列表 + ``` src/components/ ├── WaveformChart.vue # 主容器组件 (~1143 行) @@ -105,7 +106,9 @@ src/components/ **职责**:提供共享的类型、常量和工具 **文件**: + - `core/types.ts` - 基础类型定义 + ```typescript export interface DisplaySeries { ... } export interface TrackLayout { ... } @@ -128,7 +131,9 @@ src/components/ **职责**:数据规范化、轨道布局计算 **文件**: + - `data/types.ts` - 数据类型 + ```typescript export type WaveformData = ... export type WaveformDisplayMode = ... @@ -136,6 +141,7 @@ src/components/ ``` - `data/normalize.ts` - 数据规范化 + ```typescript export function normalizeWaveformData(data: WaveformData): WaveformSeries[] export function normalizeWaveformSeries(data: WaveformData): DisplaySeries[] @@ -153,6 +159,7 @@ src/components/ ``` **来源**: + - `waveform.ts` → `data/types.ts` + `data/normalize.ts` - `WaveformChart.vue` 中的 `trackLayouts` 计算逻辑 → `data/layout.ts` @@ -163,6 +170,7 @@ src/components/ **职责**:渲染波形轨道、网格、坐标轴、波形线 **文件**: + - `rendering/WaveformTrack.vue` - 波形轨道组件(已存在) - `rendering/types.ts` - 渲染相关类型 ```typescript @@ -171,10 +179,12 @@ src/components/ ``` **可选优化**: + - `rendering/Grid.vue` - 独立网格组件 - `rendering/Axis.vue` - 独立坐标轴组件 **来源**: + - `WaveformTrack.vue` → `rendering/WaveformTrack.vue` --- @@ -184,17 +194,19 @@ src/components/ **职责**:管理缩放行为、变换状态 **文件**: + - `zoom/useZoom.ts` - 缩放组合式函数 + ```typescript export function useZoom(options: ZoomOptions) { const sharedTransform = shallowRef(zoomIdentity) const independentTransforms = shallowRef([]) - + function configureZoom() { ... } function resetViewport() { ... } function handleSharedZoom(event: D3ZoomEvent) { ... } function handleIndependentZoom(event: D3ZoomEvent, trackIndex: number) { ... } - + return { sharedTransform, independentTransforms, @@ -221,17 +233,19 @@ src/components/ **职责**:处理用户交互(悬浮、点击、工具栏) **文件**: + - `interaction/WaveformToolbar.vue` - 工具栏(已存在) - `interaction/WaveformTooltip.vue` - 悬浮提示(已存在) - `interaction/useInteraction.ts` - 交互管理 + ```typescript export function useInteraction(options: InteractionOptions) { const interactionMode = ref('zoom') - + function setInteractionMode(mode: WaveformInteractionMode) { ... } function handleOverlayClick(event: PointerEvent, trackIndex?: number) { ... } - + return { interactionMode, setInteractionMode, @@ -241,16 +255,17 @@ src/components/ ``` - `interaction/useHover.ts` - 悬浮逻辑 + ```typescript export function useHover(options: HoverOptions) { const hoveredSeriesPoints = ref([]) const hoveredTrackIndex = ref(null) const hoverPosition = ref({ x: 0, y: 0 }) - + function handlePointerMove(event: PointerEvent, trackIndex?: number) { ... } function clearHover() { ... } function nearestPoint(series: DisplaySeries, xValue: number) { ... } - + return { hoveredSeriesPoints, hoveredTrackIndex, @@ -270,6 +285,7 @@ src/components/ ``` **来源**: + - `WaveformToolbar.vue` → `interaction/WaveformToolbar.vue` - `WaveformTooltip.vue` → `interaction/WaveformTooltip.vue` - `WaveformChart.vue` 中的交互逻辑 → `interaction/useInteraction.ts` + `interaction/useHover.ts` @@ -281,21 +297,23 @@ src/components/ **职责**:管理标注和图形(创建、编辑、删除、渲染) **文件**: + - `annotation/WaveformAnnotationLayer.vue` - 标注渲染层(已存在) - `annotation/WaveformEditor.vue` - 标注编辑器(已存在) - `annotation/useAnnotation.ts` - 标注管理逻辑 + ```typescript export function useAnnotation(options: AnnotationOptions) { const selection = ref(null) const editingDraft = ref(null) const rangeDraft = ref(null) - + function createAnnotation(point: WaveformPoint, seriesId: string) { ... } function editAnnotation(id: string) { ... } function deleteAnnotation(id: string) { ... } function selectMarkup(kind: 'annotation' | 'shape', id: string) { ... } - + return { selection, editingDraft, @@ -309,6 +327,7 @@ src/components/ ``` - `annotation/markup.ts` - 标注工具函数 + ```typescript export function layoutAnnotationBox(...) { ... } export function resolveAnnotationStyle(...) { ... } @@ -325,6 +344,7 @@ src/components/ ``` **来源**: + - `WaveformAnnotationLayer.vue` → `annotation/WaveformAnnotationLayer.vue` - `WaveformEditor.vue` → `annotation/WaveformEditor.vue` - `waveform-markup.ts` → `annotation/markup.ts` + `annotation/types.ts` @@ -464,26 +484,20 @@ watch(() => props.data, resetViewport) :track="track" @pointer-move="handlePointerMove($event, track.index)" /> - + - + - + - + - - + + { **风险**:移动文件可能导致测试失败、功能损坏 **缓解措施**: + - 分阶段重构,每个阶段运行测试 - 保持向后兼容 - 使用 Git 分支,可以随时回滚 @@ -548,6 +563,7 @@ describe('useZoom', () => { **风险**:大量文件的导入路径需要更新 **缓解措施**: + - 使用 IDE 的重构功能 - 在新目录的 `index.ts` 中保持导出一致 - 逐步迁移,保留旧路径的重导出 @@ -557,6 +573,7 @@ describe('useZoom', () => { **风险**:类型定义分散后可能产生循环依赖 **缓解措施**: + - 明确类型依赖关系 - 共享类型放在 `core/types.ts` - 避免系统之间直接依赖类型 @@ -566,6 +583,7 @@ describe('useZoom', () => { **风险**:拆分可能影响打包体积和加载性能 **缓解措施**: + - 使用 Tree-shaking 优化 - 合理使用动态导入 - 监控打包体积变化 diff --git a/.claude/plan.md b/.claude/plan.md index b1d4546..c0dfe15 100644 --- a/.claude/plan.md +++ b/.claude/plan.md @@ -3,6 +3,7 @@ ## 目标 将 `WaveformChart.vue`(1743 行)拆分为更小的、职责单一的子组件: + 1. **WaveformTooltip.vue** - 悬浮提示组件 2. **WaveformTrack.vue** - 单个波形轨道组件 3. **WaveformAnnotationLayer.vue** - 标注层组件 @@ -10,6 +11,7 @@ ## 当前代码分析 ### 主组件职责(过多) + - ✅ 数据管理和状态协调 - ✅ 缩放和交互事件处理 - 🔴 渲染波形轨道(网格、轴、波形线、十字线) @@ -19,13 +21,14 @@ - ✅ 编辑器管理(已拆分) ### 模板结构(1055-1743 行) + ```vue
... - + ... @@ -34,10 +37,10 @@ - +
...
- + @@ -51,10 +54,11 @@ **职责**:显示鼠标悬浮时的数据点信息 **Props**: + ```typescript interface Props { visible: boolean - position: { x: number, y: number } + position: { x: number; y: number } timeUnit: 's' | 'ms' hoveredPoint: WaveformPoint | null seriesPoints: Array<{ @@ -68,6 +72,7 @@ interface Props { ``` **提取内容**: + - 模板:1463-1478 行(16 行) - 样式:1682-1742 行(61 行) - 计算属性:`tooltipStyle`(471-477 行) @@ -79,11 +84,12 @@ interface Props { **职责**:渲染单个波形轨道(网格、坐标轴、波形线、十字线、overlay) **Props**: + ```typescript interface Props { track: TrackLayout clipPathId: string - margin: { top: number, right: number, bottom: number, left: number } + margin: { top: number; right: number; bottom: number; left: number } innerWidth: number showTooltip: boolean zoomable: boolean @@ -91,11 +97,12 @@ interface Props { activeInteractionMode: WaveformInteractionMode frameNumber?: string | number timeUnit: 's' | 'ms' - hoveredPoint?: HoveredSeriesPoint // 用于显示十字线 + hoveredPoint?: HoveredSeriesPoint // 用于显示十字线 } ``` **Emits**: + ```typescript interface Emits { (e: 'pointer-move', event: PointerEvent): void @@ -108,6 +115,7 @@ interface Emits { ``` **提取内容**: + - 模板:1099-1265 行(166 行) - 相关函数: - `shouldShowYAxisLabel` (247-262) @@ -119,6 +127,7 @@ interface Emits { - 样式:部分轨道相关样式 **注意事项**: + - 轨道组件需要在父组件中接收 D3 渲染的坐标轴 - 或者在 `onMounted` 中自己调用 D3 渲染坐标轴 @@ -129,6 +138,7 @@ interface Emits { **职责**:渲染所有标注和图形(annotations + shapes) **Props**: + ```typescript interface Props { renderedAnnotations: RenderedAnnotation[] @@ -142,6 +152,7 @@ interface Props { ``` **Emits**: + ```typescript interface Emits { (e: 'select-markup', kind: 'annotation' | 'shape', id: string): void @@ -150,6 +161,7 @@ interface Emits { ``` **提取内容**: + - 模板:1285-1419 行(135 行) - 相关函数: - `isSelected` (491-493) @@ -166,12 +178,14 @@ interface Emits { ## 实施步骤 ### 阶段 1: 创建 WaveformTooltip.vue ✅ + 1. 创建组件文件 2. 提取模板和样式 3. 实现计算属性 `tooltipStyle` 4. 更新主组件使用新组件 ### 阶段 2: 创建 WaveformAnnotationLayer.vue ✅ + 1. 创建组件文件 2. 提取标注层模板 3. 提取相关工具函数 @@ -179,6 +193,7 @@ interface Emits { 5. 更新主组件 ### 阶段 3: 创建 WaveformTrack.vue ✅ + 1. 创建组件文件 2. 提取轨道渲染逻辑 3. 处理 D3 坐标轴渲染(使用 `ref` + `onMounted`) @@ -186,6 +201,7 @@ interface Emits { 5. 更新主组件 ### 阶段 4: 验证和测试 ✅ + 1. 运行类型检查 `pnpm typecheck` 2. 运行代码规范检查 `pnpm lint` 3. 运行单元测试 `pnpm test` @@ -196,20 +212,25 @@ interface Emits { ## 设计决策 ### 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. 样式隔离 + 每个组件使用 `