Skip to content
全部文档

图表小部件

简介

Filament 自带多种「图表(chart)」小部件模板,可用于展示实时、交互式图表。

先用命令创建小部件:

bash
php artisan make:filament-widget BlogPostsChart --chart

所有图表共用同一个 ChartWidget 类。图表类型由 getType() 方法设置。本例中该方法返回字符串 'line'

protected ?string $heading 变量用于设置描述图表的标题。若需动态设置标题,可覆盖 getHeading() 方法。

getData() 方法用于返回数据集与标签数组。每个数据集是带标签的待绘制点数组,每个标签为字符串。该结构与 Filament 用于渲染图表的 Chart.js 库相同。可参考 Chart.js 文档,按图表类型充分了解 getData() 可返回的内容。

php
<?php

namespace App\Filament\Widgets;

use Filament\Widgets\ChartWidget;

class BlogPostsChart extends ChartWidget
{
    protected ?string $heading = 'Blog Posts';

    protected function getData(): array
    {
        return [
            'datasets' => [
                [
                    'label' => 'Blog posts created',
                    'data' => [0, 10, 5, 2, 21, 32, 45, 74, 65, 45, 77, 89],
                ],
            ],
            'labels' => ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
        ];
    }

    protected function getType(): string
    {
        return 'line';
    }
}

然后在仪表盘中查看该小部件。

折线图折线图

可用图表类型

以下是可扩展的可用图表小部件类,以及对应的 Chart.js 文档页,供参考 getData() 应返回什么:

例如,可从 getType() 返回 'bar' 使用柱状图:

柱状图柱状图

以下是其他可用图表类型的示例:

饼图饼图
环形图环形图
雷达图雷达图
极地区域图极地区域图
散点图散点图
气泡图气泡图

自定义图表颜色

可通过设置 $color 属性自定义图表数据的颜色

php
protected string $color = 'info';

若要进一步自定义颜色,或在多个数据集间使用多种颜色,仍可在数据中使用 Chart.js 的颜色选项

php
protected function getData(): array
{
    return [
        'datasets' => [
            [
                'label' => 'Blog posts created',
                'data' => [0, 10, 5, 2, 21, 32, 45, 74, 65, 45, 77, 89],
                'backgroundColor' => '#36A2EB',
                'borderColor' => '#9BD0F5',
            ],
        ],
        'labels' => ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
    ];
}

从 Eloquent 模型生成图表数据

要从 Eloquent 模型生成图表数据,Filament 建议安装 flowframe/laravel-trend 包。可查看其文档

以下是使用 laravel-trend 包从模型生成图表数据的示例:

php
use Flowframe\Trend\Trend;
use Flowframe\Trend\TrendValue;

protected function getData(): array
{
    $data = Trend::model(BlogPost::class)
        ->between(
            start: now()->startOfYear(),
            end: now()->endOfYear(),
        )
        ->perMonth()
        ->count();

    return [
        'datasets' => [
            [
                'label' => 'Blog posts',
                'data' => $data->map(fn (TrendValue $value) => $value->aggregate),
            ],
        ],
        'labels' => $data->map(fn (TrendValue $value) => $value->date),
    ];
}

筛选图表数据

基础 Select 筛选

可为图表设置筛选以更改展示的数据。常见用途是更改图表数据所覆盖的时间范围。

要设置默认筛选值,请设置 $filter 属性:

php
public ?string $filter = 'today';

然后定义 getFilters() 方法,返回筛选的值与标签数组:

php
protected function getFilters(): ?array
{
    return [
        'today' => 'Today',
        'week' => 'Last week',
        'month' => 'Last month',
        'year' => 'This year',
    ];
}

可在 getData() 方法中使用当前激活的筛选值:

php
protected function getData(): array
{
    $activeFilter = $this->filter;

    // ...
}
带筛选的图表带筛选的图表

DANGER

$filter 属性可由用户控制。虽然 <select> 元素只提供 getFilters() 返回的键,但构造的请求可将 $this->filter 设为任意字符串,因此不限于这些键。用于查询前必须确保值有效——例如对照 getFilters() 的键检查,或使用带安全默认值的 match 表达式。切勿将 $this->filter 直接拼入原始查询。

自定义筛选

可用 schema 组件 为图表小部件构建自定义筛选。该方式定义筛选更灵活。

开始时,使用 HasFiltersSchema trait 并实现 filtersSchema() 方法:

php
use Filament\Forms\Components\DatePicker;
use Filament\Schemas\Schema;
use Filament\Widgets\ChartWidget\Concerns\HasFiltersSchema;

class BlogPostsChart extends ChartWidget
{
    use HasFiltersSchema;
    
    // ...
    
    public function filtersSchema(Schema $schema): Schema
    {
        return $schema->components([
            DatePicker::make('startDate')
                ->default(now()->subDays(30)),
            DatePicker::make('endDate')
                ->default(now()),
        ]);
    }
}

筛选值可通过 $this->filters 数组访问,可在 getData() 方法中使用:

php
protected function getData(): array
{
    $startDate = $this->filters['startDate'] ?? null;
    $endDate = $this->filters['endDate'] ?? null;

    return [
        // ...
    ];
}

$this->filters 数组始终反映当前表单数据。请注意该数据未经验证,因实时可用,且仅应用于查询数据库。使用前必须确保数据有效。

带自定义筛选的图表带自定义筛选的图表

INFO

若要添加一次作用于多个小部件的筛选,请参阅仪表盘中的筛选小部件数据

延迟筛选更新

默认情况下,使用 filtersSchema() 的筛选在更改时会立即更新图表数据。但对复杂查询或更好的用户体验,你可能希望延迟筛选更新,直到用户点击「Apply」按钮。

延迟时,筛选更改仅在用户点击「Apply」按钮时应用,确保用户调整完所有筛选后图表才重新渲染。

页面首次加载时,图表会用默认筛选值展示数据,确保用户无需操作即可立即看到有意义的内容。

要启用延迟筛选,将 $hasDeferredFilters 属性设为 true

php
use Filament\Widgets\ChartWidget\Concerns\HasFiltersSchema;

class BlogPostsChart extends ChartWidget
{
    use HasFiltersSchema;

    protected bool $hasDeferredFilters = true;

    // ...
}

若需动态控制是否延迟筛选,可覆盖 hasDeferredFilters() 方法:

php
public function hasDeferredFilters(): bool
{
    return auth()->user()->prefersDeferredFilters();
}

将筛选重置为默认值

使用延迟筛选时,筛选下拉页脚会在「Apply」按钮旁出现「Reset」链接。点击该链接会将所有筛选恢复为 filtersSchema() 中定义的默认值。例如,若在 DatePicker 上设置了 ->default(now()->subDays(30)),重置会恢复该默认日期,而非空值。

自定义筛选操作

可自定义延迟筛选时出现的 apply 与 reset 操作。所有可用于自定义操作触发按钮的方法均可使用:

php
use Filament\Actions\Action;

public function filtersApplyAction(Action $action): Action
{
    return $action
        ->label('Update Chart')
        ->color('success');
}

public function filtersResetAction(Action $action): Action
{
    return $action
        ->label('Clear Filters')
        ->color('danger');
}

空状态

getData() 返回空数组时,图表小部件会渲染「空状态」而非图表。

要自定义何时渲染空状态,请覆盖 isEmpty() 方法:

php
public function isEmpty(): bool
{
    $data = $this->getCachedData();

    return empty($data['datasets'][0]['data'] ?? []);
}

设置空状态标题

要自定义空状态标题,请设置 $emptyStateHeading 属性:

php
protected ?string $emptyStateHeading = 'No data available';

也可覆盖 getEmptyStateHeading() 方法以返回动态标题:

php
use Illuminate\Contracts\Support\Htmlable;

public function getEmptyStateHeading(): string | Htmlable
{
    return "No sales yet for {$this->filter}";
}

设置空状态描述

要自定义空状态描述,请设置 $emptyStateDescription 属性:

php
protected ?string $emptyStateDescription = 'Check back later once data has been collected.';

也可覆盖 getEmptyStateDescription() 方法以返回动态描述:

php
use Illuminate\Contracts\Support\Htmlable;

public function getEmptyStateDescription(): string | Htmlable | null
{
    return 'Sales data will appear here once orders are placed.';
}

设置空状态图标

要自定义空状态的图标,请设置 $emptyStateIcon 属性:

php
use Filament\Support\Icons\Heroicon;

protected string | BackedEnum | null $emptyStateIcon = Heroicon::OutlinedChartBar;

也可覆盖 getEmptyStateIcon() 方法以返回动态图标:

php
use BackedEnum;
use Filament\Support\Icons\Heroicon;
use Illuminate\Contracts\Support\Htmlable;

public function getEmptyStateIcon(): string | BackedEnum | Htmlable
{
    return Heroicon::OutlinedShoppingCart;
}

添加空状态操作

可通过覆盖 getEmptyStateActions() 方法,为空状态添加操作以提示用户采取行动:

php
use Filament\Actions\Action;

public function getEmptyStateActions(): array
{
    return [
        Action::make('refresh')
            ->label('Refresh')
            ->action('refresh'),
    ];
}

使用自定义空状态视图

可通过覆盖 getEmptyState() 方法使用完全自定义的空状态视图:

php
use Illuminate\Contracts\Support\Htmlable;
use Illuminate\Contracts\View\View;

public function getEmptyState(): View | Htmlable | null
{
    return view('widgets.charts.custom-empty-state');
}

实时更新图表数据(轮询)

默认情况下,图表小部件每 5 秒刷新一次数据。

要自定义,可在类上覆盖 $pollingInterval 属性为新间隔:

php
protected ?string $pollingInterval = '10s';

也可完全禁用轮询:

php
protected ?string $pollingInterval = null;

设置图表最大高度

可用 $maxHeight 属性为图表设置最大高度,避免过大:

php
protected ?string $maxHeight = '300px';
带最大高度的图表带最大高度的图表

设置图表配置选项

可在图表类上指定 $options 变量,以控制 Chart.js 库提供的众多配置选项。例如,可为折线图关闭图例

php
protected ?array $options = [
    'plugins' => [
        'legend' => [
            'display' => false,
        ],
    ],
];

也可覆盖 getOptions() 方法以返回动态选项数组:

php
protected function getOptions(): array
{
    return [
        'plugins' => [
            'legend' => [
                'display' => false,
            ],
        ],
    ];
}

这些 PHP 数组在渲染图表时会转换为 JSON 对象。若要从该方法返回原始 JavaScript,可返回 RawJs 对象。例如在需要使用 JavaScript 回调函数时很有用:

php
use Filament\Support\RawJs;

protected function getOptions(): RawJs
{
    return RawJs::make(<<<JS
        {
            scales: {
                y: {
                    ticks: {
                        callback: (value) => '€' + value,
                    },
                },
            },
        }
    JS);
}

在主题中为图表设置样式

Chart.js 将图表绘制在 <canvas> 上,因此样式表几乎无法触及。自定义主题仅为 CSS,且无法调用 getOptions(),故 Filament 将主题最可能想改的部分暴露为 CSS 自定义属性。可在 .fi-wi-chart 或其上方任意元素上设置,以一次覆盖面板中全部图表:

css
.fi-wi-chart {
    --chart-border-width: 1;
    --chart-line-tension: 0.4;
    --chart-point-radius: 3;
    --chart-point-style: rect;
    --chart-bar-border-radius: 4;
}

--chart-border-width 设置图表围绕数据绘制的线条粗细。--chart-line-tension 控制折线图曲线弯曲程度(0 为直线段,最大到 1)。--chart-point-radius 设置折线、雷达或散点图标记大小,--chart-point-style 设置其形状,可接受 Chart.js 的任意点样式——circlecrosscrossRotdashlinerectrectRoundedrectRotstartriangle——以及 none 以完全隐藏。--chart-bar-border-radius 圆角化柱状图柱体(默认已略有圆角)。设为 0 可得直角柱。

这些值交给 Chart.js 而非浏览器使用,因此是不带单位的纯数字与关键字。若设为 Chart.js 无法使用的值会被忽略并保留默认。配色方案变化时也会重新读取,故可为亮/暗模式设置不同值。

INFO

这些属性用于一次为面板中全部图表设置样式,这通常正是主题所需。要更改单个图表,请改用 getOptions()——在那里设置的内容会覆盖此处属性。

为图表图例设置样式

图表下方的图例同样绘制在 canvas 上。两个属性控制每个标签旁的色块:

css
.fi-wi-chart {
    --chart-legend-box-width: 16;
    --chart-legend-border-radius: 0;
}

--chart-legend-box-width 设置每个色块宽度,--chart-legend-border-radius 圆角化其边角(默认略有圆角以匹配柱状图柱体)。设为 0 可得方形色块。

为图表提示框设置样式

悬停图表时出现的提示框同样绘制在 canvas 上。其形状由另外两个属性控制:

css
.fi-wi-chart {
    --chart-tooltip-corner-radius: 0;
    --chart-tooltip-border-width: 1;
}

其颜色设置方式不同,以便使用与主题其余部分相同的色板与暗色模式变体。Filament 从你用普通 color 声明设置样式的元素读取它们:

css
.fi-wi-chart {
    & .fi-wi-chart-tooltip-bg-color {
        @apply text-gray-900 dark:text-white;
    }

    & .fi-wi-chart-tooltip-text-color {
        @apply text-white dark:text-gray-900;
    }

    & .fi-wi-chart-tooltip-border-color {
        @apply text-gray-700 dark:text-gray-200;
    }
}

提示框在你赋予宽度前没有边框,因此 --chart-tooltip-border-width.fi-wi-chart-tooltip-border-color 通常一起更改。

图表本身的颜色同理:.fi-wi-chart-bg-color.fi-wi-chart-border-color 填充与勾勒数据,.fi-wi-chart-grid-color 绘制网格线,.fi-wi-chart-text-color 标注坐标轴。

统计概览小部件内的小图表单独设置样式,有各自的属性集。

添加描述

可用 getDescription() 方法在图表标题下方添加描述:

php
public function getDescription(): ?string
{
    return 'The number of blog posts published per month.';
}
带描述的图表带描述的图表

禁用懒加载

默认情况下,小部件会懒加载,即仅在页面上可见时才会加载。

要禁用此行为,可在小部件类上覆盖 $isLazy 属性:

php
protected static bool $isLazy = false;

使图表可折叠

可通过将小部件类上的 $isCollapsible 属性设为 true,使图表可折叠:

php
protected bool $isCollapsible = true;
可折叠图表可折叠图表

使用自定义 Chart.js 插件

Chart.js 提供强大的插件系统,可扩展功能并创建自定义图表行为。本指南说明如何在图表小部件中使用它们。

步骤 1:用 NPM 安装插件

首先用 NPM 将插件安装到项目中。本指南将安装 chartjs-plugin-datalabels

bash
npm install chartjs-plugin-datalabels --save-dev

步骤 2:创建导入插件的 JavaScript 文件

创建用于定义自定义插件的新 JavaScript 文件。本指南中命名为 filament-chart-js-plugins.js。导入插件,并将其加入 window.filamentChartJsPlugins 数组:

javascript
import ChartDataLabels from 'chartjs-plugin-datalabels'

window.filamentChartJsPlugins ??= []
window.filamentChartJsPlugins.push(ChartDataLabels)

这相当于在实例化 Chart.js 图表时,通过 new Chart(..., { plugins: [...] })「内联」包含插件。

在 push 之前,若数组尚未初始化则必须先初始化。这可确保多个注册 Chart.js 插件的 JavaScript 文件(尤其是来自 Filament 插件的)不会互相覆盖,无论启动顺序如何。

可向该数组 push 任意数量要安装的插件,不必为每个插件单独建文件。

此外,也可在 window.filamentChartJsGlobalPlugins 数组中注册会使用 Chart.register([...]) 的「全局插件」:

javascript
import ChartDataLabels from 'chartjs-plugin-datalabels'

window.filamentChartJsGlobalPlugins ??= []
window.filamentChartJsGlobalPlugins.push(ChartDataLabels)

步骤 3:用 Vite 编译 JavaScript 文件

接下来需用 Vite 或所选打包工具构建该 JavaScript 文件。将其加入 Vite 配置(通常为 vite.config.js)。例如:

javascript
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';

export default defineConfig({
    plugins: [
        laravel({
            input: [
                'resources/css/app.css',
                'resources/js/app.js',
                'resources/css/filament/admin/theme.css',
                'resources/js/filament-chart-js-plugins.js', // Include the new file in the `input` array so it is built
            ],
        }),
    ],
});

npm run build 构建该文件。

步骤 4:在 Filament 中注册 JavaScript 文件

Filament 需要在渲染图表小部件时包含该 JavaScript 文件。可在如 AppServiceProvider 等服务提供者的 boot() 方法中完成:

php
use Filament\Support\Assets\Js;
use Filament\Support\Facades\FilamentAsset;
use Illuminate\Support\Facades\Vite;

FilamentAsset::register([
    Js::make('chart-js-plugins', Vite::asset('resources/js/filament-chart-js-plugins.js'))->module(),
]);

可了解更多关于资源注册,甚至为特定面板注册资源