Skip to content
全部文档

概述

简介

默认情况下,Filament 会为每个 资源自定义页面集群 注册导航项。这些类包含你可以覆盖的静态属性和方法,用于配置该导航项。

若要为应用添加第二层导航,可以使用 集群。它们适合将资源和页面分组到一起。

自定义导航项标签

默认情况下,导航标签根据资源或页面的名称生成。你可以使用 $navigationLabel 属性自定义:

php
protected static ?string $navigationLabel = 'Custom Navigation Label';

或者,你可以覆盖 getNavigationLabel() 方法:

php
public static function getNavigationLabel(): string
{
    return 'Custom Navigation Label';
}

自定义导航项图标

若要自定义导航项的 图标,可以在 资源页面 类上覆盖 $navigationIcon 属性:

php
use BackedEnum;
use Filament\Support\Icons\Heroicon;

protected static string | BackedEnum | null $navigationIcon = Heroicon::OutlinedDocumentText;
已更改的导航项图标已更改的导航项图标

若将同一导航分组内所有项的 $navigationIcon = null,这些项会在分组标签下方用竖线连接。

在导航项激活时切换图标

你可以使用 $activeNavigationIcon 属性指定仅用于激活项的导航 图标

php
use BackedEnum;
use Filament\Support\Icons\Heroicon;

protected static string | BackedEnum | null $activeNavigationIcon = Heroicon::OutlinedDocumentText;
激活时使用不同导航项图标激活时使用不同导航项图标

排序导航项

默认情况下,导航项按字母顺序排序。你可以使用 $navigationSort 属性自定义:

php
protected static ?int $navigationSort = 3;

现在,排序值较小的导航项会出现在排序值较大的项之前——顺序为升序。

排序导航项排序导航项

为导航项添加徽章

若要在导航项旁添加徽章,可以使用 getNavigationBadge() 方法并返回徽章内容:

php
public static function getNavigationBadge(): ?string
{
    return static::getModel()::count();
}
带徽章的导航项带徽章的导航项

getNavigationBadge() 返回了徽章值,默认会使用 primary 颜色显示。若要按语境为徽章设置样式,请从 getNavigationBadgeColor() 方法返回 dangergrayinfoprimarysuccesswarning

php
public static function getNavigationBadgeColor(): ?string
{
    return static::getModel()::count() > 10 ? 'warning' : 'primary';
}
带颜色徽章的导航项带颜色徽章的导航项

可以在 $navigationBadgeTooltip 中设置导航徽章的自定义提示:

php
protected static ?string $navigationBadgeTooltip = 'The number of users';

或者从 getNavigationBadgeTooltip() 返回:

php
public static function getNavigationBadgeTooltip(): ?string
{
    return 'The number of users';
}
带徽章提示的导航项带徽章提示的导航项

分组导航项

你可以通过在 资源自定义页面 上指定 $navigationGroup 属性来分组导航项:

php
use UnitEnum;

protected static string | UnitEnum | null $navigationGroup = 'Settings';
已分组的导航项已分组的导航项

同一导航分组中的所有项会一起显示在相同的分组标签下,本例中为「Settings」。未分组的项会留在导航的起始位置。

将导航项分组到其他项下

你可以通过设置 $navigationParentItem 属性,将导航项作为其他项的子项进行分组。可以通过父项的页面或资源类,或通过其标签来引用父项:

php
use App\Filament\Resources\Notifications\NotificationResource;
use UnitEnum;

protected static ?string $navigationParentItem = NotificationResource::class;

protected static string | UnitEnum | null $navigationGroup = 'Settings';

或者,你可以通过标签引用父项:

php
use UnitEnum;

protected static ?string $navigationParentItem = 'Notifications';

protected static string | UnitEnum | null $navigationGroup = 'Settings';

你也可以使用 getNavigationParentItem() 方法动态确定父项:

php
use App\Filament\Resources\Notifications\NotificationResource;

public static function getNavigationParentItem(): ?string
{
    return NotificationResource::class;
}

或者,你可以返回父项的标签:

php
public static function getNavigationParentItem(): ?string
{
    return __('filament/navigation.groups.settings.items.notifications');
}

父项和子项必须属于同一导航分组。若父项有导航分组,子项也必须定义该分组,否则无法识别正确的父项。无论通过类还是标签引用父项,都适用此规则。

TIP

若你正在考虑这样的第三层导航,应改用 集群。集群是资源和自定义页面的逻辑分组,可以共享各自独立的导航。

自定义导航分组

你可以在 配置 中调用 navigationGroups(),并按顺序传入 NavigationGroup 对象来自定义导航分组:

php
use Filament\Navigation\NavigationGroup;
use Filament\Panel;
use Filament\Support\Icons\Heroicon;

public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->navigationGroups([
            NavigationGroup::make()
                 ->label('Shop')
                 ->icon(Heroicon::OutlinedShoppingCart),
            NavigationGroup::make()
                ->label('Blog')
                ->icon(Heroicon::OutlinedPencil),
            NavigationGroup::make()
                ->label(fn (): string => __('navigation.settings'))
                ->icon(Heroicon::OutlinedCog6Tooth)
                ->collapsed(),
        ]);
}

本例中,我们为分组传入自定义 icon(),并让其中一个默认 collapsed()

排序导航分组

使用 navigationGroups() 时,你是在为导航分组定义新顺序。若只想重新排序分组而不定义完整的 NavigationGroup 对象,只需按新顺序传入分组的标签:

php
$panel
    ->navigationGroups([
        'Shop',
        'Blog',
        'Settings',
    ])

使导航分组不可折叠

默认情况下,导航分组是可折叠的。

可折叠的导航分组可折叠的导航分组

你可以在 NavigationGroup 对象上调用 collapsible(false) 来禁用此行为:

php
use Filament\Navigation\NavigationGroup;
use Filament\Support\Icons\Heroicon;

NavigationGroup::make()
    ->label('Settings')
    ->icon(Heroicon::OutlinedCog6Tooth)
    ->collapsible(false);
不可折叠的导航分组不可折叠的导航分组

或者,可以在 配置 中为所有分组全局设置:

php
use Filament\Panel;

public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->collapsibleNavigationGroups(false);
}

为导航分组添加额外 HTML 属性

你可以为导航分组传入额外 HTML 属性,它们会合并到外层 DOM 元素上。将属性数组传给 extraSidebarAttributes()extraTopbarAttributes() 方法,键为属性名,值为属性值:

php
NavigationGroup::make()
    ->extraSidebarAttributes(['class' => 'featured-sidebar-group']),
    ->extraTopbarAttributes(['class' => 'featured-topbar-group']),

extraSidebarAttributes() 会应用于侧栏中的导航分组元素,extraTopbarAttributes() 仅在使用 顶部导航 时应用于顶栏导航分组下拉菜单。

使用枚举注册导航分组

你可以使用枚举类注册导航分组,从而在单一位置控制它们的标签、图标和顺序,而无需在 配置 中注册。

为此,可以创建一个包含每个分组 case 的枚举类:

php
enum NavigationGroup
{
    case Shop;
    
    case Blog;
    
    case Settings;
}

case 的定义顺序会控制导航分组的顺序。

要为资源或自定义页面使用枚举导航分组,可以将 $navigationGroup 属性设为枚举 case:

php
protected static string | UnitEnum | null $navigationGroup = NavigationGroup::Shop;

你也可以在枚举类上实现 HasLabel 接口,为每个分组定义自定义标签:

php
use Filament\Support\Contracts\HasLabel;

enum NavigationGroup implements HasLabel
{
    case Shop;
    
    case Blog;
    
    case Settings;

    public function getLabel(): string
    {
        return match ($this) {
            self::Shop => __('navigation-groups.shop'),
            self::Blog => __('navigation-groups.blog'),
            self::Settings => __('navigation-groups.settings'),
        };
    }
}

你也可以在枚举类上实现 HasIcon 接口,为每个分组定义自定义图标:

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

enum NavigationGroup implements HasIcon
{
    case Shop;
    
    case Blog;
    
    case Settings;

    public function getIcon(): string | BackedEnum | Htmlable | null
    {
        return match ($this) {
            self::Shop => Heroicon::OutlinedShoppingCart,
            self::Blog => Heroicon::OutlinedPencil,
            self::Settings => Heroicon::OutlinedCog6Tooth,
        };
    }
}

桌面端可折叠侧栏

若要让侧栏在桌面端和移动端都可折叠,可以使用 配置

php
use Filament\Panel;

public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->sidebarCollapsibleOnDesktop();
}
桌面端可折叠侧栏桌面端可折叠侧栏

默认情况下,在桌面端折叠侧栏时仍会显示导航图标。你可以使用 sidebarFullyCollapsibleOnDesktop() 方法完全折叠侧栏:

php
use Filament\Panel;

public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->sidebarFullyCollapsibleOnDesktop();
}
桌面端完全可折叠侧栏桌面端完全可折叠侧栏

INFO

本节仅适用于 sidebarCollapsibleOnDesktop(),不适用于 sidebarFullyCollapsibleOnDesktop(),因为完全折叠的 UI 只是隐藏整个侧栏,而不是改变其设计。

在桌面端使用可折叠侧栏时,通常也会使用 导航分组。默认情况下,侧栏折叠时每个导航分组的标签会被隐藏,因为没有空间显示它们。即使导航分组本身是 可折叠的,折叠侧栏中仍会显示所有项,因为没有可点击以展开分组的分组标签。

这些问题可以通过向导航分组对象 传入 icon() 来解决,从而实现非常精简的侧栏设计。定义图标后,折叠侧栏会始终显示图标而不是各项。点击图标时,会在图标旁打开下拉菜单,显示分组中的项。

向导航分组传入图标时,即使各项也有图标,展开的侧栏 UI 也不会显示项图标。这是为了保持导航层级清晰、设计精简。不过,折叠侧栏的下拉菜单中会显示各项的图标,因为下拉菜单已打开,层级已经清晰。

带导航分组图标的可折叠侧栏带导航分组图标的可折叠侧栏

注册自定义导航项

若要注册新的导航项,可以使用 配置

php
use Filament\Navigation\NavigationItem;
use Filament\Pages\Dashboard;
use Filament\Panel;
use Filament\Support\Icons\Heroicon;
use function Filament\Support\original_request;

public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->navigationItems([
            NavigationItem::make('Analytics')
                ->url('https://filament.pirsch.io', shouldOpenInNewTab: true)
                ->icon(Heroicon::OutlinedPresentationChartLine)
                ->group('Reports')
                ->sort(3),
            NavigationItem::make('dashboard')
                ->label(fn (): string => __('filament-panels::pages/dashboard.title'))
                ->url(fn (): string => Dashboard::getUrl())
                ->isActiveWhen(fn () => original_request()->routeIs('filament.admin.pages.dashboard')),
            // ...
        ]);
}

有条件地隐藏导航项

你也可以使用 visible()hidden() 方法,并传入要检查的条件,有条件地隐藏导航项:

php
use Filament\Navigation\NavigationItem;

NavigationItem::make('Analytics')
    ->visible(fn(): bool => auth()->user()->can('view-analytics'))
    // or
    ->hidden(fn(): bool => ! auth()->user()->can('view-analytics')),

禁用资源或页面导航项

若要阻止资源或页面出现在导航中,可以使用:

php
protected static bool $shouldRegisterNavigation = false;

或者,你可以覆盖 shouldRegisterNavigation() 方法:

php
public static function shouldRegisterNavigation(): bool
{
    return false;
}

DANGER

shouldRegisterNavigation() 只会从侧栏隐藏链接——它不会阻止用户直接输入 URL。若要真正限制访问,请使用 资源授权页面授权

使用顶部导航

默认情况下,Filament 使用侧栏导航。你可以通过 配置 改用顶部导航:

php
use Filament\Panel;

public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->topNavigation();
}
顶部导航顶部导航

自定义侧栏宽度

你可以通过在 配置 中将宽度传给 sidebarWidth() 方法来自定义侧栏宽度:

php
use Filament\Panel;

public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->sidebarWidth('40rem');
}
自定义侧栏宽度的面板自定义侧栏宽度的面板

此外,若使用了 sidebarCollapsibleOnDesktop() 方法,可以通过 配置 中的 collapsedSidebarWidth() 方法自定义折叠后图标区域的宽度:

php
use Filament\Panel;

public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->sidebarCollapsibleOnDesktop()
        ->collapsedSidebarWidth('9rem');
}

高级导航自定义

可以在 配置 中调用 navigation() 方法。它允许你构建自定义导航,覆盖 Filament 自动生成的项。此 API 旨在让你完全控制导航。

注册自定义导航项

若要注册导航项,请调用 items() 方法:

php
use App\Filament\Pages\Settings;
use App\Filament\Resources\Users\UserResource;
use Filament\Navigation\NavigationBuilder;
use Filament\Navigation\NavigationItem;
use Filament\Pages\Dashboard;
use Filament\Panel;
use Filament\Support\Icons\Heroicon;
use function Filament\Support\original_request;

public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->navigation(function (NavigationBuilder $builder): NavigationBuilder {
            return $builder->items([
                NavigationItem::make('Dashboard')
                    ->icon(Heroicon::OutlinedHome)
                    ->isActiveWhen(fn (): bool => original_request()->routeIs('filament.admin.pages.dashboard'))
                    ->url(fn (): string => Dashboard::getUrl()),
                ...UserResource::getNavigationItems(),
                ...Settings::getNavigationItems(),
            ]);
        });
}
自定义导航项自定义导航项

注册自定义导航分组

若要注册分组,可以调用 groups() 方法:

php
use App\Filament\Pages\HomePageSettings;
use App\Filament\Resources\Categories\CategoryResource;
use App\Filament\Resources\Pages\PageResource;
use Filament\Navigation\NavigationBuilder;
use Filament\Navigation\NavigationGroup;
use Filament\Panel;

public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->navigation(function (NavigationBuilder $builder): NavigationBuilder {
            return $builder->groups([
                NavigationGroup::make('Website')
                    ->items([
                        ...PageResource::getNavigationItems(),
                        ...CategoryResource::getNavigationItems(),
                        ...HomePageSettings::getNavigationItems(),
                    ]),
            ]);
        });
}

禁用导航

你可以通过向 navigation() 方法传入 false 来完全禁用导航:

php
use Filament\Panel;

public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->navigation(false);
}
已禁用的导航侧栏已禁用的导航侧栏

或者,你可以传入返回布尔值的闭包来动态决定。返回 false 会隐藏导航,返回 true 则渲染默认自动发现的导航项。这适用于引导或设置向导等流程,导航应仅在用户到达特定状态后出现:

php
use Filament\Panel;

public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->navigation(fn (): bool => auth()->user()->hasCompletedOnboarding());
}

禁用顶栏

你可以通过向 topbar() 方法传入 false 来完全禁用顶栏:

php
use Filament\Panel;

public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->topbar(false);
}

替换侧栏和顶栏 Livewire 组件

你可以完全替换用于渲染侧栏和顶栏的 Livewire 组件,将自己的 Livewire 组件类名传给 sidebarLivewireComponent()topbarLivewireComponent() 方法:

php
use App\Livewire\Sidebar;
use App\Livewire\Topbar;
use Filament\Panel;

public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->sidebarLivewireComponent(Sidebar::class)
        ->topbarLivewireComponent(Topbar::class);
}

禁用面包屑

默认布局会显示面包屑,以指示当前页面在应用层级中的位置。

你可以在 配置 中禁用面包屑:

php
use Filament\Panel;

public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->breadcrumbs(false);
}

重新加载侧栏和顶栏

面板中的页面加载后,侧栏和顶栏在你离开该页面,或点击菜单项触发操作之前不会重新加载。你可以通过派发 refresh-sidebarrefresh-topbar 浏览器事件来手动重新加载这些组件以更新它们。

若要从 PHP 派发事件,可以从任何 Livewire 组件(如页面类、关系管理器类或小部件类)调用 $this->dispatch() 方法:

php
$this->dispatch('refresh-sidebar');

当你的代码不在 Livewire 组件内时(例如自定义操作类),可以向闭包函数注入 $livewire 参数,并在其上调用 dispatch()

php
use Filament\Actions\Action;
use Livewire\Component;

Action::make('create')
    ->action(function (Component $livewire) {
        // ...
    
        $livewire->dispatch('refresh-sidebar');
    })

或者,你可以使用 $dispatch() Alpine.js 辅助方法,或原生浏览器的 window.dispatchEvent() 方法从 JavaScript 派发事件:

html
<button x-on:click="$dispatch('refresh-sidebar')" type="button">
    Refresh Sidebar
</button>
javascript
window.dispatchEvent(new CustomEvent('refresh-sidebar'));