| 1 | 'use client'; |
| 2 | |
| 3 | import React, { useState, useCallback } from 'react'; |
| 4 | import { CheckCircle, Edit3, Save, RefreshCw, Check, Loader2, Play } from 'lucide-react'; |
| 5 | import type { StageStatus } from './types'; |
| 6 | |
| 7 | // 需要"继续生成"的阶段(2、3、4、5) |
| 8 | const STAGES_WITH_CONTINUE = ['character_design', 'storyboard', 'reference_generation', 'video_generation']; |
| 9 | // 需要检查后续阶段的阶段(1) |
| 10 | const STAGES_WITH_NEXT_CHECK = ['script_generation']; |
| 11 | |
| 12 | interface StageActionsProps { |
| 13 | status: StageStatus; |
| 14 | onConfirm: () => void; |
| 15 | onEdit?: () => void; |
| 16 | onSave?: () => void; |
| 17 | onSaveSelections?: () => Promise<void>; |
| 18 | onRegenerate?: () => void; |
| 19 | /** 阶段ID */ |
| 20 | stageId?: string; |
| 21 | /** 是否有待生成的项(用于阶段2、3、4、5) */ |
| 22 | hasPendingItems?: boolean; |
| 23 | /** 后续阶段是否已开始(用于阶段1) */ |
| 24 | hasNextStageStarted?: boolean; |
| 25 | /** 是否显示"确认并继续"按钮(后续阶段已执行过时隐藏) */ |
| 26 | showConfirm?: boolean; |
| 27 | isRunning: boolean; |
| 28 | } |
| 29 | |
| 30 | export default function StageActions({ |
| 31 | status, |
| 32 | onConfirm, |
| 33 | onEdit, |
| 34 | onSave, |
| 35 | onSaveSelections, |
| 36 | onRegenerate, |
| 37 | stageId = '', |
| 38 | hasPendingItems = true, |
| 39 | hasNextStageStarted = false, |
| 40 | showConfirm = true, |
| 41 | isRunning |
| 42 | }: StageActionsProps) { |
| 43 | const [saveState, setSaveState] = useState<'idle' | 'saving' | 'saved'>('idle'); |
| 44 | |
| 45 | // 是否是"继续生成"阶段(2、3、4、5) |
| 46 | const isContinueStage = STAGES_WITH_CONTINUE.includes(stageId); |
| 47 | // 是否是"重新生成"阶段(1) |
| 48 | const isRegenStage = STAGES_WITH_NEXT_CHECK.includes(stageId); |
| 49 | |
| 50 | // 重新生成/继续生成按钮:在 != 'running' 状态下显示 (pending时也不会到这个组件) |
| 51 | const showRegen = !!onRegenerate && status !== 'running'; |
| 52 | |
| 53 | // 按钮是否禁用逻辑 |
| 54 | let isButtonDisabled = isRunning; |
| 55 | if (!isRunning && stageId) { |
| 56 | // 除了第一阶段和第六阶段外,completed 状态下禁用继续生成 |
| 57 | if (stageId !== 'script_generation' && stageId !== 'post_production' && status === 'completed') { |
| 58 | isButtonDisabled = true; |
| 59 | } |
| 60 | } |
| 61 | |
| 62 | // 其余按钮在 waiting、running、completed、stopped 状态显示 |
| 63 | const showActions = status === 'waiting' || status === 'running' || status === 'completed' || status === 'stopped'; |
| 64 | // 保存选项:在 waiting、completed 和 stopped 状态下都显示 |
| 65 | const showSaveSelections = onSaveSelections && (status === 'waiting' || status === 'completed' || status === 'stopped'); |
| 66 | // "确认并继续"在 showConfirm=true 且 waiting/running/completed 时显示(stopped 状态不显示确认) |
| 67 | const showConfirmBtn = showConfirm && (status === 'waiting' || status === 'running' || status === 'completed'); |
| 68 | |
| 69 | const handleSaveClick = useCallback(async () => { |
| 70 | if (!onSaveSelections || saveState !== 'idle') return; |
| 71 | setSaveState('saving'); |
| 72 | try { |
| 73 | await onSaveSelections(); |
| 74 | setSaveState('saved'); |
| 75 | setTimeout(() => setSaveState('idle'), 1500); |
| 76 | } catch { |
| 77 | setSaveState('idle'); |
| 78 | } |
| 79 | }, [onSaveSelections, saveState]); |
| 80 | |
| 81 | if (!showRegen && !showActions && !showSaveSelections) return null; |
| 82 | |
| 83 | return ( |
| 84 | <div className="border-t border-gray-200 bg-white px-4 sm:px-6 py-4 flex flex-wrap items-center justify-between gap-3 flex-shrink-0"> |
| 85 | {/* 左侧:重新生成/继续生成按钮 */} |
| 86 | <div> |
| 87 | {showRegen && ( |
| 88 | <button |
| 89 | onClick={onRegenerate} |
| 90 | disabled={isButtonDisabled} |
| 91 | className="flex items-center gap-2 px-4 py-2 bg-white border border-orange-300 text-orange-600 rounded-lg text-sm font-medium hover:bg-orange-50 transition-colors disabled:opacity-50 disabled:cursor-not-allowed" |
| 92 | title={isButtonDisabled ? (status === 'error' ? '重新尝试生成' : (isContinueStage && !hasPendingItems ? '所有项已生成完毕' : (isRegenStage && hasNextStageStarted ? '后续阶段已开始,无法重新生成' : ''))) : ''} |
| 93 | > |
| 94 | {isContinueStage ? <Play className="w-4 h-4" /> : <RefreshCw className="w-4 h-4" />} |
| 95 | {isContinueStage ? '继续生成' : '重新生成'} |
| 96 | </button> |
| 97 | )} |
| 98 | </div> |
| 99 | |
| 100 | {/* 右侧:操作按钮组 */} |
| 101 | <div className="flex flex-wrap items-center justify-end gap-3"> |
| 102 | {showActions && onEdit && ( |
| 103 | <button |
| 104 | onClick={onEdit} |
| 105 | disabled={isRunning} |
| 106 | className="flex items-center gap-2 px-4 py-2 bg-white border border-gray-200 text-gray-700 rounded-lg text-sm font-medium hover:bg-gray-50 transition-colors disabled:opacity-50" |
| 107 | > |
| 108 | <Edit3 className="w-4 h-4" /> |
| 109 | 修改 |
| 110 | </button> |
| 111 | )} |
| 112 | |
| 113 | {showActions && onSave && ( |
| 114 | <button |
| 115 | onClick={onSave} |
| 116 | disabled={isRunning} |
| 117 | className="flex items-center gap-2 px-4 py-2 bg-white border border-blue-300 text-blue-600 rounded-lg text-sm font-medium hover:bg-blue-50 transition-colors disabled:opacity-50" |
| 118 | > |
| 119 | <Save className="w-4 h-4" /> |
| 120 | 保存 |
| 121 | </button> |
| 122 | )} |
| 123 | |
| 124 | {showSaveSelections && ( |
| 125 | <button |
| 126 | onClick={handleSaveClick} |
| 127 | disabled={saveState !== 'idle' || isRunning} |
| 128 | className="flex items-center gap-2 px-4 py-2 bg-white border border-blue-600 text-blue-600 rounded-lg text-sm font-medium hover:bg-blue-50 transition-colors disabled:opacity-50 min-w-[120px] justify-center" |
| 129 | > |
| 130 | {saveState === 'saving' ? ( |
| 131 | <Loader2 className="w-4 h-4 animate-spin" /> |
| 132 | ) : saveState === 'saved' ? ( |
| 133 | <Check className="w-4 h-4 text-green-600" /> |
| 134 | ) : ( |
| 135 | <Save className="w-4 h-4" /> |
| 136 | )} |
| 137 | {saveState === 'saving' ? '正在保存...' : saveState === 'saved' ? '保存成功' : '保存选择'} |
| 138 | </button> |
| 139 | )} |
| 140 | |
| 141 | {showConfirmBtn && ( |
| 142 | <button |
| 143 | onClick={onConfirm} |
| 144 | disabled={isRunning} |
| 145 | className="flex items-center gap-2 px-6 py-2 bg-blue-600 text-white rounded-lg text-sm font-medium hover:bg-blue-700 transition-colors disabled:opacity-50 disabled:bg-blue-300" |
| 146 | > |
| 147 | <CheckCircle className="w-4 h-4" /> |
| 148 | 确认并继续 |
| 149 | </button> |
| 150 | )} |
| 151 | </div> |
| 152 | </div> |
| 153 | ); |
| 154 | } |
| 155 |