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.9 KiB
7.9 KiB
项目资源面板 - 添加角色/场景/道具编辑删除功能
日期: 2026-02-09
类型: Feature + Bugfix
影响范围: 前端 - 项目资源面板,后端 - 项目元素删除逻辑
概述
为项目资源面板中的角色(Character)、场景(Location)、道具(Prop)添加更多菜单,支持编辑和删除操作,与现有的实拍(Footage)功能保持一致。同时修复了后端删除接口的关键 Bug。
🐛 Bug 修复(重要)
问题描述
后端删除接口(角色/场景/道具)存在严重 Bug:
ProjectElementService的delete_*方法将整个对象传递给 Repository- 但 Repository 的
delete方法期望接收 UUID - 导致 500 错误:
invalid input for query argument $1
错误示例
DELETE /api/v1/projects/{project_id}/characters/{character_id} 500
错误信息:invalid UUID "character_id=UUID('...') description='...' role_type=2...":
length must be between 32..36 characters, got 499
根本原因
BaseRepository.delete(id: UUID) 期望接收 UUID,但被传递了完整的模型对象。
修复方案
后端修改:
-
Service 层 (
server/app/services/project_element_service.py)- ✅ 修改
delete_character:传递character_id而非character对象 - ✅ 修改
delete_location:传递location_id而非location对象 - ✅ 修改
delete_prop:传递prop_id而非prop对象
- ✅ 修改
-
Repository 层(新增 delete 方法覆盖)
- ✅
ProjectCharacterRepository.delete(character_id)- 正确使用character_id - ✅
ProjectLocationRepository.delete(location_id)- 正确使用location_id - ✅
ProjectPropRepository.delete(prop_id)- 正确使用prop_id
- ✅
技术细节:
- 各 Repository 继承自
BaseRepository,但主键字段名称不同 - ProjectCharacter 使用
character_id(非标准的id) - ProjectLocation 使用
location_id - ProjectProp 使用
prop_id - 必须覆盖
delete方法以使用正确的主键字段
变更内容
1. 前端 API 层 (client/src/services/api/project-elements.ts)
新增方法:
deleteCharacter(projectId, characterId)- 删除角色deleteLocation(projectId, locationId)- 删除场景deleteProp(projectId, propId)- 删除道具updateCharacter(projectId, characterId, payload)- 更新角色updateLocation(projectId, locationId, payload)- 更新场景updateProp(projectId, propId, payload)- 更新道具
对接后端接口:
DELETE /api/v1/projects/{project_id}/characters/{character_id}DELETE /api/v1/projects/{project_id}/locations/{location_id}DELETE /api/v1/projects/{project_id}/props/{prop_id}PUT /api/v1/projects/{project_id}/characters/{character_id}PUT /api/v1/projects/{project_id}/locations/{location_id}PUT /api/v1/projects/{project_id}/props/{prop_id}
2. Hooks 层 (client/src/hooks/api/useResourceLibrary.ts)
新增 Hooks:
useDeleteProjectElement(projectId)- 删除项目元素(角色/场景/道具)useUpdateProjectElement(projectId)- 更新项目元素(角色/场景/道具)
特性:
- 自动根据元素类型调用对应的 API 方法
- 操作成功后自动刷新资源库列表缓存
- 支持 TanStack Query 的 mutation 特性(loading、error 状态)
3. UI 组件 (client/src/components/features/project/ProjectResourcePanel.tsx)
功能增强:
- 更多菜单:所有资源类型(角色/场景/道具/实拍)的卡片右上角显示三点菜单
- 编辑功能:
- 点击"编辑"打开弹窗
- 支持修改名称和描述
- 验证名称不能为空
- 显示保存中状态
- 删除功能:
- 点击"删除"打开确认弹窗
- 显示资源名称提醒用户
- 根据资源类型调用对应的删除接口
实现细节:
- 复用现有的 Dialog 组件(延迟加载)
- 编辑弹窗根据资源类型动态显示标题
- 删除逻辑区分实拍和项目元素,调用不同的 API
- 更新逻辑区分实拍和项目元素,调用不同的 API
用户体验
操作流程
编辑资源:
- 鼠标悬停资源卡片,右上角显示三点菜单
- 点击三点菜单 → 选择"编辑"
- 弹窗中修改名称和描述
- 点击"保存"按钮
- 保存成功后自动关闭弹窗并刷新列表
删除资源:
- 鼠标悬停资源卡片,右上角显示三点菜单
- 点击三点菜单 → 选择"删除"
- 确认弹窗显示资源名称
- 点击"确认删除"
- 删除成功后自动刷新列表
UI 一致性
- 所有资源类型(角色/场景/道具/实拍)使用统一的菜单样式
- 编辑和删除图标与文案保持一致
- 删除选项使用红色(error)强调危险操作
技术细节
API 调用逻辑
// 删除逻辑
if (filterType === RESOURCE_TYPES.FOOTAGE) {
// 实拍使用 project-resources API
deleteResource.mutate({ id: resource.id });
} else {
// 角色/场景/道具使用 project-elements API
deleteElement.mutate({
type: filterType as 'character' | 'location' | 'prop',
elementId: resource.id,
});
}
// 更新逻辑
if (filterType === RESOURCE_TYPES.FOOTAGE) {
// 实拍使用 project-resources API
updateResource.mutate({ id, name, description });
} else {
// 角色/场景/道具使用 project-elements API
updateElement.mutate({
type: filterType as 'character' | 'location' | 'prop',
elementId,
payload: { name, description },
});
}
缓存失效策略
- 使用 TanStack Query 的
invalidateQueries - 操作成功后仅失效对应类型的资源列表:
queryClient.invalidateQueries({ queryKey: resourceLibraryKeys.list(projectId!, type) });
后续优化
- 批量操作:支持多选后批量删除
- 拖拽排序:支持通过拖拽调整资源顺序
- 快捷键:支持键盘快捷键(如 Delete 键删除选中资源)
- 撤销操作:删除后支持撤销恢复
代码变更统计
前端:
client/src/components/features/project/ProjectResourcePanel.tsx | +167 -62
client/src/hooks/api/useResourceLibrary.ts | +77
client/src/services/api/project-elements.ts | +38
后端(Bug 修复):
server/app/services/project_element_service.py | +3 -3
server/app/repositories/project_character_repository.py | +10
server/app/repositories/project_location_repository.py | +10
server/app/repositories/project_prop_repository.py | +10
文档:
docs/client/changelogs/2026-02-09-project-element-menu.md | +262 (新增)
--------------------------------------------------------------------
总计: 253 insertions(+), 65 deletions(-)
测试建议
功能测试
-
编辑功能:
- ✅ 编辑角色名称和描述
- ✅ 编辑场景名称和描述
- ✅ 编辑道具名称和描述
- ✅ 编辑实拍名称和描述
- ✅ 名称为空时禁用保存按钮
- ✅ 保存中显示 loading 状态
-
删除功能:
- ✅ 删除角色
- ✅ 删除场景
- ✅ 删除道具
- ✅ 删除实拍
- ✅ 删除后列表自动刷新
- ✅ 删除确认弹窗显示正确的资源名称
-
边界情况:
- ✅ 网络错误时显示错误提示
- ✅ 重复点击菜单不会多次打开弹窗
- ✅ 弹窗打开时点击背景可关闭
UI 测试
-
菜单显示:
- ✅ 鼠标悬停卡片时显示三点菜单
- ✅ 菜单图标位置正确(右上角)
- ✅ 菜单背景半透明黑色
-
弹窗样式:
- ✅ 编辑弹窗标题根据资源类型动态变化
- ✅ 删除按钮使用红色(destructive)样式
- ✅ 表单布局整齐,间距合理
关联文档
- 后端 API 文档:
server/app/api/v1/project_elements.py - 项目元素 Schema:
server/app/schemas/project_element.py - ADR: 项目级资源所有权 -
docs/server/adrs/01-project-level-resource-ownership.md