40776cd4fa
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m36s
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Validate - Code Quality (push) Failing after 4m40s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m22s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 1m21s
CI/CD Pipeline / Unit Tests (push) Failing after 6m50s
CI/CD Pipeline / Integration Tests (push) Successful in 2m30s
CI/CD Pipeline / Frontend Lint (push) Successful in 42s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 1m43s
CI/CD Pipeline / PR Build API Image (push) Has been skipped
CI/CD Pipeline / PR Build Web Image (push) Has been skipped
CI/CD Pipeline / PR Build Worker Image (push) Has been skipped
CI/CD Pipeline / Build Staging API Image (push) Successful in 13m59s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m13s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 4m22s
CI/CD Pipeline / Build Production API Image (push) Has been skipped
CI/CD Pipeline / Build Production Web Image (push) Has been skipped
CI/CD Pipeline / Build Production Worker Image (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (push) Failing after 32s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 6m9s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 6m45s
199 lines
7.5 KiB
Markdown
Executable File
199 lines
7.5 KiB
Markdown
Executable File
# VoiceLibrary 页面重构方案
|
||
|
||
## 现状分析
|
||
|
||
**当前文件:** `apps/web/src/pages/voices/VoiceLibrary.tsx` — 1783 行
|
||
|
||
**代码质量:**
|
||
- `any`: 0 处
|
||
- `ts-ignore`: 0 处
|
||
- `eslint-disable`: 0 处
|
||
- `TODO`: 1 处
|
||
- 整体质量良好,重构阻力小
|
||
|
||
## 目录结构(重构后)
|
||
|
||
```
|
||
apps/web/src/pages/voices/
|
||
├── VoiceLibrary.tsx # 主组件(目标:~550 行,-69%)
|
||
├── types.ts # 类型定义
|
||
├── constants.ts # 常量 + 配置
|
||
├── utils/
|
||
│ ├── format.ts # 格式化工具函数
|
||
│ └── audio.ts # 音频工具函数
|
||
├── components/
|
||
│ ├── VoiceCard.tsx # 预设音色卡片
|
||
│ ├── CloneVoiceCard.tsx # 克隆音色卡片
|
||
│ ├── CloneDetailModal.tsx # 克隆详情弹窗
|
||
│ ├── CloneCardSkeleton.tsx # 克隆卡片骨架屏
|
||
│ ├── UploadModal.tsx # 上传音色弹窗
|
||
│ ├── TTSModal.tsx # TTS合成弹窗
|
||
│ ├── VoiceFilterBar.tsx # 筛选栏(搜索/性别/语言)
|
||
│ └── VoiceTabBar.tsx # Tab切换栏
|
||
└── hooks/
|
||
├── useVoices.ts # 音色数据查询 + 筛选
|
||
├── useAudioPlayer.ts # 音频播放控制
|
||
├── useVoiceUpload.ts # 音色上传逻辑
|
||
├── useTTS.ts # TTS合成逻辑
|
||
└── useCloneVoice.ts # 克隆音色操作
|
||
```
|
||
|
||
## 三阶段渐进式重构
|
||
|
||
### Phase 1:抽离类型、常量、工具函数
|
||
|
||
**目标:** 主文件 1783 → ~1550 行(-13%)
|
||
|
||
**抽出内容:**
|
||
|
||
1. **`types.ts`** — 类型定义(~60行)
|
||
- `PresetVoiceDisplay` / `ClonedVoiceDisplay` / `VoiceCardProps` / `CloneVoiceCardProps`
|
||
- `TabKey` / `Toast` / `VoiceUploadMetadata`
|
||
- 现有 `mapPresetToDisplay` / `mapCloneToDisplay` 数据映射函数
|
||
|
||
2. **`constants.ts`** — 常量配置(~40行)
|
||
- `CLONE_STATUS_CONFIG` 克隆状态配置
|
||
- `GENDER_LABEL_MAP` / `LANGUAGE_LABEL_MAP` 性别/语言标签映射
|
||
- Tab 配置项
|
||
|
||
3. **`utils/format.ts`** — 格式化工具(~25行)
|
||
- `formatTime` 时长格式化
|
||
- `formatFileSize` 文件大小格式化
|
||
- `genderLabel` / `languageLabel` / `genderClass`
|
||
|
||
4. **`utils/audio.ts`** — 音频工具(~15行)
|
||
- `getAudioDuration` 获取音频文件时长
|
||
|
||
5. **`format.test.ts`** — 工具函数单测
|
||
- 覆盖 formatTime / formatFileSize 等纯函数
|
||
|
||
**Phase 1 交付:**
|
||
- 新增文件:6 个(types.ts / constants.ts / utils/format.ts / utils/audio.ts / format.test.ts)
|
||
- 主文件减少:~230 行
|
||
- 纯机械抽离,无逻辑改动
|
||
|
||
---
|
||
|
||
### Phase 2:抽离子组件
|
||
|
||
**目标:** 主文件 1550 → ~950 行(-39%)
|
||
|
||
**抽出组件:**
|
||
|
||
1. **`components/VoiceCard.tsx`** — 预设音色卡片(~120行)
|
||
- 卡片渲染:头像、名称、性别标签、播放按钮、进度条、收藏
|
||
- Props:voice / playingId / currentTime / onPlay / onPause / onSeek / onToggleStar
|
||
|
||
2. **`components/CloneVoiceCard.tsx`** — 克隆音色卡片(~160行)
|
||
- 三种状态:processing / success / failed
|
||
- 操作:播放、详情、删除、重试、使用
|
||
- Props:voice / playingId / currentTime / onPlayPause / onShowDetail / onDelete / onRetry / onUse
|
||
|
||
3. **`components/CloneDetailModal.tsx`** — 克隆详情弹窗(~90行)
|
||
- 展示克隆音色详细信息
|
||
- 状态展示、音频播放、操作按钮
|
||
|
||
4. **`components/CloneCardSkeleton.tsx`** — 克隆卡片骨架屏(~20行)
|
||
- 加载状态占位
|
||
|
||
5. **`components/UploadModal.tsx`** — 上传音色弹窗(~180行)
|
||
- 文件选择、名称/性别/描述填写
|
||
- 上传进度展示
|
||
- Props:open / onClose / onUpload
|
||
|
||
6. **`components/TTSModal.tsx`** — TTS合成弹窗(~170行)
|
||
- 文本输入、音色选择、语速调节
|
||
- 合成状态轮询(idle/synthesizing/done/error)
|
||
- 保存到素材库
|
||
- Props:open / onClose / onSave
|
||
|
||
7. **`components/VoiceFilterBar.tsx`** — 筛选栏(~80行)
|
||
- 搜索框、性别筛选、语言筛选
|
||
- Props:searchText / filterGender / filterLang / onChange handlers
|
||
|
||
**Phase 2 交付:**
|
||
- 新增组件:7 个
|
||
- 主文件减少:~600 行
|
||
- 纯UI抽离,业务逻辑保留在主组件
|
||
|
||
---
|
||
|
||
### Phase 3:抽离业务逻辑 Hook
|
||
|
||
**目标:** 主文件 950 → ~550 行(-42%)
|
||
|
||
**抽出 Hook:**
|
||
|
||
1. **`hooks/useVoices.ts`** — 音色数据 Hook(~200行)
|
||
- 三个 useQuery:presetVoices / cloneVoices / materialVoices / unifiedStats
|
||
- 筛选逻辑:searchText / filterGender / filterLang → filteredPreset / filteredClone
|
||
- 统计数据:presetCount / cloneCount / materialCount
|
||
- 收藏切换 handleToggleStar
|
||
- 返回:data / loading / filtered / counts / handlers
|
||
|
||
2. **`hooks/useAudioPlayer.ts`** — 音频播放控制 Hook(~120行)
|
||
- playingId / currentTime / intervalRef 状态
|
||
- handlePlay / handlePause / handleSeek
|
||
- 自动停止(切换新音频时停止旧的)
|
||
- 组件卸载清理
|
||
- 注意:与 VoiceMaterialLibrary 的 useAudioPlayer 类似但有差异(一个操作DOM音频,一个操作audio元素),评估是否复用还是独立
|
||
|
||
3. **`hooks/useVoiceUpload.ts`** — 音色上传 Hook(~120行)
|
||
- uploadFile / uploadName / uploadGender / uploadDesc / uploadProgress 状态
|
||
- uploadMutation(上传文件 + 创建音色)
|
||
- buildVoiceMetadata 元数据构建
|
||
- getAudioDuration 时长获取
|
||
- 成功后刷新列表 + toast 关闭弹窗
|
||
|
||
4. **`hooks/useTTS.ts`** — TTS合成 Hook(~110行)
|
||
- ttsOpen / ttsText / ttsVoiceId / ttsSpeed 弹窗状态
|
||
- ttsJobId / ttsStatus / ttsAudioUrl / ttsError 合成状态
|
||
- handleTtsSynthesize 发起合成 + 轮询
|
||
- handleTtsSave 保存到素材库
|
||
- handleTtsClose 清理状态
|
||
|
||
5. **`hooks/useCloneVoice.ts`** — 克隆音色操作 Hook(~80行)
|
||
- deleteMutation / retryMutation
|
||
- handleCloneDelete / handleCloneRetry
|
||
- detailVoice 详情状态
|
||
- 操作后刷新列表
|
||
|
||
**Phase 3 交付:**
|
||
- 新增 Hook:5 个
|
||
- 主文件减少:~400 行
|
||
- 主文件只剩:Tab切换逻辑 + JSX 组装 + 顶层状态编排
|
||
|
||
---
|
||
|
||
## 最终效果汇总
|
||
|
||
| 阶段 | 主文件行数 | 减少行数 | 减少比例 | 新增文件 |
|
||
|------|-----------|---------|----------|----------|
|
||
| 初始 | 1783 | — | — | — |
|
||
| Phase 1 | ~1550 | -233 | -13.1% | 5 |
|
||
| Phase 2 | ~950 | -600 | -38.7% | 7 |
|
||
| Phase 3 | ~550 | -400 | -42.1% | 5 |
|
||
| **总计** | **~550** | **-1233** | **-69.2%** | **17** |
|
||
|
||
## 与 GeneratePage / VoiceMaterialLibrary 的异同
|
||
|
||
**相同点:**
|
||
- 三阶段渐进式重构(类型常量 → 组件 → Hook)
|
||
- 每阶段独立PR,独立验证
|
||
- 每阶段加 smoke test 保证 vitest related 模式可用
|
||
- 代码质量基线高(0 any / 0 ts-ignore)
|
||
|
||
**不同点:**
|
||
- VoiceLibrary 有两个数据源(预设音色 + 克隆音色),还有TTS和上传功能
|
||
- 音频播放逻辑与 VoiceMaterialLibrary 类似但操作的音频类型不同
|
||
- 有 CloneModal 是外部组件(已在 @/components/voice/CloneModal),不需要重写
|
||
- 骨架屏组件比较简单
|
||
|
||
## 风险与注意事项
|
||
|
||
1. **vitest related 模式**:每阶段新增文件需建立测试覆盖,避免 "No test files found"
|
||
2. **Preview Deploy**:环境问题导致失败,不影响代码质量
|
||
3. **音频播放逻辑**:VoiceLibrary 和 VoiceMaterialLibrary 的播放控制类似但是两套独立实现,后续可考虑抽象为共享 Hook
|
||
4. **克隆状态**:CLONE_STATUS_CONFIG 是核心常量,抽离时注意类型完整
|
||
5. **上传逻辑**:uploadMutation 内部逻辑较复杂(上传文件 + 获取时长 + 创建音色),抽 Hook 时保持逻辑不变
|