# 时间轴资源块点击自动切换资源库Tab功能 **日期**: 2026-01-28 **类型**: 功能增强 **影响范围**: 时间轴、资源库面板 ## 概述 实现了时间轴资源块点击时,自动切换资源库面板到对应资源类型 tab 的功能。 ## 需求 用户点击时间轴中的资源块时: - 点击**角色**资源 → 打开资源库 + 自动切换到**角色** tab - 点击**场景**资源 → 打开资源库 + 自动切换到**场景** tab - 点击**道具**资源 → 打开资源库 + 自动切换到**道具** tab - 点击**实拍**资源 → 打开资源库 + 自动切换到**实拍** tab ## 实现方案 采用**方案 1:通过 UIStore 集中管理**,符合 React 和 Zustand 最佳实践。 ### 架构设计 ``` TimelineItem (资源块点击) ↓ TimelineTrack.onResourceSelect ↓ useTimelineLogic.handleResourceSelect ↓ [查找资源对象,获取类型] → setResourcePanelActiveTab(type) ↓ UIStore.resourcePanelActiveTab (全局状态) ↓ LeftSidebar.activeCategory ↓ ProjectResourcePanel.filterType ↓ [自动切换到对应 Tab] ``` ### 技术实现 #### 1. UIStore 状态管理 **文件**: `client/src/stores/uiStore.ts` 新增状态和操作: ```typescript type ResourcePanelTab = 'character' | 'scene' | 'prop' | 'footage'; interface UIState { // ... 其他状态 resourcePanelActiveTab: ResourcePanelTab; // 新增:资源面板活动 tab // ... 其他操作 setResourcePanelActiveTab: (tab: ResourcePanelTab) => void; // 新增:设置活动 tab } ``` 状态持久化配置(已添加到 `partialize`): - 用户切换的 tab 会被保存到 localStorage - 下次打开时自动恢复上次选择的 tab #### 2. 时间轴资源选择逻辑 **文件**: `client/src/hooks/useTimelineLogic.ts` 增强 `handleResourceSelect` 函数: ```typescript const handleResourceSelect = useCallback( (_resourceId: string) => { const wasSelected = selectedResourceId === _resourceId; selectResource(_resourceId); // 查找资源对象以获取其类型 const resource = resources?.find((r) => r.id === _resourceId); if (wasSelected) { toggleResourcesPanel(); } else { setResourcesPanelOpen(true); // 根据资源类型切换到对应的 tab if (resource?.type) { const tabMap: Record = { character: 'character', scene: 'scene', prop: 'prop', footage: 'footage', }; const targetTab = tabMap[resource.type]; if (targetTab) { setResourcePanelActiveTab(targetTab); } } } }, [selectResource, setResourcesPanelOpen, toggleResourcesPanel, selectedResourceId, resources, setResourcePanelActiveTab] ); ``` **关键点**: - 通过 `resources` 数组查找当前资源的类型 - 使用类型映射表确保类型安全 - 只在打开面板时切换 tab(避免重复切换时的干扰) #### 3. 资源面板状态绑定 **文件**: `client/src/components/features/project/ProjectResourcePanel.tsx` 绑定全局状态: ```typescript const { resourcePanelActiveTab, setResourcePanelActiveTab } = useUIStore(); // 优先使用外部传入的 activeCategory,其次使用全局状态,最后使用内部状态 const filterType = activeCategory !== undefined ? activeCategory : resourcePanelActiveTab || internalFilterType; const handleFilterTypeChange = (type: string) => { if (onCategoryChange) { onCategoryChange(type); } else { // 更新全局状态 setResourcePanelActiveTab(type as 'character' | 'scene' | 'prop' | 'footage'); setInternalFilterType(type); } }; ``` **状态优先级**: 1. 外部 props (`activeCategory`) - 最高优先级,支持完全受控 2. 全局状态 (`resourcePanelActiveTab`) - 中等优先级,支持跨组件通信 3. 本地状态 (`internalFilterType`) - 最低优先级,后备方案 #### 4. 左侧边栏状态同步 **文件**: `client/src/components/layout/LeftSidebar.tsx` 移除本地状态,使用全局状态: ```typescript // 移除: const [activeCategory, setActiveCategory] = useState(RESOURCE_CATEGORIES[0].key); // 改为使用全局状态 const { resourcePanelActiveTab, setResourcePanelActiveTab } = useUIStore(); // 传递到子组件 setResourcePanelActiveTab(category as 'character' | 'scene' | 'prop' | 'footage')} /> ``` ## 修改文件 1. ✅ `client/src/stores/uiStore.ts` - 添加全局状态和操作 2. ✅ `client/src/hooks/useTimelineLogic.ts` - 增强资源选择逻辑 3. ✅ `client/src/components/features/project/ProjectResourcePanel.tsx` - 绑定全局状态 4. ✅ `client/src/components/layout/LeftSidebar.tsx` - 移除本地状态,使用全局状态 ## 测试要点 ### 功能测试 - [ ] 点击时间轴中的**角色**资源块,资源库应打开并显示角色 tab - [ ] 点击时间轴中的**场景**资源块,资源库应打开并显示场景 tab - [ ] 点击时间轴中的**道具**资源块,资源库应打开并显示道具 tab - [ ] 点击时间轴中的**实拍**资源块,资源库应打开并显示实拍 tab - [ ] 点击已选中的资源块,应切换资源库的打开/关闭状态 - [ ] 手动切换 tab,状态应正确保存 ### 状态持久化测试 - [ ] 切换到某个 tab 后刷新页面,应恢复上次选择的 tab - [ ] 跨浏览器标签页,状态应保持一致 ### 边界情况测试 - [ ] 资源类型为空或未知时,不应崩溃 - [ ] 资源库面板已打开时再次点击资源块,行为应符合预期 ## 优点 1. **集中状态管理**:符合 Zustand 架构,易于维护和调试 2. **状态持久化**:用户体验更好,记住用户选择 3. **类型安全**:TypeScript 严格类型检查,减少运行时错误 4. **易于扩展**:未来可从其他地方控制资源面板 tab(如快捷键、搜索结果等) 5. **向后兼容**:保留了 props 传递方式,支持完全受控组件 ## 性能考虑 - ✅ 使用 `useCallback` 避免不必要的重新渲染 - ✅ 状态更新只在必要时触发 - ✅ 资源查找操作使用 `Array.find()`,时间复杂度 O(n),可接受 ## 未来优化 1. 如果资源列表非常大(>1000),考虑使用 Map 优化查找性能 2. 可以添加资源块悬停预览功能 3. 支持快捷键切换资源 tab ## 相关文档 - [资源面板性能优化](./2026-01-28-resource-panel-performance-optimization.md) - [项目技术栈规范](../../.claude/skills/jointo-tech-stack/SKILL.md)