// dashboard/src/components/sync/BatchPipelineCard.tsx import React from 'react'; import { Download, AlertTriangle, Play, StopCircle, Loader, FileText, RefreshCw, SlidersHorizontal, } from 'lucide-react'; import { CustomSelect } from '../CustomSelect'; import type { BatchStatus } from '../../hooks/useSyncState'; interface BatchPipelineCardProps { targetPhase: 'download' | 'parse' | 'translate' | 'embed' | 'target'; setTargetPhase: ( p: 'download' | 'parse' | 'translate' | 'embed' | 'target' ) => void; batchLimitCount: number; setBatchLimitCount: (c: number) => void; sortOrder: 'default' | 'pub_year_desc' | 'created_at_desc'; setSortOrder: (o: 'default' | 'pub_year_desc' | 'created_at_desc') => void; skipCompleted: boolean; setSkipCompleted: (s: boolean) => void; skipFailed: boolean; setSkipFailed: (s: boolean) => void; skipPrecedingFailed: boolean; setSkipPrecedingFailed: (s: boolean) => void; skipPrecedingUncompleted: boolean; setSkipPrecedingUncompleted: (s: boolean) => void; batchStatus: BatchStatus; batchError: string | null; logsContainerRef: React.RefObject; handleStartBatch: () => Promise; handleStopBatch: () => Promise; } export function BatchPipelineCard({ targetPhase, setTargetPhase, batchLimitCount, setBatchLimitCount, sortOrder, setSortOrder, skipCompleted, setSkipCompleted, skipFailed, setSkipFailed, skipPrecedingFailed, setSkipPrecedingFailed, skipPrecedingUncompleted, setSkipPrecedingUncompleted, batchStatus, batchError, logsContainerRef, handleStartBatch, handleStopBatch, }: BatchPipelineCardProps) { return (

馆藏文献批量学术流水线任务

针对本地馆藏中的文献,批量发起正文 PDF/HTML 下载、Markdown 结构化排版解析以及中英双语对照翻译。

{batchError && (
{batchError}
)} {/* 条件设置 */}
setTargetPhase( val as 'download' | 'parse' | 'translate' | 'embed' | 'target' ) } className="w-full" options={[ { value: 'download', label: '下载' }, { value: 'parse', label: '解析' }, { value: 'translate', label: '翻译' }, { value: 'embed', label: '向量化' }, { value: 'target', label: '天体识别' }, ]} />
setBatchLimitCount(Math.max(1, parseInt(e.target.value) || 0)) } className="w-full px-3 py-2 rounded-md bg-sunken text-content focus:outline-none focus:border-blueprint transition-all text-xs font-medium" />
setSortOrder( val as 'default' | 'pub_year_desc' | 'created_at_desc' ) } className="w-full" options={[ { value: 'default', label: '默认(不指定)' }, { value: 'pub_year_desc', label: '按出版年份降序' }, { value: 'created_at_desc', label: '按入库时间降序' }, ]} />
{/* 执行策略 */}
{/* 控制按钮 */}
{batchStatus.active ? ( ) : ( )}
{/* 进度与终端日志展示 */} {(batchStatus.active || batchStatus.total > 0) && (
{batchStatus.action === 'download' && ( )} {batchStatus.action === 'parse' && ( )} {batchStatus.action === 'translate' && ( )} {batchStatus.action === 'embed' && ( )} {batchStatus.action === 'target' && ( )} {batchStatus.action === 'download' ? '正文离线下载进度' : batchStatus.action === 'parse' ? '结构化排版解析进度' : batchStatus.action === 'translate' ? '中英双语对照翻译进度' : batchStatus.action === 'embed' ? '段落向量分块入库进度' : '天体识别与缓存进度'} {batchStatus.action === 'download' ? batchStatus.downloaded : batchStatus.parsed}{' '} / {batchStatus.total} 篇 {((batchStatus.action === 'download' && batchStatus.download_failed > 0) || (batchStatus.action !== 'download' && batchStatus.parse_failed > 0)) && ( (失败{' '} {batchStatus.action === 'download' ? batchStatus.download_failed : batchStatus.parse_failed}{' '} 篇) )}
0 ? Math.min( 100, Math.round( ((batchStatus.action === 'download' ? batchStatus.downloaded + batchStatus.download_failed : batchStatus.parsed + batchStatus.parse_failed) / batchStatus.total) * 100 ) ) : 0 }%`, }} />
{batchStatus.active && batchStatus.current_bibcode && (
当前正在处理:{' '} {batchStatus.current_bibcode}
)} {/* 滚动日志终端 */}
{batchStatus.logs.length === 0 ? (
等待数据流任务启动,暂无日志输出...
) : ( batchStatus.logs.map((log: string, idx: number) => (
{log}
)) )}
)}
); }