Widget
概述
简介
Filament 允许你创建由 Widget 组成的动态仪表盘。Widget 是仪表盘上以特定方式显示数据的元素。比如,你可以展示统计数据、图表或表格。
创建 Widget
要创建 Widget,你可以使用 make:filament-widget
命令:
php artisan make:filament-widget MyWidget
该命令将会询问你想创建哪种类型的 Widget。以下是你可以选择的选项:
Widget 排序
每个 Widget 类都包含一个 $sort
属性,你可以用它来修改其在页面中相对于其他 Widget 的排序。
protected static ?int $sort = 2;
自定义仪表盘页面
如果你想自定义仪表盘类,比如,修改 Widget 列的数量,请新建 app/Filament/Pages/Dashboard.php
文件:
<?php
namespace App\Filament\Pages;
use Filament\Pages\Dashboard as BaseDashboard;
class Dashboard extends BaseDashboard
{
// ...
}
最后,请从配置文件中删除原来的 Dashboard
类:
use Filament\Panel;
public function panel(Panel $panel): Panel
{
return $panel
// ...
->discoverPages(in: app_path('Filament/Pages'), for: 'App\\Filament\\Pages')
->pages([]);
}
如果你没有使用 discoverPages()
来发现新仪表盘类目录中的页面,那么你应该在 pages()
方法中手动这侧该类:
use App\Filament\Pages\Dashboard;
use Filament\Panel;
public function panel(Panel $panel): Panel
{
return $panel
// ...
->pages([
Dashboard::class,
]);
}
创建多个仪表盘
如果你想创建多个仪表盘,你可以重复上述过程。创建继承 Dashboard
类的新页面将允许创建多个仪表盘怕。
你也需要定义指向其他仪表盘的 URL 路径,否则它将会是 /
:
protected static string $routePath = 'finance';
你也可以通过重写 $title
属性来自定义仪表盘标题:
protected static ?string $title = 'Finance dashboard';
根据定义的导航排序顺序,向用户显示的主仪表盘是他们有权访问的第一个仪表盘(由 [canAccess()
方法](../navigation/custom pages#授权)控制)。
仪表盘的默认排序顺序为 -2
。你可以使用 $navigationSort
控制自定义仪表盘的排序顺序:
protected static ?int $navigationSort = 15;
自定义 Widget 网格
你可以更改用于显示 Widget 的网格列的数量。
首先,你必须替换原始仪表盘页面。
现在,在新的 app/Filament/Pages/Dashboard.php
文件中,你可以重写 getColumn()
方法以返回要使用的网格列数量:
public function getColumns(): int | string | array
{
return 2;
}
响应式 Widget 网格
你可能希望基于浏览器的响应式断点修改 Widget 的网格列数。可以使用一个数组来完成此操作,该数组包含 Widget 在每个断点处应占用的列数:
public function getColumns(): int | string | array
{
return [
'md' => 4,
'xl' => 5,
];
}
这与响应式 Widget 宽度非常匹配。
自定义 Widget 宽度
你可以使用 $columnSpan
属性自定义 Widget 宽度。你可以使用 1 到 12 之间的数值说明 Widget 该占用多少列,或者使用 full
使之占用页面的全宽。
protected int | string | array $columnSpan = 'full';
响应式 Widget 宽度
你可能希望基于浏览器的响应式断点修改 Widget 的宽度。可以使用一个数组来完成此操作,该数组包含 Widget 在每个断点处应占用的列数:
protected int | string | array $columnSpan = [
'md' => 2,
'xl' => 3,
];
当使用响应式 Widget 网格时,这尤其有用。
条件性隐藏 Widget
你可以重写 Widget 的 canView()
静态方法,条件性地隐藏它们:
public static function canView(): bool
{
return auth()->user()->isAdmin();
}
表格 Widget
使用如下 Widget 创建命令,你可以添加表格到仪表盘中:
php artisan make:filament-widget LatestOrders --table
现在你可以编辑 Widget 文件自定义表格。
自定义 Widget
创建 BlogPostsOverview
Widget:
php artisan make:filament-widget BlogPostsOverview
该命令将生成两个文件 —— 在 Filament 目录下的 Widgets
目录中生成一个 Widget 类,以及在 Filament 视图目录的 widgets
目录中生成一个视图文件。
该类是一个 Livewire 组件,因此 Livewire 的所有特性都是可用的。Blade 视图可以包含任何你希望使用的 HTML,并且你可以在视图中访问所有公开的 Livewire 属性。你也可以在视图中使用 $this
访问 Livewire 组件实例。
过滤 Widget 数据
你可以向仪表盘添加一个表单,允许用户过滤所有 Widget 中显示数据。当过滤器更新时,Widget 将重新加载新数据。
首先,你必须[替换原始仪表盘页面](#自定义仪表盘页面)。
现在,在新的 app/Filament/Pages/Dashboard.php
文件中,你可以添加 HasFiltersForm
trait,并添加 filtersForm()
方法来返回表单组件:
use Filament\Forms\Components\DatePicker;
use Filament\Pages\Dashboard as BaseDashboard;
use Filament\Pages\Dashboard\Concerns\HasFiltersForm;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Schema;
class Dashboard extends BaseDashboard
{
use HasFiltersForm;
public function filtersForm(Schema $schema): Schema
{
return $schema
->components([
Section::make()
->schema([
DatePicker::make('startDate'),
DatePicker::make('endDate'),
// ...
])
->columns(3),
]);
}
}
In widget classes that require data from the filters, you need to add the InteractsWithPageFilters
trait, which will allow you to use the $this->pageFilters
property to access the raw data from the filters form:
use App\Models\BlogPost;
use Carbon\CarbonImmutable;
use Filament\Widgets\StatsOverviewWidget;
use Filament\Widgets\Concerns\InteractsWithPageFilters;
use Illuminate\Database\Eloquent\Builder;
class BlogPostsOverview extends StatsOverviewWidget
{
use InteractsWithPageFilters;
public function getStats(): array
{
$startDate = $this->pageFilters['startDate'] ?? null;
$endDate = $this->pageFilters['endDate'] ?? null;
return [
StatsOverviewWidget\Stat::make(
label: 'Total posts',
value: BlogPost::query()
->when($startDate, fn (Builder $query) => $query->whereDate('created_at', '>=', $startDate))
->when($endDate, fn (Builder $query) => $query->whereDate('created_at', '<=', $endDate))
->count(),
),
// ...
];
}
}
$this->pageFilters
数组将始终反射当前表单数据。请注意,此数据未经验证,因为它是实时的,且不打算用于查询数据库以外的任何目的。在使用数据之前,你必须确保数据是有效的。在这个例子中,我们检查在将其用于查询之前是否设置了开始日期。
使用 Action 模态框过滤 Widget 数据
或者,你可以将过滤器表单替换为操作模态框,模态框可以通过单击页面标题中的按钮打开。使用这种方法有很多好处:
- 过滤器表单并不总是可见的,这允许 Widget 使用页面的全高。
- 在用户单击“应用(Apply)”按钮之前,过滤器不会更新 Widget,这意味着在用户准备就绪之前,Widget 不会重新加载。如果加载 Widget 的开销很高,这可以提高性能。
- 可以对过滤器表单执行验证,这意味着 Widget 可以依赖于数据有效这一事实——在数据有效之前,用户无法提交表单。取消模态框将放弃用户的更改。
要使用操作模态框替代过滤器表单,你可以使用 HasFiltersAction
trait 特性而不是 HasFiltersForm
。然后,将 FilterAction
类注册为 getHeaderActions()
中的一个操作:
use Filament\Forms\Components\DatePicker;
use Filament\Pages\Dashboard as BaseDashboard;
use Filament\Pages\Dashboard\Actions\FilterAction;
use Filament\Pages\Dashboard\Concerns\HasFiltersAction;
class Dashboard extends BaseDashboard
{
use HasFiltersAction;
protected function getHeaderActions(): array
{
return [
FilterAction::make()
->form([
DatePicker::make('startDate'),
DatePicker::make('endDate'),
// ...
]),
];
}
}
处理来自过滤器操作的数据与处理来自过滤器表单的数据相同,除了数据在传递给 Widget 之前经过验证。InteractisWithPageFilters
trait 仍然适用。
在用户 Session 中持久化 Widget 过滤器
默认情况下,仪表盘过滤器将在页面之间的用户 Session 中持久化。要禁用此功能,请在仪表盘页面类中重写 $persistsFiltersInSession
属性:
use Filament\Pages\Dashboard as BaseDashboard;
use Filament\Pages\Dashboard\Concerns\HasFiltersForm;
class Dashboard extends BaseDashboard
{
use HasFiltersForm;
protected bool $persistsFiltersInSession = false;
}
或者,在仪表盘页面类中重写 persistsFiltersInSession()
方法:
use Filament\Pages\Dashboard as BaseDashboard;
use Filament\Pages\Dashboard\Concerns\HasFiltersForm;
class Dashboard extends BaseDashboard
{
use HasFiltersForm;
public function persistsFiltersInSession(): bool
{
return false;
}
}
禁用默认 Widget
仪表盘中默认有两个 Widegt。你可以更新配置的 widgets()
数组禁用这些 Widget:
use Filament\Panel;
public function panel(Panel $panel): Panel
{
return $panel
// ...
->widgets([]);
}
Edit on GitHubStill need help? Join our Discord community or open a GitHub discussion