diff --git a/Endpoints/ImportEndpoints.cs b/Endpoints/ImportEndpoints.cs new file mode 100644 index 0000000..0d67caa --- /dev/null +++ b/Endpoints/ImportEndpoints.cs @@ -0,0 +1,51 @@ +using DocumentFormat.OpenXml.Packaging; +using DocumentFormat.OpenXml.Wordprocessing; +using System.Text; + +namespace WebEditor.Endpoints +{ + public static class ImportEndpoints + { + public static void MapImportEndpoints(this WebApplication app) + { + app.MapPost("/import/docx", async (HttpRequest request) => + { + if (!request.HasFormContentType) + return Results.BadRequest("Ожидается multipart/form-data"); + + var form = await request.ReadFormAsync(); + var file = form.Files.GetFile("file"); + + if (file == null || file.Length == 0) + return Results.BadRequest("Файл не найден"); + + if (!file.FileName.EndsWith(".docx", StringComparison.OrdinalIgnoreCase)) + return Results.BadRequest("Поддерживается только .docx"); + + try + { + using var stream = file.OpenReadStream(); + using var doc = WordprocessingDocument.Open(stream, false); + + var body = doc.MainDocumentPart?.Document?.Body; + if (body == null) + return Results.BadRequest("Не удалось прочитать документ"); + + // Извлекаем текст с сохранением абзацев + var sb = new StringBuilder(); + foreach (var para in body.Elements()) + { + var text = para.InnerText; + sb.AppendLine(text); + } + + return Results.Ok(new { text = sb.ToString().Trim() }); + } + catch (Exception ex) + { + return Results.BadRequest($"Ошибка чтения файла: {ex.Message}"); + } + }); + } + } +} \ No newline at end of file diff --git a/Program.cs b/Program.cs index 61934d0..0a8504a 100644 --- a/Program.cs +++ b/Program.cs @@ -39,6 +39,7 @@ public static void Main(string[] args) // ENDPOINTS app.MapAnalysisEndpoints(); app.MapExportEndpoints(); + app.MapImportEndpoints(); app.Run(); } diff --git a/client/src/components/Editor.jsx b/client/src/components/Editor.jsx index 720eb65..f1ed487 100644 --- a/client/src/components/Editor.jsx +++ b/client/src/components/Editor.jsx @@ -74,15 +74,30 @@ export default function Editor({ onAnalyze, loading, issues, result, onOpenRepor }) }, [issues]) + const handleImport = useCallback((text) => { + if (!text || !editor) return + const html = text + .split('\n') + .filter(line => line.trim()) + .map(line => `

${line}

`) + .join('') + + editor.commands.setContent(html) + + // Обновляем счётчик слов + setWordCount(text.trim().split(/\s+/).length) + }, [editor]) + return (
{/* Тулбар */} {/* Рабочая область */} diff --git a/client/src/components/EditorToolbar.jsx b/client/src/components/EditorToolbar.jsx index 7338771..248dbdd 100644 --- a/client/src/components/EditorToolbar.jsx +++ b/client/src/components/EditorToolbar.jsx @@ -42,14 +42,14 @@ function StyledSelect({ onChange, value, defaultValue, children, width }) { value={value} defaultValue={defaultValue} style={{ - height: '28px', - width: width || 'auto', - border: '1px solid #e2e8f0', - borderRadius: '4px', - fontSize: '12px', - padding: '0 4px', - cursor: 'pointer', - color: '#374151', + height: '28px', + width: width || 'auto', + border: '1px solid #e2e8f0', + borderRadius: '4px', + fontSize: '12px', + padding: '0 4px', + cursor: 'pointer', + color: '#374151', backgroundColor: '#fff' }} > @@ -58,7 +58,10 @@ function StyledSelect({ onChange, value, defaultValue, children, width }) { ) } -export default function EditorToolbar({ editor, wordCount, isDark, onToggleTheme }) { +// ↓ ВСЕ ХУКИ ТОЛЬКО ВНУТРИ КОМПОНЕНТА +export default function EditorToolbar({ editor, wordCount, isDark, onToggleTheme, onImport }) { + + // Хуки идут первыми — до любых return и условий! const setFontFamily = useCallback((font) => { editor?.chain().focus().setFontFamily(font).run() }, [editor]) @@ -67,6 +70,29 @@ export default function EditorToolbar({ editor, wordCount, isDark, onToggleTheme editor?.chain().focus().setFontSize(`${size}px`).run() }, [editor]) + const handleImport = useCallback(async (e) => { + const file = e.target.files?.[0] + if (!file) return + + const formData = new FormData() + formData.append('file', file) + + try { + const response = await fetch('https://localhost:7076/import/docx', { + method: 'POST', + body: formData + }) + if (!response.ok) throw new Error('Ошибка импорта') + const data = await response.json() + onImport(data.text) + } catch (err) { + console.error('Ошибка импорта:', err) + } + + e.target.value = '' + }, [onImport]) + + // Только после всех хуков if (!editor) return null return ( @@ -90,6 +116,40 @@ export default function EditorToolbar({ editor, wordCount, isDark, onToggleTheme + {/* Импорт DOCX */} +
+ +
+ 📄 Открыть DOCX +
+
+ + + {/* Шрифт */} setFontFamily(e.target.value)} defaultValue="Arial"> {FONTS.map(f => )} @@ -168,13 +228,8 @@ export default function EditorToolbar({ editor, wordCount, isDark, onToggleTheme {/* Правая часть */}
+ {wordCount} слов - {/* Счётчик слов */} - - {wordCount} слов - - - {/* Тёмная тема */}