表格

布局

传统表格布局的问题

传统的表格因响应性差而臭名昭著。在移动端上,渲染水平方向较长的内容时,灵活性有限:

  • 允许用户水平滚动以查看更多表格内容
  • 在较小的设备上隐藏不重要的列

这两中在 Filament 中都能实现。当表溢出时,它们会自动水平滚动,你可以选择根据浏览器的响应断点显示和隐藏列。为此,你可以使用 visibleFrom() 或 hiddenFrom() 方法:

use Filament\Tables\Columns\TextColumn;

TextColumn::make('slug')
    ->visibleFrom('md')

这很好,但仍然存在一个明显的问题 —— 在移动设备上,用户如果不滚动,就无法一次看到表格行中的很多信息。

Filament 针对这一问题提供了两种解决方案:

  1. 简单堆叠 - 使用 stackedOnMobile() 方法,无需更改列定义,即可在移动设备上自动将所有单元格垂直堆叠。
  2. 自定义布局 - 使用 Split、Stack 等布局组件,针对不同断点精确控制内容的显示方式。

在移动设备上堆叠表格单元格

实现表格响应式布局最简单的方法是在表格上使用 stackedOnMobile() 方法。该方法会自动将传统的水平表格布局转换为移动设备屏幕上的垂直卡片式布局,同时在大屏幕上保持标准的表格外观:

use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;

public function table(Table $table): Table
{
    return $table
        ->columns([
            TextColumn::make('name')
                ->searchable()
                ->sortable(),
            TextColumn::make('email'),
            TextColumn::make('phone'),
            TextColumn::make('job'),
        ])
        ->stackedOnMobile();
}
Table with stacked mobile layout on desktop
Table with stacked mobile layout on mobile

在移动端,每一行数据会以卡片形式展示,列标签位于对应数值的上方。如果表格包含可排序的列,移动端界面会在表格顶部显示一个排序下拉菜单,让用户无需依赖传统的表头行即可进行排序。此外,系统也支持批量选择功能,表头区域会显示相应的复选框。

如果你希望在不重构列结构的前提下快速实现响应式布局,这种方案非常适用。不过,如果你需要更复杂的布局,或者希望精细控制内容在不同断点(breakpoint)下的显示方式,则建议使用下文介绍的布局组件。

自定义列布局

使用 Filament,你无需编写 HTML 或 CSS 代码,即可构建响应式的类表格界面。通过这些布局功能,你可以精确指定内容在表格行内的显示位置,并针对不同的响应式断点进行配置。

Table with responsive layout
Table with responsive layout on mobile

允许用户在移动端堆叠列

我们来引入一个组件 - Split:

use Filament\Support\Enums\FontWeight;
use Filament\Tables\Columns\Layout\Split;
use Filament\Tables\Columns\TextColumn;

Split::make([
    ImageColumn::make('avatar')
        ->circular(),
    TextColumn::make('name')
        ->weight(FontWeight::Bold)
        ->searchable()
        ->sortable(),
    TextColumn::make('email'),
])
Table with a split layout
Table with a split layout on mobile

Split 组件用于包裹列字段,并允许它们在移动设备上堆叠。

默认情况下,Split 中的列将始终并排显示。但是你可以选择一个该行为开始(form())的响应式断点。在此之前,列将堆叠在一起:

use Filament\Support\Enums\FontWeight;
use Filament\Tables\Columns\Layout\Split;
use Filament\Tables\Columns\ImageColumn;
use Filament\Tables\Columns\TextColumn;

Split::make([
    ImageColumn::make('avatar')
        ->circular(),
    TextColumn::make('name')
        ->weight(FontWeight::Bold)
        ->searchable()
        ->sortable(),
    TextColumn::make('email'),
])->from('md')

本例中,这些列只有在 md 断点以上的设备中水平并排展示:

Table with a split layout on desktop
Table with a stacked layout on mobile

防止列创建空格

Split,就像表格列一样,会自动调整它们的空格,以确保每一列都有适当的分隔。你可以使用 grow(false) 来阻止这种情况发生。在这个例子中,我们将确保头像图片紧紧地贴在名称列上:

use Filament\Support\Enums\FontWeight;
use Filament\Tables\Columns\Layout\Split;
use Filament\Tables\Columns\ImageColumn;
use Filament\Tables\Columns\TextColumn;

Split::make([
    ImageColumn::make('avatar')
        ->circular()
        ->grow(false),
    TextColumn::make('name')
        ->weight(FontWeight::Bold)
        ->searchable()
        ->sortable(),
    TextColumn::make('email'),
])

允许 grow() 的其他列将进行调整以消耗新释放的空间:

Table with a column that doesn't grow
Table with a column that doesn't grow on mobile

Split 中堆叠

在 Split 中,你可以将多列垂直堆叠在一起。这允许你在桌面端中在更少列中显示更多数据:

use Filament\Support\Enums\FontWeight;
use Filament\Tables\Columns\Layout\Split;
use Filament\Tables\Columns\Layout\Stack;
use Filament\Tables\Columns\ImageColumn;
use Filament\Tables\Columns\TextColumn;

Split::make([
    ImageColumn::make('avatar')
        ->circular(),
    TextColumn::make('name')
        ->weight(FontWeight::Bold)
        ->searchable()
        ->sortable(),
    Stack::make([
        TextColumn::make('phone')
            ->icon('heroicon-m-phone'),
        TextColumn::make('email')
            ->icon('heroicon-m-envelope'),
    ]),
])
Table with a stack
Table with a stack on mobile

在移动设备上隐藏堆叠

与单个列类似,你可以根据浏览器的响应式断点选择隐藏堆叠。为此,你可以使用 visibleFrom() 方法:

use Filament\Support\Enums\FontWeight;
use Filament\Tables\Columns\Layout\Split;
use Filament\Tables\Columns\Layout\Stack;
use Filament\Tables\Columns\ImageColumn;
use Filament\Tables\Columns\TextColumn;

Split::make([
    ImageColumn::make('avatar')
        ->circular(),
    TextColumn::make('name')
        ->weight(FontWeight::Bold)
        ->searchable()
        ->sortable(),
    Stack::make([
        TextColumn::make('phone')
            ->icon('heroicon-m-phone'),
        TextColumn::make('email')
            ->icon('heroicon-m-envelope'),
    ])->visibleFrom('md'),
])
Table with a stack
Table with no stack on mobile

对齐堆叠内容

默认情况下,堆叠中的列对齐到起始位置。你可以选择将堆叠中的列对齐到 Alignment::Center 或 Alignment::End:

use Filament\Support\Enums\Alignment;
use Filament\Support\Enums\FontWeight;
use Filament\Tables\Columns\Layout\Split;
use Filament\Tables\Columns\Layout\Stack;
use Filament\Tables\Columns\ImageColumn;
use Filament\Tables\Columns\TextColumn;

Split::make([
    ImageColumn::make('avatar')
        ->circular(),
    TextColumn::make('name')
        ->weight(FontWeight::Bold)
        ->searchable()
        ->sortable(),
    Stack::make([
        TextColumn::make('phone')
            ->icon('heroicon-m-phone')
            ->grow(false),
        TextColumn::make('email')
            ->icon('heroicon-m-envelope')
            ->grow(false),
    ])
        ->alignment(Alignment::End)
        ->visibleFrom('md'),
])

请确保堆栈内的列已设置 grow(false),否则它们将拉伸以填充堆栈的整个宽度并遵循它们自己的对齐配置而不是堆栈的对齐配置。

Table with a stack aligned right

堆叠内容的间距

默认情况下,堆叠内容在列之间没有垂直 padding。要添加 padding,你可以使用 space() 方法,该方法接受 1、2 或 3,对应于 Tailwind 的间距比例:

use Filament\Tables\Columns\Layout\Stack;
use Filament\Tables\Columns\TextColumn;

Stack::make([
    TextColumn::make('phone')
        ->icon('heroicon-m-phone'),
    TextColumn::make('email')
        ->icon('heroicon-m-envelope'),
])->space(1)
Table with spaced stacked content

使用网格控制列宽

有时,当列字段包含很多内容时,使用 Split 会出现不一致的宽度。这是因为它内部是使用 Flexbox 驱动,并且每行独自控制分配多少空格给内容。

作为替代方案,你可以使用 Grid 布局,它使用 CSS 网格布局,允许你控制列宽:

use Filament\Tables\Columns\Layout\Grid;
use Filament\Tables\Columns\TextColumn;

Grid::make([
    'lg' => 2,
])
    ->schema([
        TextColumn::make('phone')
            ->icon('heroicon-m-phone'),
        TextColumn::make('email')
            ->icon('heroicon-m-envelope'),
    ])

从 lg 断点开始,这些列字段在网格内始终会消费同样的宽度。

Table with a grid column layout

你也可以在其他断点处自定义网格的列数:

use Filament\Tables\Columns\Layout\Grid;
use Filament\Tables\Columns\Layout\Stack;
use Filament\Tables\Columns\TextColumn;

Grid::make([
    'lg' => 2,
    '2xl' => 4,
])
    ->schema([
        Stack::make([
            TextColumn::make('name'),
            TextColumn::make('job'),
        ]),
        TextColumn::make('phone')
            ->icon('heroicon-m-phone'),
        TextColumn::make('email')
            ->icon('heroicon-m-envelope'),
    ])

你甚至可以控制每个组件在每个临界点所使用的网格列数:

use Filament\Tables\Columns\Layout\Grid;
use Filament\Tables\Columns\Layout\Stack;
use Filament\Tables\Columns\TextColumn;

Grid::make([
    'lg' => 2,
    '2xl' => 5,
])
    ->schema([
        Stack::make([
            TextColumn::make('name'),
            TextColumn::make('job'),
        ])->columnSpan([
            'lg' => 'full',
            '2xl' => 2,
        ]),
        TextColumn::make('phone')
            ->icon('heroicon-m-phone')
            ->columnSpan([
                '2xl' => 2,
            ]),
        TextColumn::make('email')
            ->icon('heroicon-m-envelope'),
    ])

可折叠内容

当你使用像 Split 或 Stack 这样的列布局时,你也可以添加可折叠的内容。当你不想一次性显示表中的所有数据,但仍然希望用户在需要访问时可以访问它,而无需调出当前页面时,这非常有用。

Split 和 Stack 组件可以设置为 collapsible(),同时还有一个专门的 Panel 组件,它提供了一个预设的背景颜色和边框半径,将可折叠内容与其他内容分开:

use Filament\Support\Enums\FontWeight;
use Filament\Tables\Columns\Layout\Panel;
use Filament\Tables\Columns\Layout\Split;
use Filament\Tables\Columns\Layout\Stack;
use Filament\Tables\Columns\ImageColumn;
use Filament\Tables\Columns\TextColumn;

[
    Split::make([
        ImageColumn::make('avatar')
            ->circular(),
        TextColumn::make('name')
            ->weight(FontWeight::Bold)
            ->searchable()
            ->sortable(),
    ]),
    Panel::make([
        Stack::make([
            TextColumn::make('phone')
                ->icon('heroicon-m-phone'),
            TextColumn::make('email')
                ->icon('heroicon-m-envelope'),
        ]),
    ])->collapsible(),
]

使用 collapsed(false) 方法,你可以让面板默认展开:

use Filament\Tables\Columns\Layout\Panel;
use Filament\Tables\Columns\Layout\Split;
use Filament\Tables\Columns\TextColumn;

Panel::make([
    Split::make([
        TextColumn::make('phone')
            ->icon('heroicon-m-phone'),
        TextColumn::make('email')
            ->icon('heroicon-m-envelope'),
    ])->from('md'),
])->collapsed(false)
Table with collapsible content
Table with collapsible content on mobile

将记录排列到网格中

有时,你可能会发现你的数据更适合使用网格布局,而不是列表。Filament 也可以处理这种情况!

只需使用 $table->contentGrid() 方法:

use Filament\Tables\Columns\Layout\Stack;
use Filament\Tables\Table;

public function table(Table $table): Table
{
    return $table
        ->columns([
            Stack::make([
                // Columns
            ]),
        ])
        ->contentGrid([
            'md' => 2,
            'xl' => 3,
        ]);
}

本例中,这些行将会在网格中展示:

  • 在移动端,它们只显示在 1 列中。
  • 自 md 断点开始,它们会显示成 2 列。
  • 自 xl 断点开始,它们会显示成 3 列

这些设置完全是可自定义的,从 sm 到 2xl 的任何临界点都能包含 1 到 12 列。

Table with grid layout
Table with grid layout on mobile

自定义 HTML

使用 View 组件,你可以将自定义 HTML 添加到你的表格中。它也可以设置成可折叠 collapsible():

use Filament\Support\Enums\FontWeight;
use Filament\Tables\Columns\Layout\Split;
use Filament\Tables\Columns\Layout\View;
use Filament\Tables\Columns\ImageColumn;
use Filament\Tables\Columns\TextColumn;

[
    Split::make([
        ImageColumn::make('avatar')
            ->circular(),
        TextColumn::make('name')
            ->weight(FontWeight::Bold)
            ->searchable()
            ->sortable(),
    ]),
    View::make('users.table.collapsible-row-content')
        ->collapsible(),
]

然后,新建一个 /resources/views/users/table/collapsible-row-content.blade.php 文件,并添加 HTML。你可以使用 $getRecord() 访问表中的记录:

<p class="px-4 py-3 bg-gray-100 rounded-lg">
    <span class="font-medium">
        Email address:
    </span>

    <span>
        {{ $getRecord()->email }}
    </span>
</p>

嵌入其他组件

你甚至可以将列或者其他布局组件传入到 components() 方法:

use Filament\Support\Enums\FontWeight;
use Filament\Tables\Columns\Layout\Split;
use Filament\Tables\Columns\Layout\View;
use Filament\Tables\Columns\ImageColumn;
use Filament\Tables\Columns\TextColumn;

[
    Split::make([
        ImageColumn::make('avatar')
            ->circular(),
        TextColumn::make('name')
            ->weight(FontWeight::Bold)
            ->searchable()
            ->sortable(),
    ]),
    View::make('users.table.collapsible-row-content')
        ->components([
            TextColumn::make('email')
                ->icon('heroicon-m-envelope'),
        ])
        ->collapsible(),
]

然后,在 Blade 文件中渲染组件:

<div class="px-4 py-3 bg-gray-100 rounded-lg">
    @foreach ($getComponents() as $layoutComponent)
        {{ $layoutComponent
            ->record($getRecord())
            ->recordKey($getRecordKey())
            ->rowLoop($getRowLoop())
            ->renderInLayout() }}
    @endforeach
</div>
Edit on GitHub

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

Previous
Action