diff --git a/apps/ai-game-creator-shell/src/view/project-development/ResourceClassificationPanel.tsx b/apps/ai-game-creator-shell/src/view/project-development/ResourceClassificationPanel.tsx index d8878127b..ebb1a1111 100644 --- a/apps/ai-game-creator-shell/src/view/project-development/ResourceClassificationPanel.tsx +++ b/apps/ai-game-creator-shell/src/view/project-development/ResourceClassificationPanel.tsx @@ -19,6 +19,13 @@ type UpdateLocalProjectResourceClassificationResult = { committedProjectRevision: number; }; +type DeleteLocalProjectAssetResult = { + assetId: string; + localPath: string; + committedProjectRevision: number; + fileRetained: boolean; +}; + const RESOURCE_CLASSIFICATION_CATEGORY_OPTIONS = GAME_CREATION_APP_ASSET_CATEGORIES.map((category) => ({ id: category, @@ -36,12 +43,19 @@ function resourceClassificationErrorMessage(error: unknown) { return '保存资源分类与标签失败'; } +function resourceDeleteErrorMessage(error: unknown) { + if (typeof error === 'string' && error.trim()) return error; + if (error instanceof Error && error.message) return error.message; + return '删除资源失败'; +} + type ResourceClassificationPanelProps = { projectPath: string; projectId: string; asset: GameCreationAppAssetManifestEntry; onClose: () => void; onSaved: (result: UpdateLocalProjectResourceClassificationResult) => void; + onDeleted: (result: DeleteLocalProjectAssetResult) => void; }; export function ResourceClassificationPanel({ @@ -50,6 +64,7 @@ export function ResourceClassificationPanel({ asset, onClose, onSaved, + onDeleted, }: ResourceClassificationPanelProps) { const [category, setCategory] = useState(() => gameCreationAppAssetCategory(asset), @@ -58,6 +73,8 @@ export function ResourceClassificationPanel({ gameCreationAppAssetTags(asset).join('、'), ); const [saving, setSaving] = useState(false); + const [deleting, setDeleting] = useState(false); + const [deleteArmed, setDeleteArmed] = useState(false); const [error, setError] = useState(null); async function saveResourceClassification() { @@ -98,6 +115,51 @@ export function ResourceClassificationPanel({ } } + /** + * 删除素材登记。素材不可变:只摘掉 manifest 登记,磁盘文件保留。 + * 第一次点击只进入确认态,避免误触这个不可逆操作。 + */ + async function deleteResourceClassificationAsset() { + if (!deleteArmed) { + setDeleteArmed(true); + setError(null); + return; + } + const invoke = window.__TAURI__?.core?.invoke; + if (!invoke) { + setError('删除资源需要在客户端内执行'); + return; + } + setDeleting(true); + setError(null); + try { + const status = await invoke<{ revision: number }>( + 'get_local_game_project_revision', + { projectPath }, + ); + if (!Number.isSafeInteger(status.revision) || status.revision < 0) { + throw new Error('项目 revision 无效'); + } + const result = await invoke( + 'delete_local_project_asset', + { + input: { + projectPath, + expectedProjectId: projectId, + expectedProjectRevision: status.revision, + assetId: asset.id, + }, + }, + ); + onDeleted(result); + } catch (deleteError) { + setDeleteArmed(false); + setError(resourceDeleteErrorMessage(deleteError)); + } finally { + setDeleting(false); + } + } + return (