# 清理预览区域 Mock 数据(完整版) **日期**: 2026-02-08 **类型**: 代码清理 + Bug 修复 **影响范围**: 前端预览面板 ## 概述 移除预览面板中所有的 mock 数据,确保只使用真实的 API 数据。同时修复了"正视图、侧视图、背视图"这些假数据的显示问题。 ## 🐛 核心问题 **问题描述**: 点击角色资源时,预览区域显示的是"正视图、侧视图、背视图",这些是 mock 数据,而不是真实的标签数据。 **根本原因**: ```typescript // 错误逻辑:当没有标签时,生成假的"正视图、侧视图、背视图" if (resource.type === 1) { if (hasTags) { return tags.map(...); // 显示真实标签 } // ❌ 没有标签时,返回假数据 return [ { title: '孙悟空 (正视图)', ... }, { title: '孙悟空 (侧视图)', ... }, { title: '孙悟空 (背视图)', ... }, ]; } ``` **正确逻辑**: ```typescript // ✅ 有标签 → 显示标签 // ✅ 没有标签 → 返回空数组,显示"暂无标签"空状态 if (resource.type === 1) { if (hasTags) { return tags.map(...); // 显示真实标签 } return []; // 没有标签时返回空数组 } ``` ## 清理内容 ### ✅ 已清理的 Mock 数据 #### 1. **AI 生成功能的假数据** (`usePreviewActions.ts`) **清理前**: ```typescript // 随机获取一张 Unsplash 图片作为生成结果 const mockGeneratedUrl = `https://images.unsplash.com/photo-${...}?w=800&q=80`; if (isTagItem && tags) { updatedTags[index] = { ...updatedTags[index], thumbnail_url: mockGeneratedUrl, // ❌ 假数据 }; } ``` **清理后**: ```typescript toast({ title: 'AI 生成功能未实现', description: `需要接入真实的 AI 生成 API 为 "${targetName}" 生成图片`, variant: 'destructive', }); // TODO: 接入真实的 AI 生成 API // 1. 调用后端 AI 生成接口 // 2. 等待生成完成 // 3. 更新资源/标签的图片URL ``` #### 2. **视频数据 Mock** (`usePreviewData.ts`) **清理前**: ```typescript import { mockStoryboardVideos } from '@/mocks'; // ... const storyboardVideos = mockStoryboardVideos.filter((v) => v.storyboardId === storyboardId); ``` **清理后**: ```typescript // 移除 import // ... // 获取该分镜下的所有视频 (TODO: 使用真实API替换) const storyboardVideos: any[] = []; // 临时置空,等待视频API实现 ``` **说明**: 后端已有视频API (`server/app/api/v1/storyboard_resources.py::get_storyboard_videos`),但前端尚未实现对应的 Hook,需要后续添加 `useStoryboardVideos()`. #### 3. **音效数据 Mock** (`SoundEffectPreview.tsx`) **清理前**: ```typescript import { mockStoryboardSoundEffects } from '@/mocks/storyboard-sound-effects'; // ... return mockStoryboardSoundEffects.filter((e) => e.storyboardId === storyboardId); ``` **清理后**: ```typescript // 移除 import // ... const effects = useMemo(() => { // TODO: 使用真实的音效API // return useSoundEffects(storyboardId); if (!storyboardId) return []; return []; // 临时置空,等待音效API实现 }, [storyboardId]); ``` #### 4. **资源标签和档案 Mock** (`ResourceInfoPanel.tsx`) **清理前**: ```typescript import { mockCharacterTags, mockLocationVariantTags, mockPropTags, mockTagCategories, mockProfiles, } from '@/mocks'; // Mock tags data const tags: Tag[] = useMemo(() => { if (resourceType === 1) return mockCharacterTags; if (resourceType === 2) return mockLocationVariantTags; if (resourceType === 3) return mockPropTags; return []; }, [resourceType, resourceId]); // Mock profile data const mockData = mockProfiles[typeStr]; return mockData ? { ...mockData, name: resourceName, type: typeStr } : {...}; ``` **清理后**: ```typescript // 移除所有 mock imports // TODO: 从真实API获取标签数据 const tags: Tag[] = useMemo(() => { if (!resourceType || !resourceId) return []; // 应该从 useCharacterDetail / useLocationDetail / usePropDetail 获取 return []; }, [resourceType, resourceId]); // TODO: 从真实API获取资源档案数据 const profileData = useMemo(() => { // 应该从 useCharacterDetail / useLocationDetail / usePropDetail 获取 return { type: typeStr, name: resourceName, description: '', }; }, [resourceType, resourceName]); // 标签分类也改为空数组 ``` **说明**: `ResourceInfoPanel` 目前未被 `PreviewPanel` 使用,但仍清理干净以备未来使用。 ### ⚠️ 保留的临时逻辑 以下代码**暂时保留空数组**,等待对应的真实API实现: 1. **视频列表**: `const storyboardVideos: any[] = [];` - 需要实现: `useStoryboardVideos(storyboardId)` - 后端API: `GET /api/v1/storyboard-resources/{storyboardId}/videos` 2. **音效列表**: `return [];` - 需要实现: `useSoundEffects(storyboardId)` - 后端API: (需确认是否存在) 3. **标签分类**: `categories={[]}` - 需要实现: `useTagCategories(projectId, resourceType)` - 后端API: (需确认是否存在) ## 清理后的数据流 ### ✅ 当前可用的功能 (使用真实API) ``` 用户点击角色/场景/道具 ↓ useCharacterDetail / useLocationDetail / usePropDetail ↓ GET /projects/{projectId}/characters/{characterId} ↓ 返回真实的角色+标签+资源数据 ↓ PreviewPanel 显示真实的缩略图和标签 ``` ### ⏳ 待实现的功能 1. **视频预览** ``` 用户点击分镜中的视频元素 ↓ useStoryboardVideos(storyboardId) // TODO: 需实现 ↓ GET /api/v1/storyboard-resources/{storyboardId}/videos ↓ 显示真实视频列表 ``` 2. **音效预览** ``` 用户切换到音效标签 ↓ useSoundEffects(storyboardId) // TODO: 需实现 ↓ GET /api/v1/storyboard-resources/{storyboardId}/sound-effects (?) ↓ 显示真实音效列表 ``` 3. **AI 生成图片** ``` 用户点击"生成"按钮 ↓ 调用 AI 生成 API // TODO: 需实现 ↓ POST /api/v1/ai/generate-image (?) ↓ 更新资源/标签的图片URL ``` ## 影响分析 ### 功能影响 - ✅ **角色/场景/道具预览**: 正常工作,使用真实API - ⚠️ **视频预览**: 暂时显示空列表,不影响其他功能 - ⚠️ **音效预览**: 暂时显示空列表,不影响其他功能 - ❌ **AI 生成**: 点击按钮会提示"功能未实现",不会再生成假数据 ### 用户体验 **改进**: - 不再显示误导性的假数据 - 用户看到的都是真实的项目数据 **降级**: - 视频和音效预览暂时为空 (但这些功能本来就未完全实现) - AI 生成按钮暂时不可用 (但之前生成的也是假数据) ## 后续任务 ### 高优先级 (影响核心功能) 1. **实现视频 Hook** ```typescript // client/src/hooks/api/useStoryboardVideos.ts export function useStoryboardVideos(storyboardId: string | null) { return useQuery({ queryKey: ['storyboard-videos', storyboardId], queryFn: () => storyboardVideosApi.getVideos(storyboardId!), enabled: !!storyboardId, }); } ``` 2. **实现音效 Hook** ```typescript // client/src/hooks/api/useSoundEffects.ts export function useSoundEffects(storyboardId: string | null) { return useQuery({ queryKey: ['sound-effects', storyboardId], queryFn: () => soundEffectsApi.getSoundEffects(storyboardId!), enabled: !!storyboardId, }); } ``` ### 中优先级 (增强用户体验) 3. **实现 AI 生成图片功能** - 后端: 接入真实的 AI 图片生成服务 (如 Stable Diffusion, DALL-E) - 前端: 调用生成API,显示进度,更新图片 4. **实现标签分类管理** - 后端: 标签分类的CRUD API - 前端: `useTagCategories()` Hook ### 低优先级 (可选) 5. **完善 ResourceInfoPanel** - 集成真实的资源详情数据 - 显示完整的资源档案信息 ## 验证方法 ### 1. 测试有标签的资源 ``` 1. 在后端创建一个角色,并添加标签 (如"便装"、"战斗装") 2. 在前端点击该角色 3. 预览面板应该显示: ✅ "孙悟空 - 便装" (标签1) ✅ "孙悟空 - 战斗装" (标签2) ❌ 不应该显示"正视图、侧视图、背视图" ``` ### 2. 测试没有标签的资源 ``` 1. 在后端创建一个角色,不添加任何标签 2. 在前端点击该角色 3. 预览面板应该显示: ✅ 空状态提示 (如"暂无标签"或"No items to display") ❌ 不应该显示"正视图、侧视图、背视图" ``` ### 3. 检查控制台是否有 mock 相关的警告 ```bash cd client npm run dev # 打开浏览器控制台,检查是否有 "mock" 相关的日志 ``` ### 4. 搜索代码中是否还有 mock 导入 ```bash cd client/src/components/features/preview rg "from.*@/mocks" --type ts rg "from.*mocks" --type ts rg "mockStoryboard|mockCharacter|mockLocation|mockProp" --type ts ``` **预期结果**: 只在以下文件中保留 mock (非预览核心功能): - `HistoryPanel.tsx` (历史记录面板,目前完全是mock UI) - `ResourceProfileTab.tsx` (配音选择,使用 `mockVoices`) - `VoiceSelectionDialog.tsx` (配音选择,使用 `mockVoices`) - `StoryboardResourcesPreview.tsx` (一条 Mock 逻辑注释) ### 5. 测试空状态显示 1. 打开项目页面 2. 切换到"角色"标签 3. 点击一个没有标签的角色 4. 预览面板应该显示: - ✅ 空状态组件 (EmptyState) - ✅ 提示文字 (如"No items to display") - ❌ 不应该有"正视图、侧视图、背视图" ## 对比图 ### 清理前 (❌ 错误) ``` 点击"孙悟空"角色 (无标签) ↓ 预览面板显示: - 孙悟空 (正视图) ← ❌ 假数据 - 孙悟空 (侧视图) ← ❌ 假数据 - 孙悟空 (背视图) ← ❌ 假数据 ``` ### 清理后 (✅ 正确) ``` 点击"孙悟空"角色 (无标签) ↓ 预览面板显示: - "暂无标签" 空状态 ← ✅ 真实状态 点击"孙悟空"角色 (有标签) ↓ 预览面板显示: - 孙悟空 - 便装 ← ✅ 真实标签 - 孙悟空 - 战斗装 ← ✅ 真实标签 ``` ## 测试角色创建流程 ### 1. 创建带标签的角色 ```bash # 后端测试脚本 curl -X POST http://localhost:8000/api/v1/projects/{projectId}/characters \ -H "Content-Type: application/json" \ -d '{ "name": "测试角色", "description": "用于测试标签显示", "role_type": 2, "tags": ["便装", "战斗装"] }' ``` **预期**: - 前端点击该角色,预览面板显示2个标签 - 不显示"正视图、侧视图、背视图" ### 2. 创建无标签的角色 ```bash curl -X POST http://localhost:8000/api/v1/projects/{projectId}/characters \ -H "Content-Type: application/json" \ -d '{ "name": "测试角色2", "description": "无标签角色", "role_type": 2 }' ``` **预期**: - 前端点击该角色,预览面板显示空状态 - 不显示"正视图、侧视图、背视图" ### 3. 测试资源预览 1. 打开项目页面 2. 切换到"角色"标签 3. 点击任意角色 4. 查看预览面板: - ✅ 应该显示真实的标签列表 (如果有标签) - ✅ 应该显示空状态 (如果没有标签) - ✅ 标签应该有真实的缩略图 (如果有上传) - ✅ 点击"生成"按钮应该提示"功能未实现" - ❌ 不应该显示"正视图、侧视图、背视图" ## 相关文件 ### 已修改的文件 - `client/src/components/features/preview/hooks/usePreviewData.ts` (**核心修改**: 移除"正视图、侧视图、背视图"生成逻辑) - `client/src/components/features/preview/hooks/usePreviewActions.ts` (移除假的AI生成逻辑) - `client/src/components/features/preview/SoundEffectPreview.tsx` (移除 mockStoryboardSoundEffects) - `client/src/components/features/preview/ResourceInfoPanel.tsx` (移除所有mock imports) ### 未修改的文件 (合理保留 mock) - `client/src/components/features/preview/HistoryPanel.tsx` (历史记录UI,纯展示) - `client/src/components/features/preview/tabs/ResourceProfileTab.tsx` (配音选择) - `client/src/components/features/preview/dialogs/VoiceSelectionDialog.tsx` (配音选择) ## 总结 本次清理**彻底移除了资源预览核心功能中的所有 mock 数据**,确保用户看到的都是真实的项目数据。 对于尚未实现的功能 (视频、音效、AI生成),采取了以下策略: - **视频/音效**: 临时返回空数组,不影响其他功能,等待API实现 - **AI生成**: 显示"功能未实现"提示,避免生成误导性的假数据 **核心原则**: 宁可显示空数据,也不显示假数据,确保用户对系统状态有正确的认知。