fix: 批量工作流刷新效果修改

This commit is contained in:
npc0-hue
2026-03-27 15:25:21 +08:00
parent 8257113c50
commit 950ef2d13e
2 changed files with 94 additions and 27 deletions
@@ -180,12 +180,19 @@ const TextGeneration: FC<IMainProps> = ({
const batchJobsLimit = 5 // 每页5个任务
const [totalBatchJobs, setTotalBatchJobs] = useState(0)
const [isLoadingBatchJobs, setIsLoadingBatchJobs] = useState(false)
const lastRefreshTimeRef = useRef(0) // 记录上次刷新时间,避免频繁刷新
// 从后端获取批量工作流列表
const loadBatchWorkflows = useCallback(async () => {
const loadBatchWorkflows = useCallback(async (force = false) => {
if (!appId || currentTab !== 'batch')
return
// 防止过于频繁的刷新(至少间隔 1 秒)
const now = Date.now()
if (!force && now - lastRefreshTimeRef.current < 1000)
return
lastRefreshTimeRef.current = now
setIsLoadingBatchJobs(true)
try {
const result = await fetchBatchWorkflowListApi(installedAppInfo?.id, currentPage, batchJobsLimit)
@@ -218,25 +225,9 @@ const TextGeneration: FC<IMainProps> = ({
loadBatchWorkflows()
}, [loadBatchWorkflows])
// 自动刷新批量工作流列表(每3秒)
useEffect(() => {
if (currentTab !== 'batch' || batchJobs.length === 0)
return
// 注意:不再需要自动刷新逻辑,因为每个批量任务现在自己管理进度刷新
// 每个 BatchProgress 组件会独立轮询自己的进度(每 3 秒)
// 检查是否有进行中的任务
const hasActiveJobs = batchJobs.some(job =>
job.status === 'pending' || job.status === 'processing',
)
if (!hasActiveJobs)
return
const refreshInterval = setInterval(() => {
loadBatchWorkflows()
}, 3000) // 每3秒刷新一次
return () => clearInterval(refreshInterval)
}, [currentTab, batchJobs, loadBatchWorkflows])
// 计算分页数据 - 现在数据已经是从后端分页获取的,不需要再切片
const paginatedBatchJobs = batchJobs
@@ -472,6 +463,28 @@ const TextGeneration: FC<IMainProps> = ({
loadBatchWorkflows()
console.log('批量任务重试成功,已刷新列表')
}
// 处理单个任务进度更新(只更新列表中的对应项,不刷新整个列表)
const handleJobUpdate = useCallback((jobId: string, updatedData: { status: string, processedRows: number, error?: string }) => {
setBatchJobs(prevJobs => {
// 检查是否真的有变化
const job = prevJobs.find(j => j.id === jobId)
if (!job)
return prevJobs
// 如果没有变化,不更新
if (job.status === updatedData.status && job.processedRows === updatedData.processedRows && job.error === updatedData.error)
return prevJobs
// 有变化才更新
return prevJobs.map(job =>
job.id === jobId
? { ...job, ...updatedData }
: job
)
})
}, [])
// Extend: Stop Batch import
const handleCompleted = (completionRes: string, taskId?: number, isSuccess?: boolean) => {
@@ -662,6 +675,7 @@ const TextGeneration: FC<IMainProps> = ({
jobData={job}
onDownload={() => handleBatchDownload(job.id)}
onRetrySuccess={handleRetrySuccess}
onJobUpdate={(updatedData) => handleJobUpdate(job.id, updatedData)}
/>
))
) : (
@@ -11,7 +11,7 @@ import {
RiRefreshLine,
RiStopLine,
} from '@remixicon/react'
import { resumeBatchApi, retryFailedTasksApi, stopBatchApi } from '@/service/web-extend' // extend: 批量运行工单
import { fetchProgressApi, resumeBatchApi, retryFailedTasksApi, stopBatchApi } from '@/service/web-extend' // extend: 批量运行工单
import type { BatchStatus } from '@/utils/batch-progress-manager' // extend: 批量运行工单
import ActionButton from '@/app/components/base/action-button'
@@ -32,6 +32,7 @@ export type BatchProgressProps = {
}
onDownload: () => void
onRetrySuccess?: () => void
onJobUpdate?: (jobData: { status: string, processedRows: number, error?: string }) => void // 新增:任务更新回调
}
const BatchProgress: FC<BatchProgressProps> = ({
@@ -41,10 +42,62 @@ const BatchProgress: FC<BatchProgressProps> = ({
jobData,
onDownload,
onRetrySuccess,
onJobUpdate,
}) => {
const { t } = useTranslation()
const [isLoading, setIsLoading] = useState(false)
// 本地进度状态,用于独立刷新
const [localProgress, setLocalProgress] = useState({
status: jobData.status,
processedRows: jobData.processedRows,
totalRows: jobData.totalRows,
error: jobData.error,
})
// 自动刷新单个任务的进度(每 3 秒)
useEffect(() => {
// 只在任务进行中时刷新
if (localProgress.status !== 'pending' && localProgress.status !== 'processing')
return
const refreshInterval = setInterval(async () => {
try {
const progress = await fetchProgressApi(batchId)
if (progress) {
const newStatus = progress.status as string
const newProcessedRows = progress.processed_rows as number
const newError = progress.error as string | undefined
// 只有当数据有变化时才更新
if (
newStatus !== localProgress.status
|| newProcessedRows !== localProgress.processedRows
|| newError !== localProgress.error
) {
const updatedProgress = {
status: newStatus,
processedRows: newProcessedRows,
totalRows: progress.total_rows as number,
error: newError,
}
setLocalProgress(updatedProgress)
// 通知父组件数据已更新(用于列表级别的状态同步)
onJobUpdate?.({
status: newStatus,
processedRows: newProcessedRows,
error: newError,
})
}
}
}
catch (error) {
console.error('Failed to fetch progress:', error)
}
}, 3000)
return () => clearInterval(refreshInterval)
}, [batchId, localProgress.status, localProgress.processedRows, localProgress.error, onJobUpdate])
// 停止批量处理
const handleStop = async () => {
@@ -160,8 +213,8 @@ const BatchProgress: FC<BatchProgressProps> = ({
})
// 计算进度
const progress = jobData.totalRows > 0 ? (jobData.processedRows / jobData.totalRows) * 100 : 0
const status = jobData.status as BatchStatus
const progress = localProgress.totalRows > 0 ? (localProgress.processedRows / localProgress.totalRows) * 100 : 0
const status = localProgress.status as BatchStatus
const failed_count = 0 // 从列表API没有这个字段,如果需要可以后续添加
const getBorderColor = (status: BatchStatus) => {
@@ -232,18 +285,18 @@ const BatchProgress: FC<BatchProgressProps> = ({
</div>
{/* 详细进度信息 */}
{jobData.totalRows > 0 && (
{localProgress.totalRows > 0 && (
<div className="mt-2 text-xs text-gray-500">
{t('batchWorkflow.processed', {
processed: jobData.processedRows || 0,
total: jobData.totalRows || 0,
processed: localProgress.processedRows || 0,
total: localProgress.totalRows || 0,
ns: 'extend',
})}
</div>
)}
{/* 错误信息显示 */}
{jobData.error && status === 'failed' && (
{localProgress.error && status === 'failed' && (
<div className="mt-3 rounded-lg border border-red-200 bg-red-50 p-3">
<div className="flex items-start space-x-2">
<RiErrorWarningLine className="h-4 w-4 text-red-500 mt-0.5 flex-shrink-0" />
@@ -252,7 +305,7 @@ const BatchProgress: FC<BatchProgressProps> = ({
{t('batchWorkflow.errorOccurred', { ns: 'extend'} )}
</div>
<div className="text-xs text-red-700 break-words">
{jobData.error}
{localProgress.error}
</div>
</div>
</div>