Languages

Version

Theme

Widget

概述

简介

Filament 允许你创建由 Widget 组成的动态仪表盘。Widget 是仪表盘上以特定方式显示数据的元素。比如,你可以展示统计数据图表表格

创建 Widget

要创建 Widget,你可以使用 make:filament-widget 命令:

php artisan make:filament-widget MyWidget

该命令将会询问你想创建哪种类型的 Widget。以下是你可以选择的选项:

  • Custom: 自定义 Widget。
  • Chart: 展示图表的 Widget。
  • Stats overview: 展示统计信息的 Widget。
  • Table: 展示表格的 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';

The primary dashboard shown to a user is the first one they have access to (controlled by canAccess() method), according to the defined navigation sort order.

The default sort order for dashboards is -2. You can control the sort order of custom dashboards with $navigationSort:

protected static ?int $navigationSort = 15;

自定义 Widget 网格

You may change how many grid columns are used to display widgets.

Firstly, you must replace the original Dashboard page.

Now, in your new app/Filament/Pages/Dashboard.php file, you may override the getColumns() method to return a number of grid columns to use:

public function getColumns(): int | string | array
{
    return 2;
}

响应式 Widget 网格

You may wish to change the number of widget grid columns based on the responsive breakpoint of the browser. You can do this using an array that contains the number of columns that should be used at each breakpoint:

public function getColumns(): int | string | array
{
    return [
        'md' => 4,
        'xl' => 5,
    ];
}

This pairs well with responsive widget widths.

自定义 Widget 宽度

You may customize the width of a widget using the $columnSpan property. You may use a number between 1 and 12 to indicate how many columns the widget should span, or full to make it occupy the full width of the page:

protected int | string | array $columnSpan = 'full';
响应式 Widget 宽度

You may wish to change the widget width based on the responsive breakpoint of the browser. You can do this using an array that contains the number of columns that the widget should occupy at each breakpoint:

protected int | string | array $columnSpan = [
    'md' => 2,
    'xl' => 3,
];

This is especially useful when using a responsive widgets grid.

条件性隐藏 Widget

You may override the static canView() method on widgets to conditionally hide them:

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 数据

You may add a form to the dashboard that allows the user to filter the data displayed across all widgets. When the filters are updated, the widgets will be reloaded with the new data.

Firstly, you must replace the original Dashboard page.

Now, in your new app/Filament/Pages/Dashboard.php file, you may add the HasFiltersForm trait, and add the filtersForm() method to return form components:

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(),
            ),
            // ...
        ];
    }
}

The $this->pageFilters array will always reflect the current form data. Please note that this data is not validated, as it is available live and not intended to be used for anything other than querying the database. You must ensure that the data is valid before using it. In this example, we check if the start date is set before using it in the query.

使用 Action 模态框过滤 Widget 数据

Alternatively, you can swap out the filters form for an action modal, that can be opened by clicking a button in the header of the page. There are many benefits to using this approach:

  • The filters form is not always visible, which allows you to use the full height of the page for widgets.
  • The filters do not update the widgets until the user clicks the “Apply” button, which means that the widgets are not reloaded until the user is ready. This can improve performance if the widgets are expensive to load.
  • Validation can be performed on the filters form, which means that the widgets can rely on the fact that the data is valid - the user cannot submit the form until it is. Canceling the modal will discard the user’s changes.

To use an action modal instead of a filters form, you can use the HasFiltersAction trait instead of HasFiltersForm. Then, register the FilterAction class as an action in 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'),
                    // ...
                ]),
        ];
    }
}

Handling data from the filter action is the same as handling data from the filters header form, except that the data is validated before being passed to the widget. The InteractsWithPageFilters trait still applies.

Persisting widget filters in the user’s session

By default, the dashboard filters applied will persist in the user’s session between page loads. To disable this, override the $persistsFiltersInSession property in the dashboard page class:

use Filament\Pages\Dashboard as BaseDashboard;
use Filament\Pages\Dashboard\Concerns\HasFiltersForm;

class Dashboard extends BaseDashboard
{
    use HasFiltersForm;

    protected bool $persistsFiltersInSession = false;
}

Alternatively, override the persistsFiltersInSession() method in the dashboard page class:

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

By default, two widgets are displayed on the dashboard. These widgets can be disabled by updating the widgets() array of the configuration:

use Filament\Panel;

public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->widgets([]);
}
Edit on GitHub

Still need help? Join our Discord community or open a GitHub discussion