From 2e77978d5cd1947e7ec3b5f6c9740b67e5e244ce Mon Sep 17 00:00:00 2001 From: F4ilji Date: Mon, 13 Apr 2026 14:37:57 +0500 Subject: [PATCH] changes --- .../UploadContentBuilderFilesAction.php | 56 ++++++++++++ .../UI/WEB/Controllers/PageController.php | 28 ++++++ .../Dashboard/UI/WEB/Routes/web.php | 1 + .../ContentBuilder/blocks/ContactBlock.vue | 3 +- .../ContentBuilder/blocks/CustomFormBlock.vue | 3 +- .../ContentBuilder/blocks/FastFilesBlock.vue | 71 +++++++++++++-- .../ContentBuilder/blocks/FilesBlock.vue | 86 +++++++++++++++---- .../ContentBuilder/blocks/HeadingBlock.vue | 3 +- .../ContentBuilder/blocks/PageItemBlock.vue | 3 +- .../blocks/PageResourceListBlock.vue | 3 +- .../ContentBuilder/blocks/PersonBlock.vue | 3 +- .../ContentBuilder/blocks/PostItemBlock.vue | 3 +- .../ContentBuilder/blocks/PostListBlock.vue | 3 +- .../ContentBuilder/blocks/SliderBlock.vue | 3 +- .../ContentBuilder/blocks/StepperBlock.vue | 3 +- .../ContentBuilder/blocks/VideoBlock.vue | 3 +- 16 files changed, 241 insertions(+), 34 deletions(-) create mode 100644 app/Containers/Dashboard/Actions/ContentBuilder/UploadContentBuilderFilesAction.php diff --git a/app/Containers/Dashboard/Actions/ContentBuilder/UploadContentBuilderFilesAction.php b/app/Containers/Dashboard/Actions/ContentBuilder/UploadContentBuilderFilesAction.php new file mode 100644 index 0000000..e39f12f --- /dev/null +++ b/app/Containers/Dashboard/Actions/ContentBuilder/UploadContentBuilderFilesAction.php @@ -0,0 +1,56 @@ +uploadFileTask->run($file); + + $results[] = [ + 'path' => $uploadResult['path'], + 'url' => $uploadResult['url'], + 'title' => $uploadResult['original_name'], + 'expansion' => strtolower($file->getClientOriginalExtension()), + 'size' => $this->formatFileSize($file->getSize()), + ]; + } + + return $results; + } + + /** + * Форматирует размер файла в читаемый вид + */ + private function formatFileSize(int $bytes): string + { + $units = ['B', 'KB', 'MB', 'GB']; + $bytes = max($bytes, 0); + $pow = floor(($bytes ? log($bytes) : 0) / log(1024)); + $pow = min($pow, count($units) - 1); + $bytes /= (1 << (10 * $pow)); + + return round($bytes, 2) . ' ' . $units[$pow]; + } +} diff --git a/app/Containers/Dashboard/UI/WEB/Controllers/PageController.php b/app/Containers/Dashboard/UI/WEB/Controllers/PageController.php index 10f8092..77ad444 100644 --- a/app/Containers/Dashboard/UI/WEB/Controllers/PageController.php +++ b/app/Containers/Dashboard/UI/WEB/Controllers/PageController.php @@ -3,6 +3,7 @@ namespace App\Containers\Dashboard\UI\WEB\Controllers; 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\ListPagesAction; @@ -10,6 +11,7 @@ use App\Containers\Dashboard\Actions\Pages\UpdatePageAction; use App\Containers\Dashboard\UI\WEB\Requests\StorePageRequest; use App\Containers\Dashboard\UI\WEB\Requests\UpdatePageRequest; use App\Http\Controllers\Controller; +use Illuminate\Http\JsonResponse; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Inertia\Inertia; @@ -21,6 +23,7 @@ class PageController extends Controller private readonly CreatePageAction $createPageAction, private readonly UpdatePageAction $updatePageAction, private readonly DeletePageAction $deletePageAction, + private readonly UploadContentBuilderFilesAction $uploadContentBuilderFilesAction, ) {} /** @@ -115,4 +118,29 @@ class PageController extends Controller ->with('error', 'Ошибка при удалении страницы: ' . $e->getMessage()); } } + + /** + * Загружает файлы для ContentBuilder и возвращает метаданные + */ + public function uploadFiles(Request $request): JsonResponse + { + $request->validate([ + 'files' => 'required|array', + 'files.*' => 'required|file|mimes:pdf,docx,xlsx,pptx,zip,doc,xls,ppt|max:524288', + ]); + + try { + $results = $this->uploadContentBuilderFilesAction->run($request->file('files')); + + return response()->json([ + 'success' => true, + 'files' => $results, + ]); + } catch (\Exception $e) { + return response()->json([ + 'success' => false, + 'error' => 'Ошибка при загрузке файлов: ' . $e->getMessage(), + ], 500); + } + } } diff --git a/app/Containers/Dashboard/UI/WEB/Routes/web.php b/app/Containers/Dashboard/UI/WEB/Routes/web.php index 82c3bd9..36552f8 100755 --- a/app/Containers/Dashboard/UI/WEB/Routes/web.php +++ b/app/Containers/Dashboard/UI/WEB/Routes/web.php @@ -364,6 +364,7 @@ Route::middleware(['access-check', 'dashboard.auth'])->group(function () { Route::get('/', [PageController::class, 'index'])->name('index'); Route::get('/create', [PageController::class, 'create'])->name('create'); Route::post('/', [PageController::class, 'store'])->name('store'); + Route::post('/upload-files', [PageController::class, 'uploadFiles'])->name('upload-files'); Route::get('/{page}/edit', [PageController::class, 'edit'])->name('edit'); Route::put('/{page}', [PageController::class, 'update'])->name('update'); Route::delete('/{page}', [PageController::class, 'destroy'])->name('destroy'); diff --git a/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/ContactBlock.vue b/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/ContactBlock.vue index 600adba..fa45136 100644 --- a/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/ContactBlock.vue +++ b/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/ContactBlock.vue @@ -23,7 +23,7 @@ export default { name: 'ContactBlock', props: { modelValue: { type: Object, required: true } }, - emits: ['update:modelValue'], + emits: ['update:modelValue', 'update'], data() { return { contacts: [] }; }, async mounted() { try { @@ -34,6 +34,7 @@ export default { methods: { update(field, value) { this.$emit('update:modelValue', { ...this.modelValue, [field]: value }); + this.$emit('update'); } } } diff --git a/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/CustomFormBlock.vue b/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/CustomFormBlock.vue index 3a9d32d..4f67728 100644 --- a/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/CustomFormBlock.vue +++ b/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/CustomFormBlock.vue @@ -35,7 +35,7 @@ export default { name: 'CustomFormBlock', props: { modelValue: { type: Object, required: true } }, - emits: ['update:modelValue'], + emits: ['update:modelValue', 'update'], data() { return { forms: [] }; }, async mounted() { try { @@ -46,6 +46,7 @@ export default { methods: { update(field, value) { this.$emit('update:modelValue', { ...this.modelValue, [field]: value }); + this.$emit('update'); }, updateSettings(field, value) { this.update('settings', { ...this.modelValue.settings, [field]: value }); diff --git a/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/FastFilesBlock.vue b/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/FastFilesBlock.vue index baf4dd7..85f2d1e 100644 --- a/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/FastFilesBlock.vue +++ b/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/FastFilesBlock.vue @@ -22,12 +22,22 @@ + +
+ + + + + Загрузка файлов... +
+

PDF, DOCX, XLSX, PPTX, ZIP @@ -41,21 +51,66 @@ export default { props: { modelValue: { type: Object, required: true } }, - emits: ['update:modelValue'], + emits: ['update:modelValue', 'update'], + data() { + return { + uploading: false + }; + }, methods: { update(field, value) { this.$emit('update:modelValue', { ...this.modelValue, [field]: value }); + this.$emit('update'); }, removeFile(index) { this.update('path', this.modelValue.path.filter((_, i) => i !== index)); }, - handleFilesUpload(event) { + async handleFilesUpload(event) { const files = Array.from(event.target.files); - const newPaths = [...this.modelValue.path]; - files.forEach(file => { - newPaths.push(URL.createObjectURL(file)); - }); - this.update('path', newPaths); + if (files.length === 0) return; + + // Reset input to allow selecting same files again + event.target.value = ''; + + await this.uploadFilesToServer(files); + }, + async uploadFilesToServer(files) { + if (files.length === 0) return; + + this.uploading = true; + + try { + const formData = new FormData(); + files.forEach(file => { + formData.append('files[]', file); + }); + + const response = await fetch(route('dashboard.pages.upload-files'), { + method: 'POST', + headers: { + 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]')?.content ?? '', + 'Accept': 'application/json', + }, + body: formData, + }); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const result = await response.json(); + + if (result.success && result.files) { + const newPaths = result.files.map(file => file.path); + this.update('path', [...this.modelValue.path, ...newPaths]); + } else { + console.error('File upload failed:', result.error); + } + } catch (error) { + console.error('File upload error:', error); + } finally { + this.uploading = false; + } } } } diff --git a/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/FilesBlock.vue b/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/FilesBlock.vue index ac3ffe5..8bc5fd4 100644 --- a/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/FilesBlock.vue +++ b/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/FilesBlock.vue @@ -33,12 +33,22 @@ + +

+ + + + + Загрузка файлов... +
+

PDF, DOCX, XLSX, PPTX, ZIP. Макс. 512MB @@ -60,10 +70,16 @@ export default { props: { modelValue: { type: Object, required: true } }, - emits: ['update:modelValue'], + emits: ['update:modelValue', 'update'], + data() { + return { + uploading: false + }; + }, methods: { update(field, value) { this.$emit('update:modelValue', { ...this.modelValue, [field]: value }); + this.$emit('update'); }, updateFile(index, field, value) { const files = [...this.modelValue.file]; @@ -71,24 +87,64 @@ export default { this.update('file', files); }, addFile() { - this.update('file', [...this.modelValue.file, { title: '', path: '', expansion: '', size: '', time_added: Date.now() }]); + this.update('file', [...this.modelValue.file, { title: '', path: '', expansion: '', size: '', time_added: Math.floor(Date.now() / 1000) }]); }, removeFile(index) { this.update('file', this.modelValue.file.filter((_, i) => i !== index)); }, - handleFilesUpload(event) { + async handleFilesUpload(event) { const files = Array.from(event.target.files); - files.forEach(file => { - const extension = file.name.split('.').pop().toLowerCase(); - const size = (file.size / (1024 * 1024)).toFixed(2) + ' MB'; - this.update('file', [...this.modelValue.file, { - title: file.name, - path: URL.createObjectURL(file), - expansion: extension, - size: size, - time_added: Date.now() - }]); - }); + if (files.length === 0) return; + + // Reset input to allow selecting same files again + event.target.value = ''; + + await this.uploadFilesToServer(files); + }, + async uploadFilesToServer(files) { + if (files.length === 0) return; + + this.uploading = true; + + try { + const formData = new FormData(); + files.forEach(file => { + formData.append('files[]', file); + }); + + const response = await fetch(route('dashboard.pages.upload-files'), { + method: 'POST', + headers: { + 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]')?.content ?? '', + 'Accept': 'application/json', + }, + body: formData, + }); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const result = await response.json(); + + if (result.success && result.files) { + const newFiles = result.files.map(file => ({ + title: file.title, + path: file.path, + expansion: file.expansion, + size: file.size, + time_added: Math.floor(Date.now() / 1000) + })); + + this.update('file', [...this.modelValue.file, ...newFiles]); + } else { + console.error('File upload failed:', result.error); + } + } catch (error) { + console.error('File upload error:', error); + } finally { + this.uploading = false; + } } } } diff --git a/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/HeadingBlock.vue b/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/HeadingBlock.vue index c3cf047..d42ef90 100644 --- a/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/HeadingBlock.vue +++ b/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/HeadingBlock.vue @@ -41,13 +41,14 @@ export default { required: true } }, - emits: ['update:modelValue'], + emits: ['update:modelValue', 'update'], methods: { update(field, value) { this.$emit('update:modelValue', { ...this.modelValue, [field]: value }); + this.$emit('update'); } } } diff --git a/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/PageItemBlock.vue b/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/PageItemBlock.vue index 1668384..b4dd0d7 100644 --- a/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/PageItemBlock.vue +++ b/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/PageItemBlock.vue @@ -23,7 +23,7 @@ export default { name: 'PageItemBlock', props: { modelValue: { type: Object, required: true } }, - emits: ['update:modelValue'], + emits: ['update:modelValue', 'update'], data() { return { pages: [] }; }, async mounted() { try { @@ -34,6 +34,7 @@ export default { methods: { update(field, value) { this.$emit('update:modelValue', { ...this.modelValue, [field]: value }); + this.$emit('update'); } } } diff --git a/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/PageResourceListBlock.vue b/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/PageResourceListBlock.vue index defa843..5de6e65 100644 --- a/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/PageResourceListBlock.vue +++ b/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/PageResourceListBlock.vue @@ -23,7 +23,7 @@ export default { name: 'PageResourceListBlock', props: { modelValue: { type: Object, required: true } }, - emits: ['update:modelValue'], + emits: ['update:modelValue', 'update'], data() { return { resources: [] }; }, async mounted() { try { @@ -34,6 +34,7 @@ export default { methods: { update(field, value) { this.$emit('update:modelValue', { ...this.modelValue, [field]: value }); + this.$emit('update'); } } } diff --git a/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/PersonBlock.vue b/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/PersonBlock.vue index 87124e7..5c5aaa9 100644 --- a/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/PersonBlock.vue +++ b/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/PersonBlock.vue @@ -88,10 +88,11 @@ export default { props: { modelValue: { type: Object, required: true } }, - emits: ['update:modelValue'], + emits: ['update:modelValue', 'update'], methods: { update(field, value) { this.$emit('update:modelValue', { ...this.modelValue, [field]: value }); + this.$emit('update'); }, updateInfo(index, field, value) { const info = [...this.modelValue.info]; diff --git a/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/PostItemBlock.vue b/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/PostItemBlock.vue index 3a7897f..bfb7204 100644 --- a/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/PostItemBlock.vue +++ b/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/PostItemBlock.vue @@ -23,7 +23,7 @@ export default { name: 'PostItemBlock', props: { modelValue: { type: Object, required: true } }, - emits: ['update:modelValue'], + emits: ['update:modelValue', 'update'], data() { return { posts: [] }; }, async mounted() { try { @@ -34,6 +34,7 @@ export default { methods: { update(field, value) { this.$emit('update:modelValue', { ...this.modelValue, [field]: value }); + this.$emit('update'); } } } diff --git a/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/PostListBlock.vue b/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/PostListBlock.vue index c5d0891..50aa01c 100644 --- a/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/PostListBlock.vue +++ b/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/PostListBlock.vue @@ -37,7 +37,7 @@ export default { name: 'PostListBlock', props: { modelValue: { type: Object, required: true } }, - emits: ['update:modelValue'], + emits: ['update:modelValue', 'update'], data() { return { categories: [] }; }, async mounted() { try { @@ -48,6 +48,7 @@ export default { methods: { update(field, value) { this.$emit('update:modelValue', { ...this.modelValue, [field]: value }); + this.$emit('update'); } } } diff --git a/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/SliderBlock.vue b/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/SliderBlock.vue index aab899e..8abfe63 100644 --- a/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/SliderBlock.vue +++ b/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/SliderBlock.vue @@ -28,7 +28,7 @@ export default { props: { modelValue: { type: Object, required: true } }, - emits: ['update:modelValue'], + emits: ['update:modelValue', 'update'], data() { return { sliders: [] }; }, @@ -44,6 +44,7 @@ export default { methods: { update(field, value) { this.$emit('update:modelValue', { ...this.modelValue, [field]: value }); + this.$emit('update'); } } } diff --git a/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/StepperBlock.vue b/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/StepperBlock.vue index 8ca0a88..0989894 100644 --- a/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/StepperBlock.vue +++ b/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/StepperBlock.vue @@ -79,10 +79,11 @@ export default { props: { modelValue: { type: Object, required: true } }, - emits: ['update:modelValue'], + emits: ['update:modelValue', 'update'], methods: { update(field, value) { this.$emit('update:modelValue', { ...this.modelValue, [field]: value }); + this.$emit('update'); }, updateStep(index, field, value) { const steps = [...this.modelValue.steps]; diff --git a/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/VideoBlock.vue b/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/VideoBlock.vue index a816290..cbea99d 100644 --- a/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/VideoBlock.vue +++ b/resources/js/Pages/Dashboard/Components/ContentBuilder/blocks/VideoBlock.vue @@ -41,10 +41,11 @@ export default { props: { modelValue: { type: Object, required: true } }, - emits: ['update:modelValue'], + emits: ['update:modelValue', 'update'], methods: { update(field, value) { this.$emit('update:modelValue', { ...this.modelValue, [field]: value }); + this.$emit('update'); }, handleFileUpload(event) { const file = event.target.files[0];