diff --git a/backend/api/v1/documents/serializers.py b/backend/api/v1/documents/serializers.py
index 5bd2ece9..d6d3b7ea 100644
--- a/backend/api/v1/documents/serializers.py
+++ b/backend/api/v1/documents/serializers.py
@@ -278,6 +278,7 @@ class DocumentWriteSerializer(serializers.ModelSerializer):
"title",
"description",
"file",
+ "extracted_text",
"sent_to_all",
"department_ids",
"recipient_ids",
diff --git a/frontend/src/components/documents/DocumentUploadForm.tsx b/frontend/src/components/documents/DocumentUploadForm.tsx
index 301b7689..9f17b29c 100644
--- a/frontend/src/components/documents/DocumentUploadForm.tsx
+++ b/frontend/src/components/documents/DocumentUploadForm.tsx
@@ -2,28 +2,86 @@
import { useState, useCallback } from "react";
import { useDropzone } from "react-dropzone";
-import { Upload, X, FileText, AlertCircle } from "lucide-react";
+import { Upload, X, FileText, AlertCircle, Loader2 } from "lucide-react";
import { apiClient } from "@/lib/api";
import { toast } from "sonner";
+import { processDocument, needsProcessing, type ProcessingProgress } from "@/lib/document-utils";
interface DocumentUploadFormProps {
onSuccess?: () => void;
onCancel?: () => void;
}
+// Сообщения для различных этапов обработки
+const STAGE_MESSAGES: Record = {
+ compressing: "Сжатие изображения...",
+ ocr: "Распознавание текста (OCR)...",
+ extracting_text: "Извлечение текста...",
+ generating_thumbnail: "Создание миниатюры...",
+ complete: "Обработка завершена",
+};
+
export function DocumentUploadForm({ onSuccess, onCancel }: DocumentUploadFormProps) {
const [title, setTitle] = useState("");
const [description, setDescription] = useState("");
const [file, setFile] = useState(null);
+ const [processedFile, setProcessedFile] = useState(null);
+ const [extractedText, setExtractedText] = useState("");
+ const [isProcessing, setIsProcessing] = useState(false);
+ const [processingProgress, setProcessingProgress] = useState(null);
const [isSubmitting, setIsSubmitting] = useState(false);
const [error, setError] = useState(null);
- const onDrop = useCallback((acceptedFiles: File[]) => {
+ const onDrop = useCallback(async (acceptedFiles: File[]) => {
if (acceptedFiles.length > 0) {
- setFile(acceptedFiles[0]);
+ const selectedFile = acceptedFiles[0];
+ setFile(selectedFile);
+ setError(null);
+
// Автоматически заполняем название, если оно пустое
- if (!title && acceptedFiles[0].name) {
- setTitle(acceptedFiles[0].name.replace(/\.[^/.]+$/, "")); // Убираем расширение
+ if (!title && selectedFile.name) {
+ setTitle(selectedFile.name.replace(/\.[^/.]+$/, "")); // Убираем расширение
+ }
+
+ // Проверяем, требуется ли обработка файла
+ if (needsProcessing(selectedFile)) {
+ setIsProcessing(true);
+ setProcessingProgress({
+ stage: "compressing",
+ progress: 0,
+ message: "Начало обработки..."
+ });
+
+ try {
+ const result = await processDocument(selectedFile, {
+ enableOCR: true,
+ enableCompression: true,
+ enableTextExtraction: true,
+ enableThumbnail: true,
+ onProgress: (progress) => {
+ setProcessingProgress(progress);
+ },
+ });
+
+ // Сохраняем извлеченный текст
+ if (result.extractedText) {
+ setExtractedText(result.extractedText);
+ }
+
+ // Если файл был обработан (сжат), используем обработанную версию
+ if (result.processedFile) {
+ setProcessedFile(result.processedFile);
+ }
+
+ toast.success("Документ обработан успешно");
+ } catch (err) {
+ console.error("Ошибка обработки документа:", err);
+ setError(err instanceof Error ? err.message : "Не удалось обработать документ");
+ toast.error("Ошибка обработки документа");
+ } finally {
+ setIsProcessing(false);
+ setProcessingProgress(null);
+ }
}
}
}, [title]);
@@ -54,10 +112,14 @@ export function DocumentUploadForm({ onSuccess, onCancel }: DocumentUploadFormPr
setIsSubmitting(true);
try {
+ // Используем обработанный файл, если он есть, иначе оригинальный
+ const fileToUpload = processedFile || file;
+
await apiClient.createDocument({
title,
description,
- file,
+ file: fileToUpload,
+ extracted_text: extractedText || undefined, // Отправляем извлеченный текст, если есть
});
toast.success("Документ успешно загружен");
@@ -66,6 +128,8 @@ export function DocumentUploadForm({ onSuccess, onCancel }: DocumentUploadFormPr
setTitle("");
setDescription("");
setFile(null);
+ setProcessedFile(null);
+ setExtractedText("");
if (onSuccess) {
onSuccess();
@@ -113,21 +177,56 @@ export function DocumentUploadForm({ onSuccess, onCancel }: DocumentUploadFormPr
) : (
-
-
-
-
{file.name}
-
- {(file.size / 1024 / 1024).toFixed(2)} МБ
-
+
+
+
+
+
{file.name}
+
+ {(file.size / 1024 / 1024).toFixed(2)} МБ
+
+
+
-
+
+ {/* Индикатор обработки */}
+ {isProcessing && processingProgress && (
+
+
+
+
+ {STAGE_MESSAGES[processingProgress.stage] || "Обработка..."}
+
+
+
+
+ {processingProgress.progress}%
+
+
+ )}
+
+ {/* Сообщение о сжатии */}
+ {processedFile && !isProcessing && (
+
+ ✓ Изображение сжато: {(file.size / 1024 / 1024).toFixed(2)} МБ → {(processedFile.size / 1024 / 1024).toFixed(2)} МБ
+
+ )}
)}
@@ -163,21 +262,45 @@ export function DocumentUploadForm({ onSuccess, onCancel }: DocumentUploadFormPr
/>
+ {/* Извлеченный текст */}
+ {extractedText && (
+
+
+
+ )}
+
{/* Buttons */}
{onCancel && (
diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts
index c57680de..870b8cf4 100644
--- a/frontend/src/lib/api.ts
+++ b/frontend/src/lib/api.ts
@@ -302,11 +302,13 @@ class ApiClient {
async createDocument(data: {
title: string;
description?: string;
- file: File;
+ file: File | Blob;
+ extracted_text?: string;
}): Promise
{
const formData = new FormData();
formData.append('title', data.title);
if (data.description) formData.append('description', data.description);
+ if (data.extracted_text) formData.append('extracted_text', data.extracted_text);
formData.append('file', data.file);
return this.request('/api/v1/documents/', {