feat: 添加素材删除和重新处理功能

- 在MaterialCard组件中添加删除按钮,使用DeleteConfirmDialog替代window.confirm
- 在MaterialCard组件中添加重新处理按钮,当素材状态为Pending时显示
- 在ProjectDetails页面中添加删除和重新处理的处理函数
- 遵循Tauri开发规范和前端开发规范
- 实现了统一的删除确认对话框UI交互
This commit is contained in:
imeepos 2025-07-15 13:12:01 +08:00
parent 3f90013a47
commit 9aa2cdd49f
2 changed files with 122 additions and 3 deletions

View File

@ -1,17 +1,20 @@
import React, { useState, useEffect } from 'react';
import {
FileVideo, FileAudio, FileImage, File, Clock, ExternalLink, ChevronDown, ChevronUp,
Monitor, Volume2, Palette, Calendar, Hash, Zap, HardDrive, Film, Eye, Brain, Loader2, User, Edit2
Monitor, Volume2, Palette, Calendar, Hash, Zap, HardDrive, Film, Eye, Brain, Loader2, User, Edit2, Trash2, RefreshCw
} from 'lucide-react';
import { Material, MaterialSegment } from '../types/material';
import { useMaterialStore } from '../store/materialStore';
import { useVideoClassificationStore } from '../store/videoClassificationStore';
import { Model } from '../types/model';
import { invoke } from '@tauri-apps/api/core';
import { DeleteConfirmDialog } from './DeleteConfirmDialog';
interface MaterialCardProps {
material: Material;
onEdit?: (material: Material) => void;
onDelete?: (materialId: string, materialName: string) => void;
onReprocess?: (materialId: string) => void;
}
// 格式化时间(秒转为 mm:ss 格式)
@ -60,7 +63,7 @@ const formatDate = (dateString: string): string => {
*
*
*/
export const MaterialCard: React.FC<MaterialCardProps> = ({ material, onEdit }) => {
export const MaterialCard: React.FC<MaterialCardProps> = ({ material, onEdit, onDelete, onReprocess }) => {
const { getMaterialSegments } = useMaterialStore();
const { startClassification, isLoading: classificationLoading } = useVideoClassificationStore();
const [segments, setSegments] = useState<MaterialSegment[]>([]);
@ -69,6 +72,9 @@ export const MaterialCard: React.FC<MaterialCardProps> = ({ material, onEdit })
const [isClassifying, setIsClassifying] = useState(false);
const [associatedModel, setAssociatedModel] = useState<Model | null>(null);
const [loadingModel, setLoadingModel] = useState(false);
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
const [isDeleting, setIsDeleting] = useState(false);
const [isReprocessing, setIsReprocessing] = useState(false);
// 获取素材类型图标
const getTypeIcon = (type: string) => {
@ -209,6 +215,43 @@ export const MaterialCard: React.FC<MaterialCardProps> = ({ material, onEdit })
}
};
// 处理删除素材
const handleDeleteClick = () => {
setShowDeleteConfirm(true);
};
const handleDeleteConfirm = async () => {
if (!onDelete) return;
setIsDeleting(true);
try {
await onDelete(material.id, material.name);
setShowDeleteConfirm(false);
} catch (error) {
console.error('删除素材失败:', error);
} finally {
setIsDeleting(false);
}
};
const handleDeleteCancel = () => {
setShowDeleteConfirm(false);
};
// 处理重新处理素材
const handleReprocessClick = async () => {
if (!onReprocess) return;
setIsReprocessing(true);
try {
await onReprocess(material.id);
} catch (error) {
console.error('重新处理素材失败:', error);
} finally {
setIsReprocessing(false);
}
};
return (
<div className="border border-gray-200 rounded-lg p-4 hover:shadow-md transition-shadow">
{/* 素材基本信息 */}
@ -218,6 +261,22 @@ export const MaterialCard: React.FC<MaterialCardProps> = ({ material, onEdit })
<h4 className="font-medium text-gray-900 truncate">{material.name}</h4>
</div>
<div className="flex items-center space-x-2">
{/* 重新处理按钮 - 仅在状态为 Pending 时显示 */}
{material.processing_status === 'Pending' && onReprocess && (
<button
onClick={handleReprocessClick}
disabled={isReprocessing}
className="text-gray-400 hover:text-green-500 transition-colors disabled:opacity-50"
title="重新处理"
>
{isReprocessing ? (
<Loader2 className="w-4 h-4 animate-spin" />
) : (
<RefreshCw className="w-4 h-4" />
)}
</button>
)}
{onEdit && (
<button
onClick={() => onEdit(material)}
@ -227,6 +286,17 @@ export const MaterialCard: React.FC<MaterialCardProps> = ({ material, onEdit })
<Edit2 className="w-4 h-4" />
</button>
)}
{onDelete && (
<button
onClick={handleDeleteClick}
className="text-gray-400 hover:text-red-500 transition-colors"
title="删除素材"
>
<Trash2 className="w-4 h-4" />
</button>
)}
<span className={`px-2 py-1 text-xs rounded-full ${getStatusColor(material.processing_status)}`}>
{material.processing_status}
</span>
@ -471,6 +541,17 @@ export const MaterialCard: React.FC<MaterialCardProps> = ({ material, onEdit })
)}
</div>
)}
{/* 删除确认对话框 */}
<DeleteConfirmDialog
isOpen={showDeleteConfirm}
title="删除素材"
message="确定要删除这个素材吗?此操作不可撤销。"
itemName={material.name}
deleting={isDeleting}
onConfirm={handleDeleteConfirm}
onCancel={handleDeleteCancel}
/>
</div>
);
};

View File

@ -39,6 +39,8 @@ export const ProjectDetails: React.FC = () => {
stats,
loadMaterials,
loadMaterialStats,
deleteMaterial,
processMaterials,
isLoading: materialsLoading
} = useMaterialStore();
const {
@ -287,6 +289,36 @@ export const ProjectDetails: React.FC = () => {
}
};
// 素材删除处理函数
const handleDeleteMaterial = async (materialId: string, _materialName: string) => {
try {
await deleteMaterial(materialId);
// 重新加载素材列表
if (project) {
loadMaterials(project.id);
}
} catch (error) {
console.error('删除素材失败:', error);
throw error;
}
};
// 素材重新处理函数
const handleReprocessMaterial = async (materialId: string) => {
try {
await processMaterials([materialId]);
// 重新加载素材列表
if (project) {
loadMaterials(project.id);
}
} catch (error) {
console.error('重新处理素材失败:', error);
throw error;
}
};
if (isLoading) {
return (
<div className="flex items-center justify-center min-h-[400px]">
@ -527,7 +559,13 @@ export const ProjectDetails: React.FC = () => {
) : materials.length > 0 ? (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 gap-3 md:gap-4">
{materials.map((material) => (
<MaterialCard key={material.id} material={material} onEdit={handleEditMaterial} />
<MaterialCard
key={material.id}
material={material}
onEdit={handleEditMaterial}
onDelete={handleDeleteMaterial}
onReprocess={handleReprocessMaterial}
/>
))}
</div>
) : (