# 新建项目流程优化 > **日期**:2026-02-08 > **类型**:功能优化 > **影响范围**:前端 - CreateProjectModal --- ## 变更概述 优化新建项目流程,移除项目类型选择,固定显示集数字段,新增共享资源选择功能。 ## 背景 根据 [ADR 02: 跨项目资源共享](../../server/adrs/02-cross-project-resource-sharing.md) 的设计,用户在创建项目时: - 项目内容类型(content_type)选择增加了用户认知负担 - 用户更关心资源共享而非项目分类 - 需要支持跨项目复用资源(角色、场景、道具) ## 变更内容 ### 1. 移除项目类型选择 **变更前**: ```tsx ``` **变更后**: - 完全移除项目类型选择字段 - 表单 schema 不再包含 `projectType` - 后端 API 调用时不再传递 `contentType` 参数 **影响**: - 简化用户操作流程 - 减少认知负担 - 项目创建更聚焦于实际需求(集数、时长、资源) ### 2. 集数字段固定显示 **变更前**: ```tsx // 仅当项目类型为 series 或 anime 时显示 {showEpisodes && ( )} ``` **变更后**: ```tsx // 始终显示集数字段 ``` **影响**: - 所有项目默认支持多集结构 - 单集项目设置为 1 集即可 - 统一项目数据模型 ### 3. 新增共享资源选择功能 #### 3.1 UI 结构 新增两个组件: - `ResourceSelectorPanel.tsx` - 资源选择面板(树形结构) - `SelectedResourcesDisplay.tsx` - 已选资源展示(Tag 列表) #### 3.2 交互流程 ``` ┌─────────────────────────────────────────────────────────────────────┐ │ 项目属性 [关闭 ✕] │ ├─────────────────────────────────────────────────────────────────────┤ │ 项目名称: [_____________________] │ │ 集数: [1] 单集计划时长: [60] [分钟▾] │ │ │ │ 共享资源 [选择资源] ◄─────┐ │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ │ [角色] 孙悟空 (西游记第一季) [✕] │ │ │ │ │ [场景] 花果山 (西游记第一季) [✕] │ │ │ │ │ [道具] 金箍棒 (西游记第一季) [✕] │ │ │ │ └─────────────────────────────────────────────────────────────┘ │ │ │ [取消] [立即创建] │ │ └─────────────────────────────────────────────────────────────────────┘ │ 点击"选择资源"展开右侧面板 ─────────────────────────┘ │ ┌─────────────────────────────────────────────────────────────────────┤ │ ┌────────────────────┐ │ │ │ 共享资源 │ │ │ ├────────────────────┤ │ │ │ [ ] ▶ 我的项目 │ │ │ │ [✓] ▼ 西游记系列 │ │ │ │ [✓] 孙悟空 │ │ │ │ [✓] 花果山 │ │ │ │ [✓] 金箍棒 │ │ │ │ │ │ │ │ 已选 3 项资源 │ │ │ └────────────────────┘ │ └─────────────────────────────────────────────────────────────────────┘ ``` #### 3.3 数据结构 ```typescript interface SharedResource { id: string; type: 'project' | 'folder' | 'character' | 'scene' | 'prop'; name: string; parentName?: string; count?: number; } ``` #### 3.4 核心特性 **ResourceSelectorPanel**: - 树形结构展示项目/文件夹层级 - 支持多选(Checkbox) - 显示资源统计(角色数、场景数、道具数) - 展开/收起控制 - 滚动区域(ScrollArea) **SelectedResourcesDisplay**: - Tag 列表展示已选资源 - 不同资源类型不同颜色标识 - 项目/文件夹:蓝色/紫色 - 角色:绿色 - 场景:橙色 - 道具:粉色 - 支持删除单个资源 - 空状态提示 #### 3.5 状态管理 ```tsx // 共享资源面板展开状态 const [resourcePanelOpen, setResourcePanelOpen] = useState(false); // 已选资源列表 const [selectedResources, setSelectedResources] = useState([]); // 资源选择回调 const handleResourceSelect = useCallback((resources: SharedResource[]) => { setSelectedResources(resources); }, []); // 删除单个资源 const handleRemoveResource = useCallback((resourceId: string) => { setSelectedResources(prev => prev.filter(r => r.id !== resourceId)); }, []); ``` ### 4. 面板互斥逻辑 风格选择面板和资源选择面板互斥: ```tsx onClick={() => { setResourcePanelOpen(prev => !prev); setStylePanelOpen(false); // 关闭风格面板 }} ``` ## 技术实现 ### 文件变更 **修改**: - `client/src/components/features/project/CreateProjectModal.tsx` **新增**: - `client/src/components/features/project/ResourceSelectorPanel.tsx` - `client/src/components/features/project/SelectedResourcesDisplay.tsx` ### 依赖组件 - `@/components/ui/checkbox` - 复选框 - `@/components/ui/scroll-area` - 滚动区域 - `lucide-react` - 图标 - `Share2` - 共享资源按钮 - `Folder` - 项目/文件夹图标 - `Users` - 角色图标 - `MapPin` - 场景图标 - `Package` - 道具图标 - `ChevronRight/ChevronDown` - 展开/收起图标 ### 表单验证调整 ```typescript // 移除 projectType 字段 const createProjectSchema = z.object({ name: z.string().min(1).max(50), folderId: z.string().optional(), episodes: z.number().min(1), // 必填,不再可选 aspectRatio: z.enum([...]), duration: z.string().min(1), durationUnit: z.enum(['seconds', 'minutes']), styleAndRoles: z.string().max(500).optional(), }); ``` ### API 调用调整 ```typescript // 创建项目 const newProject = await createProject.mutateAsync({ name: data.name, folderId: data.folderId?.startsWith('virtual-') ? undefined : data.folderId, aspectRatio: data.aspectRatio, plannedDuration: durationInSeconds, styleAndCharacters, // TODO: 待后端 API 支持 // sharedResources: selectedResources.map(r => ({ id: r.id, type: r.type })), }); ``` ## 后续工作 ### Phase 1:Mock 数据替换为真实 API **当前状态**: - 资源树使用硬编码的 mock 数据 - 位置:`ResourceSelectorPanel.tsx` 第 26-87 行 **待实现**: ```typescript // 1. 创建 API 接口 // GET /api/v1/projects/shareable-resources export async function getShareableResources(): Promise { const response = await apiClient.get('/projects/shareable-resources'); return response.data; } // 2. 使用 React Query 获取数据 const { data: resourceTree } = useQuery({ queryKey: ['shareable-resources'], queryFn: getShareableResources, enabled: resourcePanelOpen, // 仅展开时加载 }); ``` ### Phase 2:保存共享关系 **待实现**: ```typescript // 创建项目时传递共享资源 const newProject = await createProject.mutateAsync({ // ... 其他字段 sharedResources: selectedResources.map(r => ({ resourceId: r.id, resourceType: r.type, })), }); ``` **后端需求**: - 参考 ADR 02 第 6.1 节:新建项目时共享资源 - 接口:`POST /api/v1/projects` - 请求体新增字段:`shared_resources: Array<{resource_id, resource_type, share_type}>` ### Phase 3:性能优化 **优化方向**: 1. **虚拟滚动**(资源多时) - 使用 `react-virtual` 或 `react-window` - 优化大列表渲染性能 2. **搜索过滤** - 添加资源搜索框 - 支持按名称过滤 3. **批量操作** - 全选/取消全选项目 - 全选/取消全选文件夹下所有资源 ## 测试建议 ### 1. 功能测试 - [ ] 创建项目时集数字段始终显示且默认为 1 - [ ] 点击"选择资源"按钮展开右侧面板 - [ ] 资源树正确展开/收起 - [ ] 多选资源后在左侧 Tag 列表正确显示 - [ ] 删除单个资源 Tag 正常工作 - [ ] 风格面板和资源面板互斥显示 - [ ] 弹窗宽度根据面板展开状态自适应 ### 2. 边界测试 - [ ] 未选择资源时显示空状态提示 - [ ] 选择大量资源时滚动正常 - [ ] 关闭弹窗后清空选择状态 - [ ] 资源名称过长时截断显示 ### 3. 样式测试 - [ ] 不同资源类型颜色区分明显 - [ ] 资源树缩进层级清晰 - [ ] Hover 状态过渡自然 - [ ] 暗色模式适配(如果有) ## 相关文档 - [ADR 02: 跨项目资源共享](../../server/adrs/02-cross-project-resource-sharing.md) - [ADR 01: 项目级资源归属](../../server/adrs/01-project-level-resource-ownership.md) ## 变更记录 - 2026-02-08:初始版本 - 移除项目类型、新增共享资源选择