Files
mixvideo-v2/apps/desktop/src/components/MaterialSegmentDeleteDialog.tsx
imeepos 590e254fe1 feat: 添加项目详情/素材管理的MaterialSegment聚合视图功能
- 新增MaterialSegment聚合视图,支持按AI分类和模特聚合展示
- 实现后端MaterialSegmentViewService和相关API命令
- 创建前端React组件:MaterialSegmentView、MaterialSegmentGroup、MaterialSegmentCard等
- 添加MaterialSegment详细信息模态框和批量操作对话框
- 实现搜索、筛选、排序、分页功能
- 集成虚拟滚动和性能优化
- 在ProjectDetails页面添加片段管理选项卡
- 遵循promptx开发规范和UI/UX设计标准
2025-07-15 16:49:08 +08:00

194 lines
8.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React from 'react';
import { AlertTriangle, X, Trash2 } from 'lucide-react';
import { SegmentWithDetails } from '../types/materialSegmentView';
interface MaterialSegmentDeleteDialogProps {
segments: SegmentWithDetails[];
isOpen: boolean;
onClose: () => void;
onConfirm: () => void;
isLoading?: boolean;
}
/**
* MaterialSegment删除确认对话框组件
* 遵循 Tauri 开发规范的组件设计模式
*/
export const MaterialSegmentDeleteDialog: React.FC<MaterialSegmentDeleteDialogProps> = ({
segments,
isOpen,
onClose,
onConfirm,
isLoading = false,
}) => {
if (!isOpen) return null;
const isBatch = segments.length > 1;
// 格式化时长
const formatDuration = (seconds: number) => {
const minutes = Math.floor(seconds / 60);
const secs = Math.round(seconds % 60);
return `${minutes}:${secs.toString().padStart(2, '0')}`;
};
// 计算总时长
const totalDuration = segments.reduce((sum, segment) => sum + segment.segment.duration, 0);
return (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4">
<div className="bg-white rounded-lg shadow-xl max-w-md w-full">
{/* 对话框头部 */}
<div className="flex items-center justify-between p-6 border-b border-gray-200">
<div className="flex items-center space-x-3">
<div className="w-10 h-10 bg-red-100 rounded-full flex items-center justify-center">
<AlertTriangle className="w-5 h-5 text-red-600" />
</div>
<div>
<h3 className="text-lg font-semibold text-gray-900">
{isBatch ? '批量删除片段' : '删除片段'}
</h3>
<p className="text-sm text-gray-600">
{isBatch ? `确认删除 ${segments.length} 个片段` : '确认删除该片段'}
</p>
</div>
</div>
<button
onClick={onClose}
disabled={isLoading}
className="p-2 text-gray-400 hover:text-gray-600 hover:bg-gray-100 rounded-lg transition-colors disabled:opacity-50"
>
<X className="w-5 h-5" />
</button>
</div>
{/* 对话框内容 */}
<div className="p-6">
<div className="mb-4">
<p className="text-gray-700 mb-3">
{isBatch
? '您即将删除以下片段,此操作不可撤销:'
: '您即将删除该片段,此操作不可撤销:'
}
</p>
{/* 片段列表 */}
<div className="bg-gray-50 rounded-lg p-4 max-h-48 overflow-y-auto">
{isBatch ? (
<div className="space-y-2">
{segments.slice(0, 5).map((segmentWithDetails, _index) => (
<div key={segmentWithDetails.segment.id} className="flex items-center justify-between text-sm">
<span className="text-gray-900 truncate flex-1 mr-2">
{segmentWithDetails.material_name}
</span>
<div className="flex items-center space-x-2 text-gray-600">
<span>#{segmentWithDetails.segment.segment_index}</span>
<span>{formatDuration(segmentWithDetails.segment.duration)}</span>
</div>
</div>
))}
{segments.length > 5 && (
<div className="text-sm text-gray-500 text-center pt-2 border-t border-gray-200">
{segments.length - 5} ...
</div>
)}
</div>
) : (
<div className="space-y-2">
<div className="flex items-center justify-between">
<span className="text-sm text-gray-600"></span>
<span className="text-sm text-gray-900">{segments[0].material_name}</span>
</div>
<div className="flex items-center justify-between">
<span className="text-sm text-gray-600"></span>
<span className="text-sm text-gray-900">#{segments[0].segment.segment_index}</span>
</div>
<div className="flex items-center justify-between">
<span className="text-sm text-gray-600"></span>
<span className="text-sm text-gray-900">{formatDuration(segments[0].segment.duration)}</span>
</div>
{segments[0].classification && (
<div className="flex items-center justify-between">
<span className="text-sm text-gray-600"></span>
<span className="text-sm text-gray-900">{segments[0].classification.category}</span>
</div>
)}
</div>
)}
</div>
{/* 统计信息 */}
{isBatch && (
<div className="mt-4 p-3 bg-red-50 rounded-lg">
<div className="flex items-center justify-between text-sm">
<span className="text-red-700 font-medium"></span>
</div>
<div className="mt-2 space-y-1 text-sm text-red-600">
<div className="flex items-center justify-between">
<span></span>
<span className="font-medium">{segments.length} </span>
</div>
<div className="flex items-center justify-between">
<span></span>
<span className="font-medium">{formatDuration(totalDuration)}</span>
</div>
<div className="flex items-center justify-between">
<span></span>
<span className="font-medium">
{segments.filter(s => s.classification).length}
</span>
</div>
</div>
</div>
)}
</div>
{/* 警告信息 */}
<div className="bg-yellow-50 border border-yellow-200 rounded-lg p-3 mb-4">
<div className="flex items-start">
<AlertTriangle className="w-4 h-4 text-yellow-600 mt-0.5 mr-2 flex-shrink-0" />
<div className="text-sm text-yellow-800">
<p className="font-medium mb-1"></p>
<ul className="list-disc list-inside space-y-1 text-xs">
<li></li>
<li></li>
<li>AI分类数据也将被删除</li>
{isBatch && <li></li>}
</ul>
</div>
</div>
</div>
</div>
{/* 对话框底部 */}
<div className="flex items-center justify-end space-x-3 p-6 border-t border-gray-200 bg-gray-50">
<button
onClick={onClose}
disabled={isLoading}
className="px-4 py-2 text-gray-600 hover:text-gray-800 transition-colors disabled:opacity-50"
>
</button>
<button
onClick={onConfirm}
disabled={isLoading}
className="inline-flex items-center px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
>
{isLoading ? (
<>
<div className="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin mr-2" />
...
</>
) : (
<>
<Trash2 className="w-4 h-4 mr-2" />
{isBatch ? `删除 ${segments.length} 个片段` : '删除片段'}
</>
)}
</button>
</div>
</div>
</div>
);
};