图表小部件
简介
Filament 自带多种「图表(chart)」小部件模板,可用于展示实时、交互式图表。
先用命令创建小部件:
php artisan make:filament-widget BlogPostsChart --chart所有图表共用同一个 ChartWidget 类。图表类型由 getType() 方法设置。本例中该方法返回字符串 'line'。
protected ?string $heading 变量用于设置描述图表的标题。若需动态设置标题,可覆盖 getHeading() 方法。
getData() 方法用于返回数据集与标签数组。每个数据集是带标签的待绘制点数组,每个标签为字符串。该结构与 Filament 用于渲染图表的 Chart.js 库相同。可参考 Chart.js 文档,按图表类型充分了解 getData() 可返回的内容。
<?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() 应返回什么:
- Bar chart - Chart.js documentation
- Bubble chart - Chart.js documentation
- Doughnut chart - Chart.js documentation
- Line chart - Chart.js documentation
- Pie chart - Chart.js documentation
- Polar area chart - Chart.js documentation
- Radar chart - Chart.js documentation
- Scatter chart - Chart.js documentation
例如,可从 getType() 返回 'bar' 使用柱状图:


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












自定义图表颜色
可通过设置 $color 属性自定义图表数据的颜色:
protected string $color = 'info';若要进一步自定义颜色,或在多个数据集间使用多种颜色,仍可在数据中使用 Chart.js 的颜色选项:
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 包从模型生成图表数据的示例:
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 属性:
public ?string $filter = 'today';然后定义 getFilters() 方法,返回筛选的值与标签数组:
protected function getFilters(): ?array
{
return [
'today' => 'Today',
'week' => 'Last week',
'month' => 'Last month',
'year' => 'This year',
];
}可在 getData() 方法中使用当前激活的筛选值:
protected function getData(): array
{
$activeFilter = $this->filter;
// ...
}

DANGER
$filter 属性可由用户控制。虽然 <select> 元素只提供 getFilters() 返回的键,但构造的请求可将 $this->filter 设为任意字符串,因此不限于这些键。用于查询前必须确保值有效——例如对照 getFilters() 的键检查,或使用带安全默认值的 match 表达式。切勿将 $this->filter 直接拼入原始查询。
自定义筛选
可用 schema 组件 为图表小部件构建自定义筛选。该方式定义筛选更灵活。
开始时,使用 HasFiltersSchema trait 并实现 filtersSchema() 方法:
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() 方法中使用:
protected function getData(): array
{
$startDate = $this->filters['startDate'] ?? null;
$endDate = $this->filters['endDate'] ?? null;
return [
// ...
];
}$this->filters 数组始终反映当前表单数据。请注意该数据未经验证,因实时可用,且仅应用于查询数据库。使用前必须确保数据有效。


INFO
若要添加一次作用于多个小部件的筛选,请参阅仪表盘中的筛选小部件数据。
延迟筛选更新
默认情况下,使用 filtersSchema() 的筛选在更改时会立即更新图表数据。但对复杂查询或更好的用户体验,你可能希望延迟筛选更新,直到用户点击「Apply」按钮。
延迟时,筛选更改仅在用户点击「Apply」按钮时应用,确保用户调整完所有筛选后图表才重新渲染。
页面首次加载时,图表会用默认筛选值展示数据,确保用户无需操作即可立即看到有意义的内容。
要启用延迟筛选,将 $hasDeferredFilters 属性设为 true:
use Filament\Widgets\ChartWidget\Concerns\HasFiltersSchema;
class BlogPostsChart extends ChartWidget
{
use HasFiltersSchema;
protected bool $hasDeferredFilters = true;
// ...
}若需动态控制是否延迟筛选,可覆盖 hasDeferredFilters() 方法:
public function hasDeferredFilters(): bool
{
return auth()->user()->prefersDeferredFilters();
}将筛选重置为默认值
使用延迟筛选时,筛选下拉页脚会在「Apply」按钮旁出现「Reset」链接。点击该链接会将所有筛选恢复为 filtersSchema() 中定义的默认值。例如,若在 DatePicker 上设置了 ->default(now()->subDays(30)),重置会恢复该默认日期,而非空值。
自定义筛选操作
可自定义延迟筛选时出现的 apply 与 reset 操作。所有可用于自定义操作触发按钮的方法均可使用:
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() 方法:
public function isEmpty(): bool
{
$data = $this->getCachedData();
return empty($data['datasets'][0]['data'] ?? []);
}设置空状态标题
要自定义空状态标题,请设置 $emptyStateHeading 属性:
protected ?string $emptyStateHeading = 'No data available';也可覆盖 getEmptyStateHeading() 方法以返回动态标题:
use Illuminate\Contracts\Support\Htmlable;
public function getEmptyStateHeading(): string | Htmlable
{
return "No sales yet for {$this->filter}";
}设置空状态描述
要自定义空状态描述,请设置 $emptyStateDescription 属性:
protected ?string $emptyStateDescription = 'Check back later once data has been collected.';也可覆盖 getEmptyStateDescription() 方法以返回动态描述:
use Illuminate\Contracts\Support\Htmlable;
public function getEmptyStateDescription(): string | Htmlable | null
{
return 'Sales data will appear here once orders are placed.';
}设置空状态图标
要自定义空状态的图标,请设置 $emptyStateIcon 属性:
use Filament\Support\Icons\Heroicon;
protected string | BackedEnum | null $emptyStateIcon = Heroicon::OutlinedChartBar;也可覆盖 getEmptyStateIcon() 方法以返回动态图标:
use BackedEnum;
use Filament\Support\Icons\Heroicon;
use Illuminate\Contracts\Support\Htmlable;
public function getEmptyStateIcon(): string | BackedEnum | Htmlable
{
return Heroicon::OutlinedShoppingCart;
}添加空状态操作
可通过覆盖 getEmptyStateActions() 方法,为空状态添加操作以提示用户采取行动:
use Filament\Actions\Action;
public function getEmptyStateActions(): array
{
return [
Action::make('refresh')
->label('Refresh')
->action('refresh'),
];
}使用自定义空状态视图
可通过覆盖 getEmptyState() 方法使用完全自定义的空状态视图:
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 属性为新间隔:
protected ?string $pollingInterval = '10s';也可完全禁用轮询:
protected ?string $pollingInterval = null;设置图表最大高度
可用 $maxHeight 属性为图表设置最大高度,避免过大:
protected ?string $maxHeight = '300px';

设置图表配置选项
可在图表类上指定 $options 变量,以控制 Chart.js 库提供的众多配置选项。例如,可为折线图关闭图例:
protected ?array $options = [
'plugins' => [
'legend' => [
'display' => false,
],
],
];也可覆盖 getOptions() 方法以返回动态选项数组:
protected function getOptions(): array
{
return [
'plugins' => [
'legend' => [
'display' => false,
],
],
];
}这些 PHP 数组在渲染图表时会转换为 JSON 对象。若要从该方法返回原始 JavaScript,可返回 RawJs 对象。例如在需要使用 JavaScript 回调函数时很有用:
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 或其上方任意元素上设置,以一次覆盖面板中全部图表:
.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 的任意点样式——circle、cross、crossRot、dash、line、rect、rectRounded、rectRot、star 或 triangle——以及 none 以完全隐藏。--chart-bar-border-radius 圆角化柱状图柱体(默认已略有圆角)。设为 0 可得直角柱。
这些值交给 Chart.js 而非浏览器使用,因此是不带单位的纯数字与关键字。若设为 Chart.js 无法使用的值会被忽略并保留默认。配色方案变化时也会重新读取,故可为亮/暗模式设置不同值。
INFO
这些属性用于一次为面板中全部图表设置样式,这通常正是主题所需。要更改单个图表,请改用 getOptions()——在那里设置的内容会覆盖此处属性。
为图表图例设置样式
图表下方的图例同样绘制在 canvas 上。两个属性控制每个标签旁的色块:
.fi-wi-chart {
--chart-legend-box-width: 16;
--chart-legend-border-radius: 0;
}--chart-legend-box-width 设置每个色块宽度,--chart-legend-border-radius 圆角化其边角(默认略有圆角以匹配柱状图柱体)。设为 0 可得方形色块。
为图表提示框设置样式
悬停图表时出现的提示框同样绘制在 canvas 上。其形状由另外两个属性控制:
.fi-wi-chart {
--chart-tooltip-corner-radius: 0;
--chart-tooltip-border-width: 1;
}其颜色设置方式不同,以便使用与主题其余部分相同的色板与暗色模式变体。Filament 从你用普通 color 声明设置样式的元素读取它们:
.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() 方法在图表标题下方添加描述:
public function getDescription(): ?string
{
return 'The number of blog posts published per month.';
}

禁用懒加载
默认情况下,小部件会懒加载,即仅在页面上可见时才会加载。
要禁用此行为,可在小部件类上覆盖 $isLazy 属性:
protected static bool $isLazy = false;使图表可折叠
可通过将小部件类上的 $isCollapsible 属性设为 true,使图表可折叠:
protected bool $isCollapsible = true;

使用自定义 Chart.js 插件
Chart.js 提供强大的插件系统,可扩展功能并创建自定义图表行为。本指南说明如何在图表小部件中使用它们。
步骤 1:用 NPM 安装插件
首先用 NPM 将插件安装到项目中。本指南将安装 chartjs-plugin-datalabels:
npm install chartjs-plugin-datalabels --save-dev步骤 2:创建导入插件的 JavaScript 文件
创建用于定义自定义插件的新 JavaScript 文件。本指南中命名为 filament-chart-js-plugins.js。导入插件,并将其加入 window.filamentChartJsPlugins 数组:
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([...]) 的「全局插件」:
import ChartDataLabels from 'chartjs-plugin-datalabels'
window.filamentChartJsGlobalPlugins ??= []
window.filamentChartJsGlobalPlugins.push(ChartDataLabels)步骤 3:用 Vite 编译 JavaScript 文件
接下来需用 Vite 或所选打包工具构建该 JavaScript 文件。将其加入 Vite 配置(通常为 vite.config.js)。例如:
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() 方法中完成:
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(),
]);