# 新建项目流程优化
> **日期**: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:初始版本 - 移除项目类型、新增共享资源选择