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.
 

7.3 KiB

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 通知反馈

关键逻辑:

// 创建项目
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 删除文件夹(级联删除)
  • 添加异步错误处理和加载状态
  • 更新删除提示文案(项目删除说明可恢复)

关键逻辑:

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 响应中提取项目列表
  • 添加加载状态支持
  • 保留项目过滤和显示逻辑

关键逻辑:

// 获取项目列表
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 通知
  • 简化编辑属性逻辑(暂时使用基础信息)

关键逻辑:

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 过滤

关键逻辑:

// 获取文件夹树
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 表示根文件夹:

// 前端 → 后端
folderId: data.folderId === '1' || data.folderId === '2' ? null : data.folderId

// 后端 → 前端(在 API 响应中处理)
folderId: response.folderId || '1' // 默认为"我的项目"

待优化项

1. FolderSelector 文件夹路径

当前 FolderSelector 显示的路径仅为文件夹名称,未构建完整路径。

建议优化:

// 使用 folderApi.getPath() 获取完整路径
const path = await folderApi.getPath(folderId);
const fullPath = path.map(p => p.name).join(' / ');

2. 编辑项目属性

当前右键菜单的"编辑属性"功能使用简化逻辑,未从 API 获取完整项目详情。

建议优化:

const handleEditProperties = async () => {
  const project = await projectApi.getById(projectId);
  setCreateProjectModalOpen(true, null, {
    id: project.id,
    name: project.name,
    // ...完整项目信息
  });
};

2. 加载状态显示

ProjectsPage 和 FolderSelector 已获取加载状态,建议在 UI 中显示。

建议添加:

{isLoadingProjects ? (
  <LoadingSpinner />
) : (
  <ProjectGridView ... />
)}

3. 错误处理

当前仅在 Modal 中显示错误 Toast,页面级错误未处理。

建议添加:

  • 使用 ErrorBoundary 包裹页面
  • 显示错误状态和重试按钮

测试建议

功能测试

  1. 创建项目

    • 在根文件夹创建项目
    • 在子文件夹创建项目
    • 验证项目出现在列表中
  2. 更新项目

    • 修改项目名称
    • 修改项目属性
    • 验证更新后的数据
  3. 删除项目

    • 删除项目(移至回收站)
    • 验证项目从列表中消失
    • 验证可在回收站中找到
  4. 克隆项目

    • 克隆项目
    • 验证副本出现在列表中
    • 验证副本名称正确(带"副本"后缀)
  5. 删除文件夹

    • 删除空文件夹
    • 删除包含项目的文件夹(级联删除)
    • 验证文件夹及内容被删除

集成测试

  1. API 调用

    • 验证请求参数正确
    • 验证响应数据格式
    • 验证错误处理
  2. 缓存更新

    • 创建后列表自动刷新
    • 更新后详情自动刷新
    • 删除后列表自动刷新

相关文档

下一步

  • 实现 ProjectPage 的 API 对接
  • 添加加载状态和错误处理 UI
  • 优化编辑项目属性功能
  • 实现回收站页面