You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
6.6 KiB
6.6 KiB
资源库数据集成 - 前端资源面板
日期: 2026-02-01
类型: 功能增强
影响范围: 前端资源面板数据源
变更概述
将 ProjectResourcePanel 的数据源从项目素材(project-resources)切换为资源库数据(resource-library),实现正确的业务逻辑:左侧面板显示角色/场景/道具档案,点击后在中间预览区域展示对应的标签和素材。
背景
之前的实现存在概念混淆:
- 错误实现: 左侧面板直接显示项目素材文件(图片/视频)
- 正确实现: 左侧面板显示资源库档案(角色/场景/道具),每个档案包含多个标签,每个标签下有多个素材
技术实现
1. 新增类型定义
文件: client/src/types/resource-library.ts
// 资源库核心类型
export interface ResourceLibraryCharacter { ... }
export interface ResourceLibraryLocation { ... }
export interface ResourceLibraryProp { ... }
export interface ResourceLibraryFootage { ... }
export interface ResourceLibraryTag { ... }
// 类型守卫和辅助函数
export function isCharacter(item: ResourceLibraryItem): item is ResourceLibraryCharacter
export function getResourceLibraryItemId(item: ResourceLibraryItem): string
export function getResourceLibraryItemThumbnail(item: ResourceLibraryItem): string | null
2. 新增资源库 API 服务
文件: client/src/services/mock/resourceLibraryApi.ts
export const resourceLibraryApi = {
getCharacters(projectId, options): Promise<ResourceLibraryCharacter[]>
getLocations(projectId, options): Promise<ResourceLibraryLocation[]>
getProps(projectId, options): Promise<ResourceLibraryProp[]>
getFootage(projectId, options): Promise<ResourceLibraryFootage[]>
getByType(projectId, type, options): Promise<ResourceLibraryItem[]>
}
功能:
- 支持按类型获取资源库数据
- 支持搜索过滤
- 支持分页(简化实现)
3. 修改 resourceApi 数据源
文件: client/src/services/mock/resourceApi.ts
核心变更:
// 旧实现:从 mockProjectResources 获取
let filteredResources = resources.filter((r) => r.projectId === projectId);
// 新实现:从资源库 API 获取
const items = await resourceLibraryApi.getByType(projectId, type, { search });
const convertedResources = items.map((item) => convertToResource(item, type));
数据转换逻辑:
- 角色/场景/道具 → Resource 格式(显示默认缩略图)
- 实拍素材 → 直接使用原始数据
- 在
metadata中保存资源数量、标签信息等
4. 更新 ProjectResourcePanel 组件
文件: client/src/components/features/project/ProjectResourcePanel.tsx
变更点:
-
标题显示资源数量:
const resourceCount = typeof item.data.metadata?.resourceCount === 'number' ? item.data.metadata.resourceCount : 0; const displayTitle = item.data.type !== RESOURCE_TYPES.FOOTAGE && resourceCount > 0 ? `${item.data.name} (${resourceCount})` : item.data.name; -
优化空状态提示:
description={ filterType === RESOURCE_TYPES.FOOTAGE ? '点击下方「上传实拍」按钮添加素材' : `点击下方「新增${categoryLabel}」按钮创建` }
Mock 数据示例
角色数据(带标签和资源)
{
character_id: '018e1234-5678-7abc-8def-300000000001',
name: '孙悟空',
description: '主角,齐天大圣',
role_type: 'main',
has_tags: true,
default_tag_id: 'tag_char_002', // 默认显示"青年"标签
resource_count: 8, // 所有标签的资源总数
default_thumbnail_url: 'https://...', // 青年标签的第一张图
tags: [
{
tag_id: 'tag_char_001',
tag_label: '少年',
resource_count: 3,
thumbnail_url: 'https://...'
},
{
tag_id: 'tag_char_002',
tag_label: '青年',
resource_count: 5,
thumbnail_url: 'https://...'
}
]
}
业务流程
用户交互流程
- 左侧资源库面板: 显示角色列表(孙悟空、唐僧、猪八戒...)
- 点击"孙悟空": 中间预览区域显示该角色的标签(少年、青年)
- 切换标签: 预览区域显示对应标签下的素材图片
- 设为默认标签: 左侧面板的缩略图自动更新为新默认标签的第一张图
数据流
用户选择 tab (角色/场景/道具/实拍)
↓
useResources hook
↓
resourceApi.getByProjectId(projectId, { type })
↓
resourceLibraryApi.getByType(projectId, type)
↓
返回资源库数据 (带标签和资源统计)
↓
转换为 Resource 格式
↓
ProjectResourcePanel 渲染
文件清单
新增文件
client/src/types/resource-library.ts- 资源库类型定义client/src/services/mock/resourceLibraryApi.ts- 资源库 API 服务
修改文件
client/src/types/index.ts- 导出资源库类型client/src/services/mock/resourceApi.ts- 切换数据源client/src/services/mock/index.ts- 导出资源库 APIclient/src/components/features/project/ProjectResourcePanel.tsx- 显示资源数量
测试要点
功能测试
- 切换 tab(角色/场景/道具/实拍)显示对应数据
- 搜索功能正常工作
- 显示资源数量(如"孙悟空 (8)")
- 无资源的项显示正确(如"唐僧")
- 实拍素材不显示资源数量
数据验证
- 角色数据包含
has_tags、default_tag_id、resource_count - 场景数据包含
location字段 - 道具数据包含完整的标签列表
- 实拍素材包含
duration、metadata
UI 验证
- 卡片显示默认缩略图
- 标题显示资源数量
- 空状态提示正确
- 删除操作正常
后续工作
- 中间预览区域: 实现点击角色后显示标签和素材的预览面板
- 标签切换: 实现标签切换时更新素材列表
- 设为默认标签: 实现"设为默认标签"按钮功能
- 真实 API 对接: 替换 mock 数据为真实后端 API
相关文档
注意事项
- 类型安全: 使用类型守卫确保数据类型正确
- 向后兼容: 保留
mockProjectResources用于上传功能 - 性能优化: 使用
useMemo和useCallback优化渲染 - 错误处理: 处理
metadata.resourceCount可能为null的情况