| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213 |
- <?php
- namespace App\Filament\Resources\TextbookResource\Pages;
- use App\Filament\Resources\TextbookResource;
- use App\Services\TextbookApiService;
- use App\Services\TextbookCoverStorageService;
- use Filament\Resources\Pages\Page;
- use Livewire\Features\SupportFileUploads\TemporaryUploadedFile;
- use Livewire\WithFileUploads;
- use Throwable;
- /**
- * 教材配图独立页:与「编辑教材」分离,支持多次选择多图追加、排序、删除后统一保存。
- */
- class ManageTextbookCovers extends Page
- {
- use WithFileUploads;
- protected static string $resource = TextbookResource::class;
- public ?int $recordId = null;
- public string $officialTitle = '';
- /** @var list<string> */
- public array $coverUrls = [];
- /** 本次待追加的本地文件(可多次选择、多次点「追加到列表」) */
- public array $photos = [];
- public ?string $saveFeedback = null;
- public string $saveFeedbackType = 'info';
- /**
- * 必须与路由参数名一致({record}),Livewire 全页组件会把 URL 段注入 mount,勿仅用 Request::route(部分环境下为空)。
- */
- public function mount(int|string $record): void
- {
- $this->recordId = (int) $record;
- if ($this->recordId <= 0) {
- abort(404);
- }
- $data = app(TextbookApiService::class)->getTextbook($this->recordId);
- if (! $data) {
- abort(404);
- }
- $this->officialTitle = (string) ($data['official_title'] ?? '');
- $this->coverUrls = $this->splitCoverPath($data['cover_path'] ?? null);
- }
- /**
- * @return list<string>
- */
- protected function splitCoverPath(?string $path): array
- {
- if ($path === null || $path === '') {
- return [];
- }
- return array_values(array_unique(array_filter(array_map('trim', explode(',', $path)))));
- }
- public function appendPhotos(): void
- {
- if ($this->photos === []) {
- \Filament\Notifications\Notification::make()
- ->title('请先选择图片')
- ->warning()
- ->send();
- return;
- }
- $this->validate([
- 'photos' => 'array',
- 'photos.*' => 'image|max:5120',
- ]);
- $storage = app(TextbookCoverStorageService::class);
- $added = 0;
- foreach ($this->photos as $file) {
- if (! $file instanceof TemporaryUploadedFile) {
- continue;
- }
- $url = $storage->uploadCover($file, (string) $this->recordId);
- if ($url !== null && $url !== '') {
- $this->coverUrls[] = $url;
- $added++;
- }
- }
- $this->photos = [];
- $n = \Filament\Notifications\Notification::make()
- ->title($added > 0 ? "已追加 {$added} 张" : '未能上传')
- ->body($added > 0 ? '确认顺序后,点击页面底部「保存到服务器」写入教材。' : '请检查图片格式或存储配置。');
- if ($added > 0) {
- $n->success();
- } else {
- $n->warning();
- }
- $n->send();
- }
- public function removeAt(int $index): void
- {
- if (! isset($this->coverUrls[$index])) {
- return;
- }
- unset($this->coverUrls[$index]);
- $this->coverUrls = array_values($this->coverUrls);
- }
- public function moveUp(int $index): void
- {
- if ($index < 1 || ! isset($this->coverUrls[$index])) {
- return;
- }
- $tmp = $this->coverUrls[$index - 1];
- $this->coverUrls[$index - 1] = $this->coverUrls[$index];
- $this->coverUrls[$index] = $tmp;
- }
- public function moveDown(int $index): void
- {
- $n = count($this->coverUrls);
- if ($index >= $n - 1 || ! isset($this->coverUrls[$index])) {
- return;
- }
- $tmp = $this->coverUrls[$index + 1];
- $this->coverUrls[$index + 1] = $this->coverUrls[$index];
- $this->coverUrls[$index] = $tmp;
- }
- public function saveCovers(): void
- {
- if ($this->coverUrls === []) {
- $this->saveFeedback = '当前没有可保存的配图,请先上传并加入列表。';
- $this->saveFeedbackType = 'warning';
- \Filament\Notifications\Notification::make()
- ->title('暂无可保存图片')
- ->warning()
- ->send();
- return;
- }
- \Log::info('ManageTextbookCovers::saveCovers start', [
- 'record_id' => $this->recordId,
- 'count' => count($this->coverUrls),
- ]);
- $csv = $this->coverUrls === [] ? null : implode(',', $this->coverUrls);
- try {
- $response = app(TextbookApiService::class)->updateTextbook($this->recordId, [
- 'cover_path' => $csv,
- ]);
- } catch (Throwable $e) {
- \Log::error('ManageTextbookCovers::saveCovers failed', [
- 'record_id' => $this->recordId,
- 'error' => $e->getMessage(),
- ]);
- $this->saveFeedback = '保存失败:' . $e->getMessage();
- $this->saveFeedbackType = 'error';
- \Filament\Notifications\Notification::make()
- ->title('保存失败')
- ->body($e->getMessage())
- ->danger()
- ->send();
- return;
- }
- $row = $response['data'] ?? $response;
- if (is_array($row) && isset($row['cover_path']) && is_string($row['cover_path'])) {
- $this->coverUrls = $this->splitCoverPath($row['cover_path']);
- }
- \Log::info('ManageTextbookCovers::saveCovers success', [
- 'record_id' => $this->recordId,
- 'saved_count' => count($this->coverUrls),
- ]);
- $this->saveFeedback = '保存成功(' . now()->format('H:i:s') . ')';
- $this->saveFeedbackType = 'success';
- \Filament\Notifications\Notification::make()
- ->title('配图已保存')
- ->success()
- ->send();
- }
- public function getTitle(): string
- {
- return '教材配图';
- }
- public function getView(): string
- {
- return 'filament.resources.textbook-resource.manage-covers';
- }
- }
|