Filament Schemas 多步骤向导(Wizard)完整指南:从基础用法到源码级定制
【免费下载链接】filamentA powerful open-source UI framework for Laravel • Build and ship apps & admin panels fast with Livewire项目地址: https://gitcode.com/GitHub_Trending/fi/filament
本指南以 Filament 的packages/schemas组件库为核心,系统讲解Filament\Schemas\Components\Wizard多步骤向导组件的全部能力:基础搭建、提交按钮、步骤图标与描述、跳转控制、延迟加载、URL 状态持久化、生命周期钩子与操作按钮定制。读完本文,你将能够在 Laravel + Livewire 应用中独立实现"分步校验、按序推进"的高质量多步骤表单,并能借助源码理解其底层推进逻辑。
一、Wizard 是什么:为有序流程而生的多步表单容器
与 Tabs 标签页 类似,Wizard 允许你将表单拆分为多个步骤(Step),任意时刻只渲染当前步骤,从而大幅减少同时可见的组件数量。它特别适合具有明确时间顺序的业务流程——例如下单(订单 → 配送 → 账单)、注册、多阶段配置等——并且每一步都会在用户推进时单独校验,确保错误在第一时间暴露。
最基础的用法是在Wizard::make()中传入一组Step,每个Step再通过schema()定义自己的字段集合:
use Filament\Schemas\Components\Wizard; use Filament\Schemas\Components\Wizard\Step; Wizard::make([ Step::make('Order') ->schema([ // 订单相关字段... ]), Step::make('Delivery') ->schema([ // 配送相关字段... ]), Step::make('Billing') ->schema([ // 账单相关字段... ]), ])从源码看,Wizard.php 的构造函数接受array<Step> | Closure形式的步骤数组(内部通过steps()方法委托给components()完成注册);Step.php 则以标签字符串为必填构造参数,并在setUp()中自动生成唯一key(规则为slug(标签)::statePath::wizard-step),用于前端 Alpine 组件与 Livewire 请求间的状态关联。
1.1 在面板资源创建页中使用向导
如果你要把向导应用到资源(Resource)的创建流程,官方推荐在创建页类上引入HasWizardtrait,并在getSteps()中返回步骤数组,参见 panel resource 集成文档:
use Filament\Resources\Pages\Concerns\HasWizard; use Filament\Resources\Pages\CreateRecord; use Filament\Schemas\Components\Wizard\Step; class CreatePostUsingWizard extends CreateRecord { use HasWizard; protected static string $resource = PostResource::class; public function getSteps(): array { return [ Step::make('Step 1') ->schema([ TextInput::make('title')->required(), ]), Step::make('Step 2') ->schema([ TextInput::make('content')->required(), ]), ]; } }仓库中的 CreatePostUsingWizard.php 就是该写法的真实示例。这样配置后,表单的提交能力只会在向导的最后一步出现,用户必须依次走完所有步骤。
1.2 在 Action 模态框中渲染向导
同样,你可以在 Action 模态框 内渲染向导:不用schema(),而是定义steps()数组并传入Step对象,即可在模态框中获得分步表单体验。
二、在最后一步渲染提交按钮:submitAction()
默认情况下,向导底部始终显示"上一步/下一步"按钮,而提交按钮由外层表单统一渲染。若你希望提交按钮仅在最后一步出现,使用submitAction()方法,它接受一段 HTML 字符串或一个 Blade 视图:
use Filament\Schemas\Components\Wizard; use Illuminate\Support\HtmlString; Wizard::make([ // ... ])->submitAction(view('order-form.submit-button')) Wizard::make([ // ... ])->submitAction(new HtmlString('<button type="submit">Submit</button>'))更推荐的做法是直接复用 Filament 内置的按钮 Blade 组件<x-filament::button>,通过Blade::render()渲染为字符串后传入:
use Filament\Schemas\Components\Wizard; use Illuminate\Support\Facades\Blade; use Illuminate\Support\HtmlString; Wizard::make([ // ... ])->submitAction(new HtmlString(Blade::render(<<<BLADE <x-filament::button type="submit" size="sm" > Submit </x-filament::button> BLADE)))你也可以把这段组件抽取为独立的 Blade 视图文件以保持整洁。从源码看,submitAction()存储的值会在toEmbeddedHtml()中渲染到向导底部的一个容器里,该容器通过x-bind:class="{ 'fi-hidden': ! isLastStep() }"控制显隐——即只有处于最后一步时才可见。
提示:如果你使用
submitAction()在最后一步渲染提交按钮,请务必确保外层表单确实能够被提交(例如配合资源创建页的HasWizard或模态框的提交机制),否则按钮将没有实际行为。
三、步骤图标:icon()与completedIcon()
3.1 为步骤设置图标
每个步骤都可以设置一个 图标,用于在向导头部(Header 的步骤指示条)展示。使用icon()方法:
use Filament\Schemas\Components\Wizard\Step; use Filament\Support\Icons\Heroicon; Step::make('Order') ->icon(Heroicon::ShoppingBag) ->schema([ // ... ]),3.2 定制"已完成"步骤的图标
当用户走完某一步后,头部会用对勾等图标标记完成状态。你可以用completedIcon()自定义完成态的图标,例如换成一个大拇指:
use Filament\Schemas\Components\Wizard\Step; use Filament\Support\Icons\Heroicon; Step::make('Order') ->completedIcon(Heroicon::HandThumbUp) ->schema([ // ... ]),在 Step.php 中,icon()与completedIcon()的类型签名均为string | BackedEnum | Htmlable | Closure | null,因此除了静态的图标枚举/字符串(如'heroicon-o-user')外,也可以传入闭包按当前状态动态计算。渲染时,Wizard.php 会对已完成步骤优先输出completedIcon(未设置时回退到内置的Heroicon::OutlinedCheck),未完成的当前/未来步骤则输出icon或两位数的步骤序号。
3.3 为步骤添加描述
在步骤标题下方,你还可以显示一行简短描述,使用description()方法:
use Filament\Schemas\Components\Wizard\Step; Step::make('Order') ->description('Review your basket') ->schema([ // ... ]),描述同样支持闭包动态计算,渲染位置在步骤头部的标签下方(见 Step.php 中fi-sc-wizard-header-step-description对应的输出逻辑)。
四、控制向导的启动与导航行为
4.1 设置默认激活步骤:startOnStep()
默认向导从第 1 步开始。若需要从指定步骤加载(例如用户上次未完成,回访时继续),使用startOnStep(),参数为从 1 开始的步骤序号:
use Filament\Schemas\Components\Wizard; Wizard::make([ // ... ])->startOnStep(2)从源码看,getCurrentStepIndex()会以getStartStep() - 1作为初始下标;startOnStep()同样支持闭包。
4.2 允许自由跳转:skippable()
默认情况下向导是顺序推进的:用户必须先校验当前步骤才能进入下一步,也无法点击头部直接跳到未来的步骤。若你想放开限制,使用skippable():
use Filament\Schemas\Components\Wizard; Wizard::make([ // ... ])->skippable()skippable()也接受一个布尔值参数,便于按条件开启:
Wizard::make([ // ... ])->skippable(FeatureFlag::active())其底层行为在 Wizard.php 的nextStep()与goToStep()中非常清晰:
nextStep():当isSkippable()为false时,会对当前步骤依次执行callBeforeValidation()→getChildSchema()->validate()→callAfterValidation(),并顺带用$nextStep?->fillStateWithNull()初始化下一步状态;校验失败(抛出Halt)则直接return,不切换步骤。goToStep():仅当isSkippable()为true时,才允许跳到当前步骤之后的任意步骤;否则只能跳转到已走过的步骤。
4.3 延迟加载昂贵的步骤内容:deferLoading()
如果某个步骤的内容渲染成本很高(例如包含复杂关系表、远程数据等),你不希望它随页面首屏一起渲染,可以给该步骤传入一个Schema对象并调用deferLoading()。这样活动步骤的内容会在进入视口时才加载,后续步骤则要等到用户真正到达时才加载:
use Filament\Forms\Components\TextInput; use Filament\Schemas\Components\Wizard; use Filament\Schemas\Components\Wizard\Step; use Filament\Schemas\Schema; Wizard::make([ Step::make('Account') ->key('accountStep') ->schema( Schema::make() ->components([ TextInput::make('name'), ]) ->deferLoading(), ), Step::make('Confirmation') ->key('confirmationStep') ->schema( Schema::make() ->components([ // ... ]) ->deferLoading(), ), ]) ->key('accountWizard')每一个延迟加载的 schema 都必须有唯一 key。在上例中,步骤上的key()会被其子 schema 继承,而Wizard上的key()用于把它与其他步骤区分开、避免命名冲突。关于延迟加载的完整语义(加载指示器、校验错误自动加载、隐藏组件中的延迟行为、repeater/builder 条目的自动唯一 key 等),参见 Schema 总览的 "Deferring the loading of a child schema" 一节。
4.4 将当前步骤持久化到 URL 查询字符串:persistStepInQueryString()
默认情况下,当前所在步骤不会反映到 URL 中,刷新页面后会回到起始步骤。调用persistStepInQueryString()后,当前步骤会以step为 key 写入 URL 查询字符串:
use Filament\Schemas\Components\Wizard; Wizard::make([ // ... ])->persistStepInQueryString()你也可以传入自定义 key 替换默认的step:
Wizard::make([ // ... ])->persistStepInQueryString('wizard-step')源码层面的实现值得注意:getStartStep()在isStepPersistedInQueryString()为真时,会优先从request()->query($this->getStepQueryStringKey())读取值,并与各步骤的getId()逐一比对,命中则返回对应序号作为起始步骤。也就是说,开启后刷新页面会停留在当前步骤,非常适用于长流程表单的"断点续填"体验。该 key 同样支持闭包动态计算。
五、步骤生命周期钩子:校验前与校验后
你可以在步骤级挂载校验钩子,用afterValidation()和beforeValidation()在校验发生前后执行任意代码:
use Filament\Schemas\Components\Wizard\Step; Step::make('Order') ->afterValidation(function () { // 校验通过后执行... }) ->beforeValidation(function () { // 校验前执行... }) ->schema([ // ... ]),两个钩子回调均支持依赖注入各类工具(utility)。对应的执行点位于 Wizard.php 的nextStep()中:callBeforeValidation()→ 子 schema 的validate()→callAfterValidation(),顺序完全固定。
5.1 阻止下一步加载:抛出Halt异常
如果在afterValidation()或beforeValidation()中抛出Filament\Support\Exceptions\Halt,向导会被迫停留在当前步骤,不再加载下一步——这是实现"校验通过后仍需人工确认才能继续"这类业务的关键机制:
use Filament\Schemas\Components\Wizard\Step; use Filament\Support\Exceptions\Halt; Step::make('Order') ->afterValidation(function () { // ... if (true) { throw new Halt(); } }) ->schema([ // ... ]),nextStep()中正是通过catch (Halt $exception) { return; }来吞掉异常并中止步骤切换的。
六、步骤内的网格布局:columns()
与所有布局组件一样,Step支持columns()方法,用于定制步骤内部的 网格系统。例如让"Order"步骤内的字段以两列排布:
use Filament\Schemas\Components\Wizard; use Filament\Schemas\Components\Wizard\Step; Wizard::make([ Step::make('Order') ->columns(2) ->schema([ // ... ]), // ... ])从 Step.php 的getAllColumns()可以看到,未显式设置列数时,步骤会继承其所在容器的列配置;设置后则覆盖父级。columns()也支持闭包与响应式断点配置。
七、定制向导操作按钮:nextAction()与previousAction()
向导底部的"下一步/上一步"按钮本质上是两个 Action 对象,因此你可以通过传入回调函数来深度定制它们。回调接收$action参数(一个Filament\Actions\Action实例),可调用 Action 文档 中的任意方法:
nextAction():定制"下一步"按钮previousAction():定制"上一步"按钮
例如将下一步按钮改名为"Next step":
use Filament\Actions\Action; use Filament\Schemas\Components\Wizard; Wizard::make([ // ... ]) ->nextAction( fn (Action $action) => $action->label('Next step'), )在 Wizard.php 中,两个 Action 通过setUp()内的registerActions()自动注册(名称分别为next与previous)。默认的下一步按钮带iconPosition(IconPosition::After)与livewireTarget('callSchemaComponentMethod'),会依据步骤数量自动生成一串"从第 N 步推进到下一步"的 Livewire 调用;上一步按钮则默认为灰色button()。通过回调你可以自由修改标签、颜色、图标、禁用状态等。若传入null则会清空已设置的修改器、恢复默认行为。
八、源码视角:一次"下一步"点击背后发生了什么
综合 Wizard.php 与 Step.php,可以梳理出点击"下一步"的完整链路:
- 前端 Alpine 组件
wizardSchemaComponent触发requestNextStep(),调用暴露给 Livewire 的nextStep($currentStepIndex)方法。 - 若
skippable()未开启,服务端取出当前步骤,按beforeValidation → validate → afterValidation顺序执行;任一步骤抛出Halt则中止。 - 校验通过后,
$nextStep?->fillStateWithNull()初始化下一步的空状态,随后通过$livewire->dispatch('next-wizard-step', key: ...)通知前端切换到下一步。 - 前端监听
x-on:next-wizard-step.window事件,只有key匹配时才执行goToNextStep(),确保多向导共存于同一页面时互不干扰。
在 WizardTest.php 中可以看到对这些行为的系统性验证:skippable()的默认值与闭包设置、startOnStep()与getCurrentStepIndex()的计算、persistStepInQueryString()的默认 key('step')与自定义 key、submitAction()/cancelAction()的存取与清空、nextAction()/previousAction()回调对标签的修改与null清空、hiddenHeader()与contained(false)的渲染、以及浏览器端的可访问性与暗色模式测试;StepTest.php 则覆盖了description()、icon()/completedIcon()的字符串/枚举/闭包三种形态、afterValidation()/beforeValidation()的调用与清空,以及formWrapper()等细节。这些测试既是行为的契约,也是你安全定制向导的保障。
九、小结:Wizard 能力速查
| 能力 | 方法 | 是否支持闭包 |
|---|---|---|
| 搭建步骤 | Wizard::make([Step::make(...), ...]) | 步骤数组可为 Closure |
| 最后一步提交按钮 | submitAction() | — |
| 步骤图标 | icon() | ✅ |
| 完成态图标 | completedIcon() | ✅ |
| 步骤描述 | description() | ✅ |
| 默认起始步骤 | startOnStep() | ✅ |
| 允许跳过/自由导航 | skippable() | ✅ |
| 延迟加载步骤内容 | Schema::make()->deferLoading() | ✅ |
| URL 持久化当前步骤 | persistStepInQueryString($key = 'step') | ✅ |
| 步骤校验前后钩子 | beforeValidation()/afterValidation() | 回调可注入 utility |
| 阻止下一步 | 回调内throw new Halt() | — |
| 步骤内网格列数 | columns() | ✅ |
| 定制上/下一步按钮 | previousAction()/nextAction() | 回调接收$action |
在实际项目中,推荐将 Wizard 与资源创建页的HasWizardtrait 或 Action 模态框 组合使用,以自动获得"仅在最后一步可提交"的完整交互;再按需叠加skippable()、persistStepInQueryString()与deferLoading()来打磨长流程表单的体验与性能。
【免费下载链接】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),仅供参考