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.7 KiB
7.7 KiB
项目选择路由驱动架构
任务背景
实现全局项目选择的统一管理,确保:
- 项目面板选择项目后,素材面板同步显示
- 素材面板选择项目后,项目面板同步选中
- 左侧搜索框选择项目后,全局同步
- 支持 URL 分享和浏览器前进/后退
- 刷新页面保持项目选择状态
架构方案
方案选择:路由驱动 + 单一数据源
核心理念:路由参数作为唯一真相来源
优势:
- ✅ 支持项目 URL 分享
- ✅ 浏览器前进/后退自动切换项目
- ✅ 刷新页面保持项目选择
- ✅ 单一数据源,避免状态不一致
- ✅ 符合 Web 标准
技术架构
1. 路由配置
文件:client/src/constants/routes.ts
已有路由配置:
EDITOR: '/editor/:projectId'
辅助函数:
export const getEditorPath = (projectId: string) => `/editor/${projectId}`;
2. 自定义 Hook:useProjectRoute
文件:client/src/hooks/useProjectRoute.ts
功能:
- 从路由参数读取
projectId - 提供
setProjectId方法用于路由导航 - 自动同步路由参数到
appStore.currentProjectId
接口:
interface UseProjectRouteReturn {
projectId: string | null;
setProjectId: (projectId: string) => void;
}
实现要点:
- 使用
useParams读取路由参数 - 使用
useNavigate进行路由导航 - 使用
useEffect同步到全局状态
3. UI Store 集成
文件:client/src/stores/uiStore.ts
修改内容:
selectProject方法增加options参数- 支持传入
navigateToProject回调函数 - 保留
selectedProjectId用于 UI 状态(如高亮显示)
新签名:
selectProject: (
projectId: string | null,
projectName?: string,
options?: {
autoCloseProjectPanel?: boolean;
navigateToProject?: (projectId: string) => void;
}
) => void;
实现步骤
步骤1:创建 useProjectRoute Hook ✅
文件:client/src/hooks/useProjectRoute.ts
实现内容:
- 封装路由参数读取逻辑
- 封装项目切换的路由导航逻辑
- 同步路由参数到 appStore
步骤2:修改 uiStore.ts ✅
文件:client/src/stores/uiStore.ts
修改内容:
- 修改
selectProject方法签名,添加options参数 - 支持传入
navigateToProject回调 - 调用回调函数进行路由导航
步骤3:修改 ProjectTreeNode.tsx ✅
文件:client/src/components/features/project/ProjectTreeNode.tsx
修改内容:
- 导入
useProjectRouteHook - 使用
setProjectId方法 - 点击项目时传递导航函数给
selectProject
代码变更:
const { setProjectId } = useProjectRoute();
const handleClick = () => {
if (isProject && node.projectId) {
selectProject(node.projectId.toString(), node.name, {
autoCloseProjectPanel: true,
navigateToProject: setProjectId,
});
}
};
步骤4:修改 LeftSidebar.tsx ✅
文件:client/src/components/layout/LeftSidebar.tsx
修改内容:
- 导入
useProjectRouteHook - 使用
projectId替代selectedProjectId - 项目选择时调用路由导航
核心变更:
const { projectId, setProjectId } = useProjectRoute();
const { data: currentProject } = useProject(projectId || null);
const handleProjectSelect = (selectedProjectId: string, projectName: string) => {
selectProject(selectedProjectId, projectName, {
autoCloseProjectPanel: false,
navigateToProject: setProjectId,
});
setInputValue(projectName);
};
步骤5:修改 ProjectResourcePanel.tsx ✅
文件:client/src/components/features/project/ProjectResourcePanel.tsx
修改内容:
- 移除内部项目状态管理(
internalSelectedProjectId) - 完全使用外部
projectIdprop - 简化
handleProjectSelect逻辑 - 移除自定义事件监听
核心变更:
// 完全使用外部 projectId
const selectedProjectId = externalProjectId;
// 简化项目选择处理
const handleProjectSelect = useCallback((_projectId: string, _projectName: string) => {
setSelectedResources(new Set());
setSearchQuery('');
}, [setSearchQuery, setSelectedResources]);
数据流
用户操作流程
1. 从项目面板选择项目
用户点击项目
↓
ProjectTreeNode.handleClick()
↓
selectProject(projectId, projectName, { navigateToProject: setProjectId })
↓
useProjectRoute.setProjectId(projectId)
↓
navigate('/editor/:projectId')
↓
路由变化 → useParams 返回新 projectId
↓
LeftSidebar, ProjectResourcePanel 接收到新 projectId
↓
UI 自动同步更新
2. 从左侧搜索框选择项目
用户选择项目
↓
LeftSidebar.handleProjectSelect()
↓
selectProject(projectId, projectName, { navigateToProject: setProjectId })
↓
路由导航
↓
全局同步
3. 浏览器前进/后退
用户点击浏览器前进/后退
↓
路由自动变化
↓
useParams 返回新 projectId
↓
所有组件自动同步
4. 直接访问项目 URL
用户访问 /editor/123
↓
路由匹配
↓
useParams 返回 projectId: "123"
↓
组件加载对应项目数据
状态管理层次
1. 路由层(最高优先级)
- 数据源:URL 参数
/editor/:projectId - 读取:
useParams<{ projectId: string }>() - 写入:
navigate(getEditorPath(projectId)) - 作用:唯一真相来源
2. 应用层
- Store:
appStore.currentProjectId - 同步:由
useProjectRouteHook 自动同步 - 作用:方便非 React 代码访问
3. UI 层
- Store:
uiStore.selectedProjectId - 作用:UI 状态(如高亮显示)
- 同步:由
selectProject方法更新
4. 组件层
- Props:
projectId向下传递 - 作用:组件受控
- 来源:从路由读取
测试要点
1. 项目面板选择测试 ✅
- 点击项目后 URL 变化
- 素材面板同步显示对应项目
- 项目面板自动收起
- 动画流畅
2. 左侧搜索框选择测试 ✅
- 选择项目后 URL 变化
- 素材面板同步更新
- 项目面板(如果打开)同步选中
- 搜索框显示项目名称
3. 路由功能测试 ✅
- 直接访问
/editor/:projectId加载对应项目 - 浏览器前进/后退切换项目
- 刷新页面保持项目选择
4. 联动测试 ✅
- 任何地方选择项目,所有组件同步
- 项目数据正确加载
- 没有重复请求
5. 边界情况测试
- 无效的 projectId
- 未登录状态
- 项目不存在
- 网络错误
优势总结
1. 用户体验
- ✅ 可分享的项目 URL
- ✅ 浏览器前进/后退支持
- ✅ 刷新页面保持状态
- ✅ 直观的地址栏反馈
2. 开发体验
- ✅ 单一数据源,易于调试
- ✅ 符合 Web 标准
- ✅ 代码清晰,易于维护
- ✅ 类型安全
3. 架构优势
- ✅ 解耦组件间依赖
- ✅ 减少状态同步复杂度
- ✅ 支持 SSR(未来扩展)
- ✅ 利于 SEO(如果需要)
后续优化建议
1. 错误处理
- 添加 404 页面处理无效项目 ID
- 添加权限检查
- 添加加载状态
2. 性能优化
- 使用 React Router 的 loader 预加载数据
- 添加项目数据缓存
- 优化路由切换动画
3. 功能增强
- 支持项目历史记录
- 支持项目快速切换(快捷键)
- 支持项目收藏/固定
4. 分析监控
- 添加路由跳转埋点
- 监控项目切换性能
- 分析用户项目访问模式
实施日期:2026-01-14
实施状态:✅ 核心功能已完成,待测试验证
下一步:边界情况测试和错误处理