This commit is contained in:
beabigegg
2025-11-12 22:53:17 +08:00
commit da700721fa
130 changed files with 23393 additions and 0 deletions

View File

@@ -0,0 +1,140 @@
import { useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { useTranslation } from 'react-i18next'
import { useMutation } from '@tanstack/react-query'
import FileUpload from '@/components/FileUpload'
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 { apiClient } from '@/services/api'
export default function UploadPage() {
const { t } = useTranslation()
const navigate = useNavigate()
const { toast } = useToast()
const [selectedFiles, setSelectedFiles] = useState<File[]>([])
const { setBatchId, setFiles, setUploadProgress } = useUploadStore()
const uploadMutation = useMutation({
mutationFn: (files: File[]) => apiClient.uploadFiles(files),
onSuccess: (data) => {
setBatchId(data.batch_id)
setFiles(data.files)
toast({
title: t('upload.uploadSuccess'),
description: t('upload.fileCount', { count: data.files.length }),
variant: 'success',
})
navigate('/processing')
},
onError: (error: any) => {
toast({
title: t('upload.uploadError'),
description: error.response?.data?.detail || t('errors.networkError'),
variant: 'destructive',
})
},
})
const handleFilesSelected = (files: File[]) => {
setSelectedFiles((prev) => [...prev, ...files])
}
const handleRemoveFile = (index: number) => {
setSelectedFiles((prev) => prev.filter((_, i) => i !== index))
}
const handleClearAll = () => {
setSelectedFiles([])
setUploadProgress(0)
}
const handleUpload = () => {
if (selectedFiles.length === 0) {
toast({
title: t('errors.validationError'),
description: '請選擇至少一個檔案',
variant: 'destructive',
})
return
}
uploadMutation.mutate(selectedFiles)
}
const formatFileSize = (bytes: number) => {
if (bytes === 0) return '0 Bytes'
const k = 1024
const sizes = ['Bytes', 'KB', 'MB', 'GB']
const i = Math.floor(Math.log(bytes) / Math.log(k))
return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i]
}
return (
<div className="max-w-4xl mx-auto space-y-6">
<div>
<h1 className="text-3xl font-bold text-foreground mb-2">{t('upload.title')}</h1>
<p className="text-muted-foreground">
OCR PDF Office
</p>
</div>
<FileUpload
onFilesSelected={handleFilesSelected}
disabled={uploadMutation.isPending}
/>
{selectedFiles.length > 0 && (
<Card>
<CardHeader>
<div className="flex items-center justify-between">
<CardTitle className="text-lg">
{t('upload.selectedFiles')} ({selectedFiles.length})
</CardTitle>
<Button variant="outline" size="sm" onClick={handleClearAll}>
{t('upload.clearAll')}
</Button>
</div>
</CardHeader>
<CardContent>
<div className="space-y-2">
{selectedFiles.map((file, index) => (
<div
key={index}
className="flex items-center justify-between p-3 bg-muted rounded-md"
>
<div className="flex-1 min-w-0">
<p className="text-sm font-medium text-foreground truncate">{file.name}</p>
<p className="text-xs text-muted-foreground">{formatFileSize(file.size)}</p>
</div>
<Button
variant="ghost"
size="sm"
onClick={() => handleRemoveFile(index)}
disabled={uploadMutation.isPending}
>
{t('upload.removeFile')}
</Button>
</div>
))}
</div>
<div className="mt-6 flex justify-end gap-3">
<Button
variant="outline"
onClick={handleClearAll}
disabled={uploadMutation.isPending}
>
{t('common.cancel')}
</Button>
<Button onClick={handleUpload} disabled={uploadMutation.isPending}>
{uploadMutation.isPending ? t('upload.uploading') : t('upload.uploadButton')}
</Button>
</div>
</CardContent>
</Card>
)}
</div>
)
}