# 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
- [ ] 优化编辑项目属性功能
- [ ] 实现回收站页面