From 950ef2d13e6da6c849075d9e8cbbecd9ca042984 Mon Sep 17 00:00:00 2001 From: npc0-hue Date: Fri, 27 Mar 2026 15:25:21 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=89=B9=E9=87=8F=E5=B7=A5=E4=BD=9C?= =?UTF-8?q?=E6=B5=81=E5=88=B7=E6=96=B0=E6=95=88=E6=9E=9C=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../share/text-generation/index.tsx | 52 +++++++++----- .../run-batch/batch-progress/index.tsx | 69 ++++++++++++++++--- 2 files changed, 94 insertions(+), 27 deletions(-) diff --git a/web/app/components/share/text-generation/index.tsx b/web/app/components/share/text-generation/index.tsx index 78fbb4422..a20f090c3 100644 --- a/web/app/components/share/text-generation/index.tsx +++ b/web/app/components/share/text-generation/index.tsx @@ -180,12 +180,19 @@ const TextGeneration: FC = ({ 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 = ({ 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 = ({ 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 = ({ jobData={job} onDownload={() => handleBatchDownload(job.id)} onRetrySuccess={handleRetrySuccess} + onJobUpdate={(updatedData) => handleJobUpdate(job.id, updatedData)} /> )) ) : ( diff --git a/web/app/components/share/text-generation/run-batch/batch-progress/index.tsx b/web/app/components/share/text-generation/run-batch/batch-progress/index.tsx index 6e53cb6c7..ea9d61163 100644 --- a/web/app/components/share/text-generation/run-batch/batch-progress/index.tsx +++ b/web/app/components/share/text-generation/run-batch/batch-progress/index.tsx @@ -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 = ({ @@ -41,10 +42,62 @@ const BatchProgress: FC = ({ 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 = ({ }) // 计算进度 - 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 = ({ {/* 详细进度信息 */} - {jobData.totalRows > 0 && ( + {localProgress.totalRows > 0 && (
{t('batchWorkflow.processed', { - processed: jobData.processedRows || 0, - total: jobData.totalRows || 0, + processed: localProgress.processedRows || 0, + total: localProgress.totalRows || 0, ns: 'extend', })}
)} {/* 错误信息显示 */} - {jobData.error && status === 'failed' && ( + {localProgress.error && status === 'failed' && (
@@ -252,7 +305,7 @@ const BatchProgress: FC = ({ {t('batchWorkflow.errorOccurred', { ns: 'extend'} )}
- {jobData.error} + {localProgress.error}