feat: add batch processing for multiple file uploads

- Add BatchState management in taskStore with progress tracking
- Implement batch processing service with concurrency control
  - Direct Track: max 5 parallel tasks
  - OCR Track: sequential processing (GPU VRAM limit)
- Refactor ProcessingPage to support batch mode with BatchProcessingPanel
- Update UploadPage to initialize batch state for multi-file uploads
- Add i18n translations for batch processing (zh-TW, en-US)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
egg
2025-12-12 17:05:16 +08:00
parent d5bc311757
commit d20751d56b
11 changed files with 1469 additions and 5 deletions

View File

@@ -14,11 +14,30 @@ import PreprocessingSettings from '@/components/PreprocessingSettings'
import PreprocessingPreview from '@/components/PreprocessingPreview'
import ProcessingTrackSelector from '@/components/ProcessingTrackSelector'
import TaskNotFound from '@/components/TaskNotFound'
import BatchProcessingPanel from '@/components/BatchProcessingPanel'
import { useTaskValidation } from '@/hooks/useTaskValidation'
import { useTaskStore, useProcessingState } from '@/store/taskStore'
import { useTaskStore, useProcessingState, useIsBatchMode } from '@/store/taskStore'
import type { LayoutModel, ProcessingOptions, PreprocessingMode, PreprocessingConfig, ProcessingTrack } from '@/types/apiV2'
/**
* ProcessingPage - Main entry point
* Routes to batch or single task processing based on state
*/
export default function ProcessingPage() {
const isBatchMode = useIsBatchMode()
// Route to appropriate component
if (isBatchMode) {
return <BatchProcessingPanel />
}
return <SingleTaskProcessing />
}
/**
* SingleTaskProcessing - Original single task processing UI
*/
function SingleTaskProcessing() {
const { t } = useTranslation()
const navigate = useNavigate()
const { toast } = useToast()

View File

@@ -7,6 +7,7 @@ import { Button } from '@/components/ui/button'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { useToast } from '@/components/ui/toast'
import { useUploadStore } from '@/store/uploadStore'
import { useTaskStore } from '@/store/taskStore'
import { apiClientV2 } from '@/services/apiV2'
import { FileText, X, Upload, Trash2, CheckCircle2, ArrowRight } from 'lucide-react'
@@ -16,6 +17,8 @@ export default function UploadPage() {
const { toast } = useToast()
const [selectedFiles, setSelectedFiles] = useState<File[]>([])
const { setBatchId, setUploadProgress } = useUploadStore()
const initBatch = useTaskStore((state) => state.initBatch)
const setCurrentTask = useTaskStore((state) => state.setCurrentTask)
const uploadMutation = useMutation({
mutationFn: async (files: File[]) => {
@@ -33,9 +36,22 @@ export default function UploadPage() {
if (tasks.length > 0) {
setBatchId(tasks[0].task_id as unknown as number)
}
// Initialize batch state with all uploaded tasks
if (tasks.length > 1) {
// Multiple files: use batch mode
initBatch(tasks.map(t => ({
taskId: t.task_id,
filename: t.filename,
})))
} else if (tasks.length === 1) {
// Single file: use single task mode
setCurrentTask(tasks[0].task_id, tasks[0].filename)
}
toast({
title: t('upload.uploadSuccess'),
description: `成功上傳 ${tasks.length} 個檔案`,
description: t('upload.filesUploaded', { count: tasks.length }),
variant: 'success',
})
navigate('/processing')