ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Filament 导航系统完全指南:从自动注册到完全自定义

Filament 导航系统完全指南:从自动注册到完全自定义 Filament 导航系统完全指南从自动注册到完全自定义【免费下载链接】filamentA powerful open-source UI framework for Laravel • Build and ship apps admin panels fast with Livewire项目地址: https://gitcode.com/GitHub_Trending/fi/filamentFilament 面板Panel默认会为每一个 资源、自定义页面 与 簇Clusters 自动生成导航项。本文以docs/06-navigation/01-overview.md为骨架逐项讲解标签、图标、排序、徽标、分组、折叠侧边栏、顶部导航与自定义构建器等全部导航能力并结合 packages/panels/src/Navigation 下的源码说明底层实现。读完本文你将掌握一套从开箱即用到像素级可控的 Filament 面板导航定制方案。一、导航的自动注册机制默认情况下Filament 会为面板中发现的每个资源、自定义页面和簇自动注册导航项。这些类中定义了大量静态属性与方法覆盖这些成员即可定制对应导航项的行为。从源码结构看这一自动发现 注册流程由 NavigationManager.php 完成mountNavigation()会依次遍历面板注册的页面getPages()、页面配置、资源getResources()与资源配置并调用各自的registerNavigationItems()方法最终把导航项收集到getNavigation()中统一处理过滤、排序、分组。如果你需要给应用增加第二层导航可以直接使用簇——它是资源与页面的逻辑分组能有效缩小侧边栏的体积。二、定制导航项标签导航标签默认由资源或页面的类名推导生成。最简单的做法是设置$navigationLabel属性protected static ?string $navigationLabel Custom Navigation Label;也可以覆写getNavigationLabel()方法适合需要动态计算如多语言翻译的场景public static function getNavigationLabel(): string { return Custom Navigation Label; }两种方式效果等价属性适用于静态文案方法适用于依赖运行时上下文如__()翻译或权限判断的场景。三、定制导航项图标导航项图标通过覆写$navigationIcon属性完成。从 Filament v4 起推荐直接使用Filament\Support\Icons\Heroicon枚举而不是裸字符串类名这样能获得完整的 IDE 类型提示与编译期检查use BackedEnum; use Filament\Support\Icons\Heroicon; protected static string | BackedEnum | null $navigationIcon Heroicon::OutlinedDocumentText;一个值得注意的细节如果同一导航组内的所有项都设置$navigationIcon null这些项会在组标签下方以竖条样式连成一体营造更紧凑的视觉。3.1 激活态切换图标可以为当前激活项单独指定一个图标通过$activeNavigationIcon属性实现例如页面处于激活状态时切换到实心Solid风格的图标use BackedEnum; use Filament\Support\Icons\Heroicon; protected static string | BackedEnum | null $activeNavigationIcon Heroicon::OutlinedDocumentText;四、排序导航项导航项默认按名称字母序排列。通过$navigationSort属性可以手动指定顺序数值越小越靠前升序protected static ?int $navigationSort 3;在 NavigationManager.php 的get()中导航项会先经isVisible()过滤再通过sortBy(fn (NavigationItem $item): int $item-getSort())排序而 NavigationItem.php 的getSort()在未设置时返回-1这保证了未显式排序的项恒排在已设置$navigationSort的项之前。五、为导航项添加徽标Badge徽标适合展示统计信息例如记录总数。覆写getNavigationBadge()并返回内容即可public static function getNavigationBadge(): ?string { return static::getModel()::count(); }徽标默认使用面板主色渲染。需要根据数值切换语义颜色时从getNavigationBadgeColor()返回danger、gray、info、primary、success或warning之一public static function getNavigationBadgeColor(): ?string { return static::getModel()::count() 10 ? warning : primary; }徽标的悬浮提示Tooltip既可以用属性声明protected static ?string $navigationBadgeTooltip The number of users;也可以从方法动态返回public static function getNavigationBadgeTooltip(): ?string { return The number of users; }底层上NavigationItem.php 中徽标颜色支持string | array | Closure三种形态badge(string | Closure $badge, string | array | Closure | null $color null)方法允许同时传入徽标内容与颜色数组形态可用于 Tailwind 下的多断点配色。六、分组导航项在资源与自定义页面类上设置$navigationGroup即可将导航项归组use UnitEnum; protected static string | UnitEnum | null $navigationGroup Settings;同一组的所有项会显示在同一组标签本例为 Settings之下未分组的项始终保持在导航顶部区域。注意属性的联合类型包含UnitEnum——这正是后面用枚举注册导航组的入口。6.1 将导航项挂在其他项之下父子层级设置$navigationParentItem属性可把当前项变成另一个导航项的子项。父项既可以用类引用也可以用标签字符串引用use App\Filament\Resources\Notifications\NotificationResource; use UnitEnum; protected static ?string $navigationParentItem NotificationResource::class; protected static string | UnitEnum | null $navigationGroup Settings;按标签引用use UnitEnum; protected static ?string $navigationParentItem Notifications; protected static string | UnitEnum | null $navigationGroup Settings;也可以覆写getNavigationParentItem()动态决定父项——既返回类名use App\Filament\Resources\Notifications\NotificationResource; public static function getNavigationParentItem(): ?string { return NotificationResource::class; }也可以返回翻译后的标签public static function getNavigationParentItem(): ?string { return __(filament/navigation.groups.settings.items.notifications); }关键约束父项与子项必须属于同一个导航组。如果父项定义了导航组子项也必须定义相同的导航组否则无法识别父项——无论你用类名还是标签引用都一样。这与 NavigationManager.php 的实现一致子项按getParentItem()分组后会在同组内查找getKey()或getLabel()与父项键匹配的项把子项合并进父项的childItems再按getSort()排序。提示如果你发现自己需要第三层导航应当优先考虑改用簇Clusters。簇是资源与自定义页面的逻辑分组拥有自己独立的导航比手工堆叠父子层级更规范。6.2 定制导航组navigationGroups()在面板配置中调用navigationGroups()按顺序传入NavigationGroup对象即可深度定制导航组——包括自定义图标与默认折叠状态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()并让 Settings 组默认collapsed()折叠。从 NavigationGroup.php 的源码可以看到collapsed()会自动连带调用collapsible()保证可折叠状态与已折叠状态一致。6.2.1 排序导航组navigationGroups()本身就是在定义组的展示顺序。如果只想重排顺序而无需完整对象直接按新顺序传入组标签即可$panel -navigationGroups([ Shop, Blog, Settings, ])底层排序逻辑位于 NavigationManager.php未分组项恒排最前getLabel()为空返回-1其余组按在注册数组中的下标排序未注册的组排到所有已注册组之后。6.2.2 让导航组不可折叠导航组默认都是可折叠的。可以在组对象上调用collapsible(false)禁用use Filament\Navigation\NavigationGroup; use Filament\Support\Icons\Heroicon; NavigationGroup::make() -label(Settings) -icon(Heroicon::OutlinedCog6Tooth) -collapsible(false);也可以全局关闭所有组的折叠能力在面板配置中设置use Filament\Panel; public function panel(Panel $panel): Panel { return $panel // ... -collapsibleNavigationGroups(false); }源码依据HasSidebartrait 中$hasCollapsibleNavigationGroups的默认值为true见 HasSidebar.php而 NavigationGroup.php 的isCollapsible()在未显式设置时回落到filament()-hasCollapsibleNavigationGroups()——这就是组级设置优先、面板级设置兜底的机制。6.2.3 给导航组添加额外 HTML 属性可以把额外属性合并到导航组的外层 DOM 元素上通过extraSidebarAttributes()与extraTopbarAttributes()传入键值对数组NavigationGroup::make() -extraSidebarAttributes([class featured-sidebar-group]), -extraTopbarAttributes([class featured-topbar-group]),其中extraSidebarAttributes()作用于侧边栏中的导航组元素extraTopbarAttributes()仅作用于使用顶部导航时顶栏导航组的下拉容器。对应 trait 位于 packages/panels/src/Navigation/Concerns适合做 CSS 钩子如高亮特定分组。6.3 用枚举注册导航组使用枚举类注册导航组可以把组的标签、图标和顺序收敛到单一文件中无需在面板配置中重复注册。定义带 case 的枚举即可case 的定义顺序即导航组的展示顺序enum NavigationGroup { case Shop; case Blog; case Settings; }资源或自定义页面上把$navigationGroup设为枚举 caseprotected static string | UnitEnum | null $navigationGroup NavigationGroup::Shop;实现HasLabel接口可以自定义每组标签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接口可以自定义每组图标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, }; } }源码印证在 NavigationGroup.php 中fromEnum()会检查 case 是否实现HasLabel/HasIcon/Collapsible接口并据此设置组的标签、图标与折叠行为同时 HasNavigation.php 支持直接把枚举类名字符串传给navigationGroups()内部会用array_reduce遍历$groups::cases()批量转换。七、桌面端可折叠侧边栏移动端默认可折叠侧边栏。若希望桌面端也支持折叠在面板配置中启用use Filament\Panel; public function panel(Panel $panel): Panel { return $panel // ... -sidebarCollapsibleOnDesktop(); }默认折叠后图标仍然显示。若希望完全折叠隐藏图标、只留一条细边栏使用sidebarFullyCollapsibleOnDesktop()use Filament\Panel; public function panel(Panel $panel): Panel { return $panel // ... -sidebarFullyCollapsibleOnDesktop(); }从 HasSidebar.php 可看到两者的状态互不干扰并且都支持传Closure做动态开关。7.1 折叠侧边栏下的导航组本节仅适用于sidebarCollapsibleOnDesktop()sidebarFullyCollapsibleOnDesktop()是整体隐藏侧边栏不涉及下述设计。桌面端折叠侧边栏常与导航组搭配使用但有两个默认局限折叠后空间不足各组标签默认会被隐藏即使组本身是可折叠的折叠侧边栏中所有项仍然全部显示——因为没有组标签可供点击展开。解决方案是给导航组对象传入icon()一旦定义了图标折叠侧边栏将始终显示图标而非子项点击图标会在侧边旁弹出下拉展示该组下的所有项从而获得极简的侧边栏外观。需要留意的是给导航组设置了图标后展开状态下子项自身的图标不再显示以保持层级清晰、设计克制但在折叠侧边栏的下拉中子项图标仍会显示——因为此时层级关系已由下拉本身表达清楚了。八、注册自定义导航项通过面板配置的navigationItems()可以注册全新的导航项。NavigationItem::make()链式调用各方法支持外部链接、动态标签、动态 URL 与激活判定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)), // ... ]); }几个值得展开的方法见 NavigationItem.phpurl()支持传入shouldOpenInNewTab决定是否新窗口打开源码注释还特别指出会对来自用户输入的 URL 做校验防止javascript:协议注入 XSSNavigationItem.phpisActiveWhen()接收闭包判断当前项是否处于激活态NavigationItem.phpgroup()、parentItem()、sort()、badge()、activeIcon()与资源/页面上的属性一一对应getKey()在未显式设置key()时回落到getLabel()NavigationItem.php这正是父子导航按标签匹配的底层依据。8.1 条件隐藏导航项visible()与hidden()接收一个布尔条件或闭包用于按需显示/隐藏use Filament\Navigation\NavigationItem; NavigationItem::make(Analytics) -visible(fn(): bool auth()-user()-can(view-analytics)) // or -hidden(fn(): bool ! auth()-user()-can(view-analytics)),实现上hidden()与visible()分别写入isHidden/isVisible状态isHidden()在任一为真时返回真NavigationItem.php该结果在NavigationManager::get()的过滤阶段与NavigationBuilder::getNavigation()中都会被再次消费。九、禁用资源或页面的导航项如果不希望某资源或页面出现在导航中protected static bool $shouldRegisterNavigation false;或覆写shouldRegisterNavigation()方法做动态判断public static function shouldRegisterNavigation(): bool { return false; }安全警告shouldRegisterNavigation()只是从侧边栏隐藏链接并不能阻止用户直接输入 URL 访问。要真正限制访问必须使用资源授权或页面授权如canAccess()。十、使用顶部导航Filament 默认使用侧边栏导航。需要顶部导航时在面板配置中启用use Filament\Panel; public function panel(Panel $panel): Panel { return $panel // ... -topNavigation(); }启用后导航组将作为顶栏下拉菜单渲染这也是extraTopbarAttributes()生效的场景。相关方法定义于 HasTopNavigation.php。十一、定制侧边栏宽度sidebarWidth()接收任意 CSS 长度值控制侧边栏宽度use Filament\Panel; public function panel(Panel $panel): Panel { return $panel // ... -sidebarWidth(40rem); }如果同时启用了sidebarCollapsibleOnDesktop()还可以用collapsedSidebarWidth()定制折叠后图标栏的宽度use Filament\Panel; public function panel(Panel $panel): Panel { return $panel // ... -sidebarCollapsibleOnDesktop() -collapsedSidebarWidth(9rem); }默认值可在 HasSidebar.php 中确认侧边栏默认20rem折叠侧边栏默认4.5rem。两个方法均支持传Closure以动态计算宽度。十二、高级导航定制NavigationBuildernavigation()方法是 Filament 导航的高级入口它允许你用NavigationBuilder完全替换自动生成的导航实现像素级控制。在 HasNavigation.php 中navigation()接受Closure | bool当闭包返回NavigationBuilder实例时get()会直接走buildNavigation()分支见 NavigationManager.php完全跳过自动发现流程。12.1 注册自定义导航项items()方法接收NavigationItem数组。可以混用手动构建的项与getNavigationItems()拉取的资源/页面导航项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(), ]); }); }注意 NavigationBuilder.php 的getNavigation()会把未分组的items()自动包进一个无标签的NavigationGroup并置于最前同时会过滤掉组内所有子项都不可见的空组。12.2 注册自定义导航组groups()方法接收NavigationGroup数组每个组通过items()挂载自己的导航项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(), ]), ]); }); }NavigationBuilder还提供流式 APIitem()追加单项、group()追加组可同时传入组内 items 与collapsible开关所有方法均返回$this以支持链式调用NavigationBuilder.php。12.3 完全禁用导航向navigation()传false即可整体隐藏导航use Filament\Panel; public function panel(Panel $panel): Panel { return $panel // ... -navigation(false); }也可以传入返回布尔的闭包做动态控制返回false隐藏导航返回true渲染默认自动发现的导航项。典型场景是引导流程onboarding或安装向导——用户完成特定状态前不显示导航use Filament\Panel; public function panel(Panel $panel): Panel { return $panel // ... -navigation(fn (): bool auth()-user()-hasCompletedOnboarding()); }该机制在 HasNavigation.php 的resolveNavigationBuilder()中实现闭包经容器调用后若返回NavigationBuilder则用之否则强转为布尔。12.4 禁用顶栏向topbar()传false可完全禁用顶栏use Filament\Panel; public function panel(Panel $panel): Panel { return $panel // ... -topbar(false); }12.5 替换侧边栏与顶栏的 Livewire 组件侧边栏与顶栏本质上是 Livewire 组件可以通过sidebarLivewireComponent()与topbarLivewireComponent()传入自定义组件类名整体替换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); }若未指定默认使用Filament\Livewire\Sidebar见 HasSidebar.php顶栏组件同理定义于 HasTopbar.php。十三、禁用面包屑默认布局会在页面顶部显示面包屑标明当前页面在应用层级中的位置。可在面板配置中禁用use Filament\Panel; public function panel(Panel $panel): Panel { return $panel // ... -breadcrumbs(false); }方法定义于 HasBreadcrumbs.php同样支持闭包动态开关。十四、手动刷新侧边栏与顶栏页面加载后侧边栏与顶栏在离开页面或点击菜单项触发动作之前不会自动刷新。需要手动更新时可派发refresh-sidebar或refresh-topbar浏览器事件。从 PHP 派发在任何 Livewire 组件页面类、关系管理器类或组件类内调用$this-dispatch()$this-dispatch(refresh-sidebar);在非 Livewire 上下文中例如自定义 Action 类把$livewire作为闭包参数注入再调用dispatch()use Filament\Actions\Action; use Livewire\Component; Action::make(create) -action(function (Component $livewire) { // ... $livewire-dispatch(refresh-sidebar); })从 JavaScript 派发使用 Alpine.js 的$dispatch()辅助方法button x-on:click$dispatch(refresh-sidebar) typebutton Refresh Sidebar /button或使用浏览器原生window.dispatchEvent()window.dispatchEvent(new CustomEvent(refresh-sidebar));结语Filament 的导航系统遵循约定优于配置的设计资源、页面与簇开箱即得导航项静态属性即可覆盖绝大多数场景当需求超出自动生成的范围时navigationGroups()、navigationItems()与navigation()NavigationBuilder提供了三个递进的自定义层级配合折叠侧边栏、顶部导航与自定义 Livewire 组件足以应对从管理后台到引导向导的任意布局需求。相关底层实现可继续查阅 packages/panels/src/Navigation 目录以及面板配置与图标指南。【免费下载链接】filamentA powerful open-source UI framework for Laravel • Build and ship apps admin panels fast with Livewire项目地址: https://gitcode.com/GitHub_Trending/fi/filament创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表