add image upload functionality with history tracking, implement processing job, and integrate Tailwind CSS
parent
35d3d609a6
commit
2225751bc8
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\ProcessHistories;
|
||||
|
||||
class HistoryController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$histories = ProcessHistories::orderBy('id','desc')->get();
|
||||
return view('history.index', compact('histories'));
|
||||
}
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Livewire;
|
||||
|
||||
use Livewire\Component;
|
||||
use Livewire\WithFileUploads;
|
||||
use App\Models\ProcessHistories;
|
||||
use Illuminate\Support\Facades\Bus;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use App\Jobs\ProcessImagesJob;
|
||||
|
||||
class ImageUpload extends Component
|
||||
{
|
||||
use WithFileUploads;
|
||||
|
||||
public $images = []; // Массив из 3 элементов
|
||||
public $imagePreviews = [];
|
||||
public $historyId;
|
||||
public $status = 'idle';
|
||||
// Возможные статусы: 'idle', 'uploaded', 'processing', 'done', ...
|
||||
public $progressMessage = '';
|
||||
public $queueJobId;
|
||||
|
||||
protected $rules = [
|
||||
'images.*' => 'image|max:4096', // 4MB, можно изменить по желанию
|
||||
];
|
||||
|
||||
public function updatedImages($value, $key)
|
||||
{
|
||||
// После обновления файла, генерируем превью
|
||||
$this->validateOnly('images.*');
|
||||
|
||||
if ($this->images[$key]) {
|
||||
$this->imagePreviews[$key] = $this->images[$key]->temporaryUrl();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function startProcessing()
|
||||
{
|
||||
$this->validate();
|
||||
|
||||
// Сохраняем изображения
|
||||
$savedImages = [];
|
||||
foreach ($this->images as $uploaded) {
|
||||
$path = $uploaded->store('images', 'public');
|
||||
$savedImages[] = [
|
||||
'path' => $path,
|
||||
'original_name' => $uploaded->getClientOriginalName()
|
||||
];
|
||||
}
|
||||
|
||||
// Создаём запись в истории
|
||||
$history = ProcessHistories::create([
|
||||
'images' => $savedImages,
|
||||
'started_at' => now(),
|
||||
]);
|
||||
|
||||
$this->historyId = $history->id;
|
||||
$this->status = 'processing';
|
||||
$this->progressMessage = 'Ставим в очередь на обработку...';
|
||||
|
||||
// Отправляем задачу в очередь
|
||||
$job = new ProcessImagesJob($history->id);
|
||||
$dispatch = dispatch($job);
|
||||
|
||||
// Можно при желании сохранить ID задачи, если нужно
|
||||
// $this->queueJobId = ... (в зависимости от драйвера очереди)
|
||||
|
||||
// Дальше фронт будет периодически обновлять состояние либо
|
||||
// мы можем слушать события Livewire (Broadcasting) по готовности
|
||||
}
|
||||
|
||||
public function pollStatus()
|
||||
{
|
||||
if ($this->historyId) {
|
||||
$history = ProcessHistories::find($this->historyId);
|
||||
if ($history) {
|
||||
if ($history->finished_at) {
|
||||
$this->status = 'done';
|
||||
$this->progressMessage = 'Обработка завершена. Файл: ' . $history->step_file_name;
|
||||
} else {
|
||||
// Можно отобразить прогресс, если в историях где-то хранится этап
|
||||
$count = $history->processed_count;
|
||||
$this->progressMessage = "Обработка в процессе... {$count}/3 изображений";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.image-upload');
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,4 @@
|
||||
{
|
||||
"/js/app.js": "/js/app.js",
|
||||
"/css/app.css": "/css/app.css"
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
История обработок
|
||||
</x-slot>
|
||||
|
||||
<div class="p-4">
|
||||
<h1 class="text-2xl font-bold mb-4">История обработок</h1>
|
||||
<table class="min-w-full border-collapse">
|
||||
<thead>
|
||||
<tr class="border-b">
|
||||
<th class="p-2 text-left">ID</th>
|
||||
<th class="p-2 text-left">Изображения</th>
|
||||
<th class="p-2 text-left">Step файл</th>
|
||||
<th class="p-2 text-left">Время начала</th>
|
||||
<th class="p-2 text-left">Время окончания</th>
|
||||
<th class="p-2 text-left">Обработано</th>
|
||||
<th class="p-2 text-left">Затраты времени (сек)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($histories as $h)
|
||||
<tr class="border-b">
|
||||
<td class="p-2">{{ $h->id }}</td>
|
||||
<td class="p-2">
|
||||
@foreach($h->images as $img)
|
||||
<div>{{ $img['original_name'] }} ({{ $img['path'] }})</div>
|
||||
@endforeach
|
||||
</td>
|
||||
<td class="p-2">{{ $h->step_file_name }}</td>
|
||||
<td class="p-2">{{ $h->started_at }}</td>
|
||||
<td class="p-2">{{ $h->finished_at }}</td>
|
||||
<td class="p-2">{{ $h->processed_count }}/3</td>
|
||||
<td class="p-2">
|
||||
@if($h->stages_timing)
|
||||
Очередь: {{ $h->stages_timing['queue_wait_time'] ?? 'N/A' }}<br>
|
||||
Загрузка: {{ $h->stages_timing['upload_time'] ?? 'N/A' }}<br>
|
||||
Обработка: {{ $h->stages_timing['processing_time'] ?? 'N/A' }}<br>
|
||||
Сохранение: {{ $h->stages_timing['saving_time'] ?? 'N/A' }}<br>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</x-app-layout>
|
@ -0,0 +1,36 @@
|
||||
<div class="p-4 space-y-4">
|
||||
<h1 class="text-2xl font-bold">Загрузка изображений</h1>
|
||||
<div class="flex space-x-4">
|
||||
@for($i=0; $i<3; $i++)
|
||||
<div class="border-2 border-dashed border-gray-300 w-32 h-32 flex items-center justify-center">
|
||||
@if(isset($imagePreviews[$i]))
|
||||
<img src="{{ $imagePreviews[$i] }}" class="object-cover w-full h-full" alt="Preview">
|
||||
@else
|
||||
<label class="cursor-pointer text-center">
|
||||
<span class="text-gray-500">Перетащите файл сюда или кликните</span>
|
||||
<input type="file" class="hidden" wire:model="images.{{ $i }}">
|
||||
</label>
|
||||
@endif
|
||||
</div>
|
||||
@endfor
|
||||
</div>
|
||||
|
||||
<div class="space-x-2">
|
||||
<button wire:click="startProcessing"
|
||||
class="bg-blue-500 text-white px-4 py-2 rounded"
|
||||
@disabled($status !== 'idle' && $status !== 'uploaded')>
|
||||
Начать
|
||||
</button>
|
||||
@if($status === 'processing')
|
||||
<button wire:poll.2s="pollStatus" class="hidden"></button>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@if($progressMessage)
|
||||
<div class="text-gray-700 mt-4">{{ $progressMessage }}</div>
|
||||
@endif
|
||||
|
||||
@if($status === 'done')
|
||||
<a href="{{ route('history') }}" class="text-blue-500 underline">Посмотреть историю обработок</a>
|
||||
@endif
|
||||
</div>
|
Loading…
Reference in new issue