返回 VideoClaw
StageProgress.tsx
根目录 / video-claw / video-claw / frontend / components / stages / StageProgress.tsx
1 'use client';
2
3 import React from 'react';
4 import { Loader } from 'lucide-react';
5
6 interface StageProgressProps {
7 /** progressMessage from StageState */
8 message: string;
9 /** fallback text when message is empty */
10 fallback?: string;
11 /** 0-100 */
12 progress: number;
13 /** accent color class, default blue */
14 color?: 'blue' | 'amber' | 'emerald' | 'violet' | 'rose' | 'cyan';
15 }
16
17 const BAR_COLORS: Record<string, { bg: string; bar: string; text: string; ring: string }> = {
18 blue: { bg: 'bg-blue-100', bar: 'bg-gradient-to-r from-blue-400 to-blue-600', text: 'text-blue-600', ring: 'ring-blue-200' },
19 amber: { bg: 'bg-amber-100', bar: 'bg-gradient-to-r from-amber-400 to-amber-600', text: 'text-amber-600', ring: 'ring-amber-200' },
20 emerald: { bg: 'bg-emerald-100', bar: 'bg-gradient-to-r from-emerald-400 to-emerald-600', text: 'text-emerald-600', ring: 'ring-emerald-200' },
21 violet: { bg: 'bg-violet-100', bar: 'bg-gradient-to-r from-violet-400 to-violet-600', text: 'text-violet-600', ring: 'ring-violet-200' },
22 rose: { bg: 'bg-rose-100', bar: 'bg-gradient-to-r from-rose-400 to-rose-600', text: 'text-rose-600', ring: 'ring-rose-200' },
23 cyan: { bg: 'bg-cyan-100', bar: 'bg-gradient-to-r from-cyan-400 to-cyan-600', text: 'text-cyan-600', ring: 'ring-cyan-200' },
24 };
25
26 export default function StageProgress({
27 message,
28 fallback = '处理中...',
29 progress,
30 color = 'blue',
31 }: StageProgressProps) {
32 const p = Math.min(100, Math.max(0, Math.round(progress)));
33 const c = BAR_COLORS[color] || BAR_COLORS.blue;
34
35 // Extract step description: progressMessage format is "阶段名: 步骤描述"
36 const stepDesc = message?.includes(': ') ? message.split(': ').slice(1).join(': ') : (message || fallback);
37
38 return (
39 <div className="mb-6">
40 <div className="flex items-center justify-between mb-2">
41 <div className={`flex items-center gap-2 text-sm font-medium ${c.text}`}>
42 <Loader className="w-4 h-4 animate-spin" />
43 <span className="truncate">{stepDesc}</span>
44 </div>
45 <span className={`text-xs font-mono tabular-nums ${c.text} opacity-70`}>{p}%</span>
46 </div>
47 <div className={`w-full ${c.bg} rounded-full h-2.5 overflow-hidden ring-1 ${c.ring}`}>
48 <div
49 className={`${c.bar} h-2.5 rounded-full transition-all duration-700 ease-out`}
50 style={{ width: `${p}%` }}
51 />
52 </div>
53 </div>
54 );
55 }
56
56 lines Plain Text