diff --git a/app/Containers/Dashboard/Actions/Pages/ExportPageAction.php b/app/Containers/Dashboard/Actions/Pages/ExportPageAction.php
new file mode 100644
index 0000000..3d0827c
--- /dev/null
+++ b/app/Containers/Dashboard/Actions/Pages/ExportPageAction.php
@@ -0,0 +1,42 @@
+load(['section.mainSection', 'seo']);
+
+ $sectionPath = null;
+
+ if ($page->section) {
+ $sectionPath = [
+ 'main_section_slug' => $page->section->mainSection?->slug,
+ 'sub_section_slug' => $page->section->slug,
+ ];
+ }
+
+ return [
+ 'export_version' => 1,
+ 'exported_at' => now()->toIso8601String(),
+ 'page' => [
+ 'title' => $page->title,
+ 'slug' => $page->slug,
+ 'code' => $page->code,
+ 'content' => $page->content,
+ 'settings' => $page->settings,
+ 'searchable' => $page->searchable,
+ 'sort' => $page->sort,
+ 'is_visible' => $page->is_visible,
+ ],
+ 'seo' => $page->seo ? [
+ 'title' => $page->seo->title,
+ 'description' => $page->seo->description,
+ ] : null,
+ 'section_path' => $sectionPath,
+ ];
+ }
+}
diff --git a/app/Containers/Dashboard/Actions/Pages/ImportPageAction.php b/app/Containers/Dashboard/Actions/Pages/ImportPageAction.php
new file mode 100644
index 0000000..533d0dc
--- /dev/null
+++ b/app/Containers/Dashboard/Actions/Pages/ImportPageAction.php
@@ -0,0 +1,84 @@
+resolveSubSectionId($data['section_path'] ?? null);
+
+ // Generate search_data from content
+ if (!empty($pageData['content'])) {
+ $pageData['search_data'] = $this->generateSearchDataTask->run($pageData['content']);
+ }
+
+ // Remove fields that should not be set during import
+ unset($pageData['icon']);
+
+ $page = Page::create($pageData);
+
+ // Attach SEO if present
+ if (!empty($data['seo'])) {
+ $page->seo()->create([
+ 'title' => $data['seo']['title'] ?? null,
+ 'description' => $data['seo']['description'] ?? null,
+ ]);
+ }
+
+ return $page;
+ }
+
+ private function resolveSubSectionId(?array $sectionPath): ?int
+ {
+ if (empty($sectionPath)) {
+ return null;
+ }
+
+ $mainSectionSlug = $sectionPath['main_section_slug'] ?? null;
+ $subSectionSlug = $sectionPath['sub_section_slug'] ?? null;
+
+ if (!$mainSectionSlug || !$subSectionSlug) {
+ return null;
+ }
+
+ $mainSection = MainSection::where('slug', $mainSectionSlug)->first();
+
+ if (!$mainSection) {
+ return null;
+ }
+
+ $subSection = SubSection::where('main_section_id', $mainSection->id)
+ ->where('slug', $subSectionSlug)
+ ->first();
+
+ return $subSection?->id;
+ }
+}
diff --git a/app/Containers/Dashboard/UI/WEB/Controllers/PageController.php b/app/Containers/Dashboard/UI/WEB/Controllers/PageController.php
index 77ad444..fb81493 100644
--- a/app/Containers/Dashboard/UI/WEB/Controllers/PageController.php
+++ b/app/Containers/Dashboard/UI/WEB/Controllers/PageController.php
@@ -6,6 +6,8 @@ use App\Containers\AppStructure\Models\Page;
use App\Containers\Dashboard\Actions\ContentBuilder\UploadContentBuilderFilesAction;
use App\Containers\Dashboard\Actions\Pages\CreatePageAction;
use App\Containers\Dashboard\Actions\Pages\DeletePageAction;
+use App\Containers\Dashboard\Actions\Pages\ExportPageAction;
+use App\Containers\Dashboard\Actions\Pages\ImportPageAction;
use App\Containers\Dashboard\Actions\Pages\ListPagesAction;
use App\Containers\Dashboard\Actions\Pages\UpdatePageAction;
use App\Containers\Dashboard\UI\WEB\Requests\StorePageRequest;
@@ -14,6 +16,7 @@ use App\Http\Controllers\Controller;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
+use Illuminate\Http\Response;
use Inertia\Inertia;
class PageController extends Controller
@@ -23,6 +26,8 @@ class PageController extends Controller
private readonly CreatePageAction $createPageAction,
private readonly UpdatePageAction $updatePageAction,
private readonly DeletePageAction $deletePageAction,
+ private readonly ExportPageAction $exportPageAction,
+ private readonly ImportPageAction $importPageAction,
private readonly UploadContentBuilderFilesAction $uploadContentBuilderFilesAction,
) {}
@@ -119,6 +124,58 @@ class PageController extends Controller
}
}
+ /**
+ * Экспортирует страницу в JSON-файл
+ */
+ public function export(Page $page): Response
+ {
+ try {
+ $data = $this->exportPageAction->run($page);
+
+ $filename = $page->slug . '_' . now()->format('Y-m-d_H-i-s') . '.json';
+
+ return response(json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT), 200, [
+ 'Content-Type' => 'application/json',
+ 'Content-Disposition' => 'attachment; filename="' . $filename . '"',
+ ]);
+ } catch (\Exception $e) {
+ return back()->with('error', 'Ошибка при экспорте страницы: ' . $e->getMessage());
+ }
+ }
+
+ /**
+ * Импортирует страницу из JSON-файла
+ */
+ public function import(Request $request): RedirectResponse
+ {
+ if (config('app.env') !== 'local') {
+ return back()->with('error', 'Импорт доступен только в окружении local');
+ }
+
+ try {
+ $request->validate([
+ 'import_file' => 'required|file|mimes:json,txt|max:512',
+ ]);
+
+ $file = $request->file('import_file');
+ $content = file_get_contents($file->getRealPath());
+ $data = json_decode($content, true);
+
+ if (json_last_error() !== JSON_ERROR_NONE) {
+ return back()->with('error', 'Некорректный JSON: ' . json_last_error_msg());
+ }
+
+ $page = $this->importPageAction->run($data);
+
+ return redirect()->route('dashboard.pages.edit', $page)
+ ->with('success', 'Страница "' . $page->title . '" успешно импортирована!');
+ } catch (\InvalidArgumentException $e) {
+ return back()->with('error', 'Ошибка валидации: ' . $e->getMessage());
+ } catch (\Exception $e) {
+ return back()->with('error', 'Ошибка при импорте страницы: ' . $e->getMessage());
+ }
+ }
+
/**
* Загружает файлы для ContentBuilder и возвращает метаданные
*/
diff --git a/app/Containers/Dashboard/UI/WEB/Routes/web.php b/app/Containers/Dashboard/UI/WEB/Routes/web.php
index 09ac00f..96c5b65 100755
--- a/app/Containers/Dashboard/UI/WEB/Routes/web.php
+++ b/app/Containers/Dashboard/UI/WEB/Routes/web.php
@@ -375,8 +375,10 @@ Route::middleware(['access-check', 'dashboard.auth', 'dashboard.permission'])->g
Route::get('/', [PageController::class, 'index'])->name('index');
Route::get('/create', [PageController::class, 'create'])->name('create');
Route::post('/', [PageController::class, 'store'])->name('store');
+ Route::post('/import', [PageController::class, 'import'])->name('import');
Route::post('/upload-files', [PageController::class, 'uploadFiles'])->name('upload-files');
Route::get('/{page}/edit', [PageController::class, 'edit'])->name('edit');
+ Route::get('/{page}/export', [PageController::class, 'export'])->name('export');
Route::put('/{page}', [PageController::class, 'update'])->name('update');
Route::delete('/{page}', [PageController::class, 'destroy'])->name('destroy');
});
diff --git a/resources/js/Pages/Dashboard/Pages/Index.vue b/resources/js/Pages/Dashboard/Pages/Index.vue
index 5aa8ff5..b94935e 100644
--- a/resources/js/Pages/Dashboard/Pages/Index.vue
+++ b/resources/js/Pages/Dashboard/Pages/Index.vue
@@ -6,13 +6,31 @@
Страницы
Управление страницами сайта
-
-
- Создать страницу
-
+
+
@@ -139,6 +157,13 @@
>
+