feat: add confirmation dialog for chapter actions in ChaptersPanel

This commit is contained in:
2026-03-13 17:13:59 +08:00
parent a6974db528
commit e024910411

View File

@@ -1445,6 +1445,7 @@ function ChaptersPanel({
const [regeneratingSegs, setRegeneratingSegs] = useState<Set<number>>(new Set()) const [regeneratingSegs, setRegeneratingSegs] = useState<Set<number>>(new Set())
const [audioVersions, setAudioVersions] = useState<Record<number, number>>({}) const [audioVersions, setAudioVersions] = useState<Record<number, number>>({})
const [chapterPlayerChIdx, setChapterPlayerChIdx] = useState<number | null>(null) const [chapterPlayerChIdx, setChapterPlayerChIdx] = useState<number | null>(null)
const [confirmAction, setConfirmAction] = useState<{ label: string; onConfirm: () => void } | null>(null)
const prevSegStatusRef = useRef<Record<number, string>>({}) const prevSegStatusRef = useRef<Record<number, string>>({})
const initialExpandDoneRef = useRef(false) const initialExpandDoneRef = useRef(false)
const segRefs = useRef<Record<number, HTMLDivElement | null>>({}) const segRefs = useRef<Record<number, HTMLDivElement | null>>({})
@@ -1577,6 +1578,19 @@ function ChaptersPanel({
const hasChapters = detail && detail.chapters.length > 0 && ['analyzing', 'ready', 'generating', 'done'].includes(status) const hasChapters = detail && detail.chapters.length > 0 && ['analyzing', 'ready', 'generating', 'done'].includes(status)
return ( return (
<>
<Dialog open={!!confirmAction} onOpenChange={open => { if (!open) setConfirmAction(null) }}>
<DialogContent className="max-w-xs">
<DialogHeader>
<DialogTitle></DialogTitle>
</DialogHeader>
<p className="text-sm text-muted-foreground">{confirmAction?.label}</p>
<div className="flex justify-end gap-2 mt-2">
<Button variant="ghost" size="sm" onClick={() => setConfirmAction(null)}></Button>
<Button variant="destructive" size="sm" onClick={() => { confirmAction?.onConfirm(); setConfirmAction(null) }}></Button>
</div>
</DialogContent>
</Dialog>
<div className="flex-1 flex flex-col bg-emerald-500/5 overflow-hidden"> <div className="flex-1 flex flex-col bg-emerald-500/5 overflow-hidden">
<div className="flex flex-col gap-1 sm:flex-row sm:items-center sm:justify-between px-3 py-2 shrink-0"> <div className="flex flex-col gap-1 sm:flex-row sm:items-center sm:justify-between px-3 py-2 shrink-0">
<span className="text-xs font-medium text-emerald-400/80"> <span className="text-xs font-medium text-emerald-400/80">
@@ -1668,7 +1682,7 @@ function ChaptersPanel({
<Volume2 className="h-3 w-3 mr-1" /> <Volume2 className="h-3 w-3 mr-1" />
{t('projectCard.chapters.generate')} {t('projectCard.chapters.generate')}
</Button> </Button>
<Button size="xs" variant="ghost" className="text-muted-foreground" disabled={loadingAction} onClick={() => onParseChapter(ch.id, ch.title)}> <Button size="xs" variant="ghost" className="text-muted-foreground" disabled={loadingAction} onClick={() => setConfirmAction({ label: '重新解析将删除已有台词,确认继续?', onConfirm: () => onParseChapter(ch.id, ch.title) })}>
{isAIMode ? <Bot className="h-3 w-3 mr-1" /> : <Wand2 className="h-3 w-3 mr-1" />} {isAIMode ? <Bot className="h-3 w-3 mr-1" /> : <Wand2 className="h-3 w-3 mr-1" />}
{t(isAIMode ? 'projectCard.chapters.reparseAI' : 'projectCard.chapters.reparse')} {t(isAIMode ? 'projectCard.chapters.reparseAI' : 'projectCard.chapters.reparse')}
</Button> </Button>
@@ -1682,10 +1696,7 @@ function ChaptersPanel({
{(ch.status === 'done' || (ch.status === 'ready' && chAllDone)) && ( {(ch.status === 'done' || (ch.status === 'ready' && chAllDone)) && (
<> <>
<span className="text-[11px] text-muted-foreground">{t('projectCard.chapters.doneBadge', { count: chDone })}</span> <span className="text-[11px] text-muted-foreground">{t('projectCard.chapters.doneBadge', { count: chDone })}</span>
<Button size="xs" variant="ghost" className="text-muted-foreground" disabled={loadingAction} onClick={() => { <Button size="xs" variant="ghost" className="text-muted-foreground" disabled={loadingAction} onClick={() => setConfirmAction({ label: '重新生成将覆盖已有音频,确认继续?', onConfirm: () => { setExpandedChapters(prev => { const n = new Set(prev); n.add(ch.id); return n }); onGenerate(ch.chapter_index, true) } })}>
setExpandedChapters(prev => { const n = new Set(prev); n.add(ch.id); return n })
onGenerate(ch.chapter_index, true)
}}>
<RefreshCw className="h-3 w-3 mr-0.5" /><Volume2 className="h-3 w-3 mr-1" />{t('projectCard.chapters.generate')} <RefreshCw className="h-3 w-3 mr-0.5" /><Volume2 className="h-3 w-3 mr-1" />{t('projectCard.chapters.generate')}
</Button> </Button>
<Button <Button
@@ -1700,6 +1711,10 @@ function ChaptersPanel({
<Button size="icon" variant="ghost" className="h-6 w-6" onClick={() => onDownload(ch.chapter_index)} title={t('projectCard.downloadAll')}> <Button size="icon" variant="ghost" className="h-6 w-6" onClick={() => onDownload(ch.chapter_index)} title={t('projectCard.downloadAll')}>
<Download className="h-3 w-3" /> <Download className="h-3 w-3" />
</Button> </Button>
<Button size="xs" variant="ghost" className="text-muted-foreground" disabled={loadingAction} onClick={() => setConfirmAction({ label: '重新解析将删除已有台词和音频,确认继续?', onConfirm: () => onParseChapter(ch.id, ch.title) })}>
{isAIMode ? <Bot className="h-3 w-3 mr-1" /> : <Wand2 className="h-3 w-3 mr-1" />}
{t(isAIMode ? 'projectCard.chapters.reparseAI' : 'projectCard.chapters.reparse')}
</Button>
</> </>
)} )}
{ch.status === 'error' && ( {ch.status === 'error' && (
@@ -1868,6 +1883,7 @@ function ChaptersPanel({
})()} })()}
</div> </div>
</>
) )
} }