# ProjectsPage API 对接 **日期**: 2026-01-21 **类型**: 功能增强 **影响范围**: 项目列表页面及相关组件 ## 变更概述 完成 ProjectsPage 及相关组件与后端项目 API 的完整对接,替换 Mock 数据为真实 API 调用。 ## 变更详情 ### 1. CreateProjectModal 组件 **文件**: `client/src/components/features/project/CreateProjectModal.tsx` **变更内容**: - ✅ 使用 `useCreateProject()` hook 替换 `addProject()` 方法 - ✅ 使用 `useUpdateProject()` hook 替换 `updateProject()` 方法 - ✅ 添加异步错误处理和加载状态 - ✅ 虚拟根文件夹 ID('1', '2')转换为 `null` - ✅ 集成 Toast 通知反馈 **关键逻辑**: ```typescript // 创建项目 const newProject = await createProject.mutateAsync({ name: data.name, folderId: data.folderId === '1' || data.folderId === '2' ? null : data.folderId, contentType: data.projectType, // ...其他字段 }); // 更新项目 await updateProject.mutateAsync({ id: editingProjectInfo.id, data: { /* 更新数据 */ } }); ``` ### 2. DeleteConfirmModal 组件 **文件**: `client/src/components/features/project/DeleteConfirmModal.tsx` **变更内容**: - ✅ 使用 `useDeleteProject()` hook 删除项目(移至回收站) - ✅ 使用 `useDeleteFolder()` hook 删除文件夹(级联删除) - ✅ 添加异步错误处理和加载状态 - ✅ 更新删除提示文案(项目删除说明可恢复) **关键逻辑**: ```typescript if (deletingResource.type === 'project') { // 删除项目(移至回收站,30天内可恢复) await deleteProject.mutateAsync(deletingResource.id); } else if (deletingResource.type === 'folder') { // 删除文件夹(级联删除) await deleteFolder.mutateAsync({ id: deletingResource.id, cascade: true }); } ``` ### 3. ProjectsPage 页面 **文件**: `client/src/pages/ProjectsPage.tsx` **变更内容**: - ✅ 使用 `useProjects()` hook 替换 `useProjectStore()` - ✅ 从 API 响应中提取项目列表 - ✅ 添加加载状态支持 - ✅ 保留项目过滤和显示逻辑 **关键逻辑**: ```typescript // 获取项目列表 const { data: projectsData, isLoading: isLoadingProjects } = useProjects(); const projects = projectsData?.items || []; ``` ### 4. 项目右键菜单组件 **文件**: - `client/src/components/features/project/ProjectContextMenu.tsx` - `client/src/components/features/project/ProjectContextMenuContent.tsx` **变更内容**: - ✅ 使用 `useCloneProject()` hook 替换 `cloneProject()` 方法 - ✅ 添加异步错误处理和 Toast 通知 - ✅ 简化编辑属性逻辑(暂时使用基础信息) **关键逻辑**: ```typescript const handleCloneProject = async () => { try { await cloneProjectMutation.mutateAsync({ id: projectId }); toast({ title: '项目已克隆', description: `项目 "${projectName}" 已成功克隆` }); } catch (error) { toast({ title: '克隆失败', variant: 'destructive' }); } }; ``` ### 4. FolderSelector 组件 **文件**: `client/src/components/features/project/FolderSelector.tsx` **变更内容**: - ✅ 使用 `useFolderTree()` hook 替换 Mock 数据 - ✅ 从后端 API 获取文件夹树形结构 - ✅ 添加加载状态和空状态显示 - ✅ 保留虚拟根节点(我的项目/协作项目) - ✅ 支持按 folderCategory 过滤 **关键逻辑**: ```typescript // 获取文件夹树 const { data: folderTreeData, isLoading } = useFolderTree(); const folderTree = folderTreeData?.tree || []; // 构建虚拟根节点 + 真实文件夹树 const fullTree = [ { folderId: VIRTUAL_ROOTS.mine.id, name: VIRTUAL_ROOTS.mine.name, children: folderTree.filter(node => node.folderCategory === 1), }, { folderId: VIRTUAL_ROOTS.collab.id, name: VIRTUAL_ROOTS.collab.name, children: folderTree.filter(node => node.folderCategory === 2), }, ]; ``` ## 技术细节 ### API Hooks 使用 所有组件统一使用 TanStack Query hooks: - `useProjects()` - 获取项目列表 - `useCreateProject()` - 创建项目 - `useUpdateProject()` - 更新项目 - `useDeleteProject()` - 删除项目(移至回收站) - `useCloneProject()` - 克隆项目 - `useDeleteFolder()` - 删除文件夹 ### 数据流转 ``` 用户操作 → Modal/Page 组件 → API Hook → 后端 API ↓ TanStack Query 缓存 ↓ 自动刷新相关查询 ``` ### 虚拟根文件夹处理 前端使用虚拟根文件夹 ID('1' = 我的项目, '2' = 协作项目),后端使用 `null` 表示根文件夹: ```typescript // 前端 → 后端 folderId: data.folderId === '1' || data.folderId === '2' ? null : data.folderId // 后端 → 前端(在 API 响应中处理) folderId: response.folderId || '1' // 默认为"我的项目" ``` ## 待优化项 ### 1. FolderSelector 文件夹路径 当前 FolderSelector 显示的路径仅为文件夹名称,未构建完整路径。 **建议优化**: ```typescript // 使用 folderApi.getPath() 获取完整路径 const path = await folderApi.getPath(folderId); const fullPath = path.map(p => p.name).join(' / '); ``` ### 2. 编辑项目属性 当前右键菜单的"编辑属性"功能使用简化逻辑,未从 API 获取完整项目详情。 **建议优化**: ```typescript const handleEditProperties = async () => { const project = await projectApi.getById(projectId); setCreateProjectModalOpen(true, null, { id: project.id, name: project.name, // ...完整项目信息 }); }; ``` ### 2. 加载状态显示 ProjectsPage 和 FolderSelector 已获取加载状态,建议在 UI 中显示。 **建议添加**: ```typescript {isLoadingProjects ? ( ) : ( )} ``` ### 3. 错误处理 当前仅在 Modal 中显示错误 Toast,页面级错误未处理。 **建议添加**: - 使用 `ErrorBoundary` 包裹页面 - 显示错误状态和重试按钮 ## 测试建议 ### 功能测试 1. **创建项目** - [ ] 在根文件夹创建项目 - [ ] 在子文件夹创建项目 - [ ] 验证项目出现在列表中 2. **更新项目** - [ ] 修改项目名称 - [ ] 修改项目属性 - [ ] 验证更新后的数据 3. **删除项目** - [ ] 删除项目(移至回收站) - [ ] 验证项目从列表中消失 - [ ] 验证可在回收站中找到 4. **克隆项目** - [ ] 克隆项目 - [ ] 验证副本出现在列表中 - [ ] 验证副本名称正确(带"副本"后缀) 5. **删除文件夹** - [ ] 删除空文件夹 - [ ] 删除包含项目的文件夹(级联删除) - [ ] 验证文件夹及内容被删除 ### 集成测试 1. **API 调用** - [ ] 验证请求参数正确 - [ ] 验证响应数据格式 - [ ] 验证错误处理 2. **缓存更新** - [ ] 创建后列表自动刷新 - [ ] 更新后详情自动刷新 - [ ] 删除后列表自动刷新 ## 相关文档 - [项目 API 对接](./2026-01-21-project-api-integration.md) - API Hooks 实现 - [项目回收站系统](../../server/changelogs/2026-01-21-project-trash-system.md) - 后端回收站实现 - [RFC 131: 项目回收站系统](../../server/rfcs/131-project-trash-system.md) - 回收站设计文档 ## 下一步 - [ ] 实现 ProjectPage 的 API 对接 - [ ] 添加加载状态和错误处理 UI - [ ] 优化编辑项目属性功能 - [ ] 实现回收站页面