*/ 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 */ 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'; } }