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.
8.8 KiB
8.8 KiB
共享资源选择器数据结构适配
日期:2026-02-08 类型:技术调整 影响范围:前端 - ResourceSelectorPanel、API 类型定义
变更概述
根据真实 API 数据结构调整共享资源选择器的数据模型,适配文件夹/项目树 API 响应格式。
背景
初版使用的是简化的 mock 数据结构,与真实 API 返回的数据格式不匹配,需要调整以支持真实数据。
API 数据结构
文件夹/项目树 API
接口:GET /api/v1/folders/tree?category=1
响应结构:
{
"success": true,
"code": 200,
"message": "Success",
"data": {
"id": "root",
"name": "根目录",
"children": [
{
"id": "019c2dcf-d972-7fe3-9078-99d90bf68253",
"name": "文件夹1",
"type": "folder",
"description": "",
"color": "#F59E0B",
"level": 0,
"folderCategory": 1,
"projectCount": 4,
"subfolderCount": 0,
"children": [
{
"id": "019c3167-493c-7ca2-9996-ad158b430ef7",
"name": "西游记之大圣归来",
"type": "project",
"contentType": "series",
"aspectRatio": "16:9",
"status": 0,
"children": []
}
]
},
{
"id": "019c2dd7-8bb7-79d2-badd-a5df4dbc6bd1",
"name": "灭霸来了",
"type": "project",
"contentType": "ad",
"aspectRatio": "16:9",
"status": 0,
"children": []
}
]
}
}
层级关系
根目录 (虚拟)
├─ 我的项目 (虚拟节点,需要前端添加)
│ ├─ 文件夹1 (level=0)
│ │ ├─ 项目A
│ │ └─ 项目B
│ ├─ 文件夹2 (level=0)
│ │ └─ 子文件夹2-1 (level=1)
│ │ └─ 项目C
│ └─ 项目D (根级项目,与文件夹同级)
└─ 协作项目 (另一个虚拟节点,category=2)
关键特点:
- API 返回的是扁平的
root.children数组 - 文件夹和项目可以在同一层级(根级项目)
- 需要前端添加"我的项目"虚拟节点
level字段表示文件夹嵌套层级(0 开始)
数据结构调整
1. ResourceTreeNode 类型定义
变更前:
interface ResourceTreeNode {
id: string;
type: 'project' | 'folder' | 'character' | 'scene' | 'prop';
name: string;
children?: ResourceTreeNode[];
characterCount?: number;
sceneCount?: number;
propCount?: number;
}
变更后:
interface ResourceTreeNode {
id: string;
type: 'folder' | 'project' | 'character' | 'scene' | 'prop';
name: string;
description?: string | null;
children?: ResourceTreeNode[];
// 文件夹特有字段
color?: string;
level?: number;
folderCategory?: number;
projectCount?: number;
subfolderCount?: number;
// 项目特有字段
contentType?: string | null;
aspectRatio?: string | null;
thumbnailUrl?: string | null;
status?: number;
isSubproject?: boolean;
parentProjectId?: string | null;
screenplayId?: string | null;
// 资源统计(需要从其他接口获取)
characterCount?: number;
sceneCount?: number;
propCount?: number;
}
2. Mock 数据结构更新
变更前:
const mockResourceTree = [
{
id: 'folder-mine',
name: '我的项目',
children: [...]
}
];
变更后:
const mockResourceTree = [
{
id: 'virtual-mine',
type: 'folder',
name: '我的项目',
level: -1, // 虚拟根节点
children: [
{
id: '019c2dcf-d972-7fe3-9078-99d90bf68253',
name: '文件夹1',
type: 'folder',
color: '#F59E0B',
level: 0,
projectCount: 4,
children: [...]
},
{
id: '019c2dd7-8bb7-79d2-badd-a5df4dbc6bd1',
name: '灭霸来了',
type: 'project', // 根级项目
contentType: 'ad',
children: []
}
]
}
];
3. 虚拟节点处理
前端需要将 API 返回的 root.children 包装到"我的项目"虚拟节点中:
// API 数据转换函数(待实现)
function transformApiDataToTree(apiData: FolderTreeResponse): ResourceTreeNode[] {
return [
{
id: 'virtual-mine',
type: 'folder',
name: '我的项目',
level: -1,
children: apiData.data.children.map(transformNode),
},
];
}
function transformNode(node: FolderTreeNode): ResourceTreeNode {
return {
...node,
children: node.children?.map(transformNode),
};
}
新增文件
1. 类型定义文件
文件:client/src/types/shared-resource.ts
内容:
FolderTreeNode- API 返回的文件夹/项目节点FolderTreeResponse- 文件夹树 API 响应ProjectResourceNode- 项目资源节点ProjectResourcesResponse- 项目资源列表响应ResourceStats- 资源统计SharedResourceSubmit- 共享资源提交数据CreateProjectWithResourcesParams- 创建项目参数
2. API 服务文件
文件:client/src/services/api/shared-resources.ts
接口:
getShareableFoldersAndProjects()- 获取文件夹/项目树getProjectResources()- 获取项目资源列表getProjectResourceStats()- 获取项目资源统计getBatchProjectResourceStats()- 批量获取资源统计
代码调整
1. ResourceSelectorPanel.tsx
变更点:
- 更新
ResourceTreeNode类型定义(匹配 API 结构) - 更新 mock 数据(使用真实 API 结构)
- 虚拟节点 ID 改为
virtual-mine(原folder-mine) - 添加 API 集成准备(注释代码)
API 集成代码(待启用):
// 从 API 加载文件夹/项目树数据
const { data: folderTreeData, isLoading } = useQuery({
queryKey: ['shareable-folders-projects'],
queryFn: () => getShareableFoldersAndProjects(1), // 1 = 我的项目
enabled: open, // 仅面板打开时加载
});
// 使用 API 数据(待实现数据转换)
const treeData = folderTreeData
? transformApiDataToTree(folderTreeData)
: mockResourceTree;
2. 状态初始化
变更前:
const [expandedIds, setExpandedIds] = useState<Set<string>>(
new Set(['folder-mine'])
);
变更后:
const [expandedIds, setExpandedIds] = useState<Set<string>>(
new Set(['virtual-mine'])
);
项目资源加载策略
问题
项目节点需要显示资源统计(角色数、场景数、道具数),但:
- 文件夹树 API 不返回资源统计
- 需要额外调用
/projects/{id}/resources/stats获取 - 如果逐个项目请求会导致大量 API 调用
解决方案
方案 A:批量预加载(推荐)
// 1. 加载文件夹树
const folderTree = await getShareableFoldersAndProjects(1);
// 2. 提取所有项目 ID
const projectIds = extractProjectIds(folderTree);
// 3. 批量获取资源统计
const stats = await getBatchProjectResourceStats(projectIds);
// 4. 合并数据
const treeWithStats = mergeStats(folderTree, stats);
方案 B:懒加载
// 展开项目时才加载其资源统计
const handleExpandProject = async (projectId: string) => {
const stats = await getProjectResourceStats(projectId);
updateProjectStats(projectId, stats);
};
推荐使用方案 A:
- 优点:一次性加载,用户体验好,便于搜索和过滤
- 缺点:初始加载时间稍长
- 优化:后端实现批量统计接口,一次查询获取所有项目统计
后续工作
Phase 1:API 集成(高优先级)
- 实现
transformApiDataToTree数据转换函数 - 实现批量资源统计接口(后端)
- 集成 React Query 加载数据
- 添加加载状态 UI(骨架屏)
- 添加错误处理 UI
Phase 2:项目资源加载(中优先级)
- 实现项目资源懒加载
- 展开项目时加载角色/场景/道具列表
- 资源节点可选择功能
- 资源节点搜索支持
Phase 3:性能优化(低优先级)
- 实现虚拟滚动(项目 > 100 个)
- 缓存已加载的资源数据
- 优化批量统计查询性能
测试建议
数据结构测试
- 文件夹嵌套层级正确显示(level 0, 1, 2...)
- 根级项目与文件夹同级显示
- 虚拟节点"我的项目"正确展开
- 项目统计数据正确显示
API 集成测试
- API 加载失败时使用 mock 数据降级
- 加载状态正确显示
- 错误提示友好
- 数据刷新机制正常
兼容性测试
- 空文件夹正确显示
- 无项目的文件夹正常渲染
- 深层嵌套(5+ 层)性能正常
相关文档
变更记录
- 2026-02-08:数据结构适配真实 API