Laravel Filament FileUpload切换Tab后PDF无法打开/下载问题
问题描述
Blade文件中设置了3个Tab,每个Tab内嵌实现了HasForms或HasTable的Livewire组件,默认激活第一个Tab。切换到第三个Tab的ProductEditor组件时,其中的产品数据手册PDF(对应FileUpload字段)能正常显示,但无法打开或下载,刷新页面后功能恢复正常。
相关代码
Blade文件的Tab结构
<nav class="relative z-0 rounded-lg shadow flex divide-x divide-gray-200" aria-label="Tabs"> <a @click.prevent="tab = 'parts'; window.location.hash = 'parts'" href="#"> <span>Parts & Sub-Products</span> </a> <a @click.prevent="tab = 'units'; window.location.hash = 'units'" href="#"> <span>Fabricated Units</span> </a> <a @click.prevent="tab = 'product'; window.location.hash = 'product'" href="#"> <span>Product Editor</span> </a> </nav> <div x-show="tab === 'parts'" style="display:none;"> @livewire('parts', ['product' => $product, 'materials' => $materials]) </div> <!-- Units --> <div x-show="tab === 'units'" style="display:none;"> <div class="mb-5"> <h2 class="font-semibold text-xl text-gray-800 leading-tight">Fabricated Units</h2> <p>Here is list of Fabricated units</p> </div> @livewire('fabricated-unit', ['product' => $product]) </div> <!-- Product editor --> <div x-show="tab === 'product'" style="display:none;"> <div class="mb-5"> <h2 class="font-semibold text-xl text-gray-800 leading-tight">Product Editor</h2> <p>You can edit or update your product here</p> </div> @livewire('product-editor', ['product' => $product]) </div>
ProductEditor组件代码
class ProductEditor extends Component implements HasForms { use InteractsWithForms; public ?array $data = []; public Product $product; public function mount(Product $product): void { $this->product = $product; $this->form->fill($product->toArray()); } public function render() { return view('livewire.product-editor'); } public function form(Form $form): Form { return $form ->schema([ TextInput::make('name')->label('Product Name')->required()->string()->minLength(3)->maxLength(255), TextInput::make('product_number')->label('Product Number')->required()->string()->minLength(3)->maxLength(255), TextInput::make('gtin_number')->label('GTIN Number')->nullable()->string()->minLength(1)->maxLength(255), Select::make('category_id') ->label('Category') ->options(Category::where('team_id', auth()->user()->currentTeam->id)->get()->pluck('name', 'id')) ->searchable(), FileUpload::make('images')->disk('product_photos')->nullable()->multiple() ->image()->reorderable()->maxSize('5120'), FileUpload::make('data_sheet_file')->label('Product Data Sheet')->disk('product_manuals')->openable()->downloadable()->previewable(false) // 问题字段 ->nullable()->acceptedFileTypes(['application/pdf']), Textarea::make('description')->label('Description')->nullable()->string()->minLength(1)->maxLength(1024), ]) ->statePath('data') ->model($this->product); } }
解决方案
问题核心是:Tab切换时x-show只是显示预先渲染的组件,而Filament的FileUpload生成的签名URL、交互事件在组件初始化时生成,动态显示时可能丢失绑定或权限上下文。以下是几种可行修复方案:
1. 按需加载Livewire组件(推荐)
避免一开始渲染所有组件并隐藏,改为切换Tab时才加载对应组件,确保ProductEditor初始化时能正确生成FileUpload的URL和事件。
修改Blade代码:
<nav class="relative z-0 rounded-lg shadow flex divide-x divide-gray-200" aria-label="Tabs"> <a @click.prevent="tab = 'parts'; window.location.hash = 'parts'" href="#"> <span>Parts & Sub-Products</span> </a> <a @click.prevent="tab = 'units'; window.location.hash = 'units'" href="#"> <span>Fabricated Units</span> </a> <a @click.prevent="tab = 'product'; window.location.hash = 'product'" href="#"> <span>Product Editor</span> </a> </nav> <div x-show="tab === 'parts'"> @if($tab === 'parts') @livewire('parts', ['product' => $product, 'materials' => $materials]) @endif </div> <!-- Units --> <div x-show="tab === 'units'"> @if($tab === 'units') <div class="mb-5"> <h2 class="font-semibold text-xl text-gray-800 leading-tight">Fabricated Units</h2> <p>Here is list of Fabricated units</p> </div> @livewire('fabricated-unit', ['product' => $product]) @endif </div> <!-- Product editor --> <div x-show="tab === 'product'"> @if($tab === 'product') <div class="mb-5"> <h2 class="font-semibold text-xl text-gray-800 leading-tight">Product Editor</h2> <p>You can edit or update your product here</p> </div> @livewire('product-editor', ['product' => $product], key="product-editor-{{ $product->id }}") @endif </div>
同时在父组件/控制器中定义$tab属性并同步hash:
// 父组件或控制器代码 public $tab = 'parts'; public function mount() { $hashTab = request()->query('tab') ?? (isset($_SERVER['HTTP_REFERER']) ? substr(parse_url($_SERVER['HTTP_REFERER'], PHP_URL_FRAGMENT), 0) : null); if($hashTab && in_array($hashTab, ['parts', 'units', 'product'])){ $this->tab = $hashTab; } }
2. Tab切换时刷新FileUpload组件
在ProductEditor中添加刷新方法,切换Tab时触发组件更新,重新生成签名URL:
在ProductEditor组件中新增:
public function refreshFileUpload() { $this->form->fill($this->product->toArray()); $this->dispatch('$refresh'); }
修改Blade中的Tab点击事件:
<a @click.prevent="tab = 'product'; window.location.hash = 'product'; $wire.call('productEditor.refreshFileUpload')" href="#"> <span>Product Editor</span> </a>
给ProductEditor组件添加唯一key:
@livewire('product-editor', ['product' => $product], key="product-editor-{{ $product->id }}")
3. 检查磁盘配置
确保product_manuals磁盘配置正确,私有磁盘需保证签名URL生成逻辑正常:
// config/filesystems.php 'product_manuals' => [ 'driver' => 'local', 'root' => storage_path('app/product_manuals'), 'url' => env('APP_URL').'/storage/product_manuals', 'visibility' => 'private', // 私有磁盘需生成签名URL ],
4. 改用Filament原生Tab组件
如果项目依赖Filament,直接使用其Tab组件,自动处理组件加载和初始化:
// 在Filament资源/组件中使用 Tabs::make('Tabs') ->tabs([ Tabs\Tab::make('Parts & Sub-Products') ->content(Livewire::make('parts', ['product' => $product, 'materials' => $materials])), Tabs\Tab::make('Fabricated Units') ->content(Livewire::make('fabricated-unit', ['product' => $product])), Tabs\Tab::make('Product Editor') ->content(Livewire::make('product-editor', ['product' => $product])), ]) ->activeTab('Parts & Sub-Products');
内容的提问来源于stack exchange,提问作者Rana Abdul Rauf
相关产品推荐
相关产品推荐

