Questionnaire 多步问答
步骤驱动的表单流程编排组件:步骤头 + 进度(「第 n / m 步」)+ 每步内容面板 + 单步校验门控。适合向导式表单 / 问卷 / 多步结算等场景。校验复用 oas-form 内核——每步面板内放一个 <oas-form>,下一步前对当前步校验、未过不放行;回退不校验、面板常驻不卸载(已填值保留)。
边界:线性门控 + 可选步跳过 +
oas-before-change宿主自定义跳转 + 条件分支走宿主组合通道(hidden数据位 +oas-before-change+oas-values-change,见下文);谓词 / 表达式引擎不内置(宿主持有全量答案数据,分支决策留在宿主可 veto、可测试的应用层)。起始页/完成页暂不内置;草稿持久化请用current+getValues()自行实现。
基础用法
steps 传步骤数组({ key?, title, description?, optional?, hidden? }),每步内容经命名插槽关联:有 key 用 slot="step-<key>",无 key 用 slot="step-<index>"。末步主按钮自动变为「完成」,点击重校全部参与步后派发 oas-submit(detail.values 为跨步汇总值)。取值与校验同口径——只含「参与步」(非 hidden 且未被跳过的步);需全量数据的宿主可读取各面板内的 oas-form 自行汇总。
基础多步问答
信息确认无误后,点击「完成」提交。
单步校验门控
下一步前对当前步 oas-form 校验(委托其 validate()),未过不放行并派发 oas-step-validate(detail: { index, key?, valid, errors });回退不校验(不会被错误困在当前步)。validation="false" 关闭门控(纯导航)。
未填不放行
校验通过后才能到达这一步。
可选步跳过
步骤声明 optional: true 后,导航区出现「跳过本步」按钮:跳过不触发校验、直接前进,派发 oas-skip(detail: { index, key? })。被跳过的步退出参与步集合——其值不进 getValues() / oas-submit、也不再参与提交重校;重新回到该步(回退 / 头部点击 / goto())即恢复参与,reset() 清全部跳过记录。
可选步(Skip)
最后一步。
宿主拦截跳步(oas-before-change)
任何跳步(按钮 / 头部点击 / next() / prev() / goto() / 跳过)前派发可取消事件 oas-before-change(detail: { index, key?, from }),宿主 preventDefault() 可否决——也是自定义分支跳转的挂点。linear="false" 关闭线性禁跳后,头部任意步可点。
preventDefault 否决跳步
第一步内容
第二步内容
确认页(演示中被宿主拦截,进不来)
条件分支(按答案跳步)
条件分支走宿主组合通道,不内置谓词引擎:宿主监听 oas-values-change 拿到答案,按业务规则改写 steps 翻转 hidden 位即生效——被隐藏步退出头部与进度、next() 自动跳过、其值退出取值与校验口径。若翻转的是用户所在步,组件自动把 current 对齐到最近可见步并派发 oas-change(宿主无需再手动跳转)。分支决策留宿主的好处:规则可单测、可在 oas-before-change 里再 veto、避免在组件内重造一套表达式语言。
按答案隐藏/恢复步骤
最后一步。
切换动画(animated)
animated 开启题目切换过渡:新面板按导航方向滑入 + 淡入(前进自行尾侧、后退自行首侧;只动 transform/opacity,合成器友好);prefers-reduced-motion: reduce 用户自动降级为无动画。时长经 CSS 变量 --oas-questionnaire-anim-duration 覆盖(缺省 --oas-transition-base);RTL 布局下滑入方向自动镜像。
animated 切换过渡(点下一步/上一步观察滑入方向)
点「下一步」:本面板按导航方向滑入 + 淡入。
前进与后退的滑入方向相反;回到第一步用「上一步」观察。
最后一步。
键盘快捷导航(shortcuts)
shortcuts 开启键盘切步:Alt+←/→ 在组件内任何位置(含输入框内)切上/下一步,并吞掉默认行为(防浏览器历史导航);裸 ←/→(无修饰键)仅在焦点不在输入框/文本域/可编辑区等控件时切步(不劫持光标与控件自身键盘),点击面板空白处焦点落在组件容器上同样可达。触发走与按钮相同的门控链路(当前步未过校验不放行);物理方向映射(→ 前进 / ← 后退),RTL 不镜像,与浏览器历史键惯例一致。
shortcuts 键盘切步(点进组件内按 Alt+←/→ 试试)
焦点在面板空白处时,裸 ←/→ 也能切步。
最后一步。
受控 current 与方法
current 双向:内部跳步写回属性,外部设置即时同步。命令式方法 next() / prev() / goto(index) / validate() / submit() / getValues() / reset() 覆盖全部导航与取值场景。
事件命名提示:步骤面板内字段自身的
oas-change/oas-input等事件会冒泡穿出 questionnaire。字段oas-change与切步oas-change同名——监听切步时请按detail.index过滤(字段事件不含index);字段值变化推荐监听oas-values-change(detail: { name, value, values })。
外部控制与取值
进度形态
progress-variant 三档:both(默认,文本 + 进度条)/ text(只留「第 n / m 步」)/ bar(只留进度条);progress="false" 整体隐藏。进度条走 role="progressbar" + aria-valuenow/min/max,读屏可朗读。
progress-variant 三档 + 隐藏
both(默认)
文本 + 进度条
丙
text
只留进度文本
丙
bar
只留进度条
丙
回退值保留与重置
面板常驻不卸载(仅切 hidden):前进再回退,已填值不丢。reset() 全部步骤回到初始值、清错误态、回第一步,不派发事件。
回退不丢值 + reset()
点「上一步」回到第一步,已改的值还在。
自定义导航区(hide-header / hide-nav)
hide-header / hide-nav 隐藏内置步骤头 / 导航区,宿主可完全自组合(进度与面板仍由组件驱动)。
hide-nav + 外部按钮驱动
内置导航已隐藏
由外部按钮驱动
纯导航模式(validation / progress / hide-header)
validation="false" 关闭单步门控(纯导航,未填也放行);progress="false" 隐藏进度区;hide-header 隐藏内置步骤头。三者可独立开启,也可组合成极简流程(只剩面板与导航按钮)。
纯导航:无门控 / 无进度 / 无头部
未填也能到达本步(门控已关闭)。
API
方法
| 方法 | 说明 |
|---|---|
next() | 下一步:先对当前步 oas-form 校验(validation="false" 时跳过),通过才前进;异步校验在途时重入直接拒绝。返回 Promise<boolean> |
prev() | 上一步:不校验(回退不被错误困住)。返回 boolean |
goto(index) | 直跳任意步:不走单步门控;越界夹取,落在 hidden 步时向后解析。返回 boolean |
validate() | 校验全部参与步(非 hidden 且未跳过;顺序执行,逐步派发 oas-step-validate),返回 Promise<boolean> |
submit() | 提交:重校全部参与步(含早期步),全过才派发 oas-submit(detail.values 为跨步汇总值,仅参与步——hidden / 已跳过步不计入)。返回 Promise<boolean> |
getValues() | 跨步值汇总(参与步口径,与校验同集合):各参与步 oas-form 当前值按步序深合并(与单表 submit() 的 detail.values 同口径,零交互的 value 属性预填同样计入);需全量数据可读各面板内的 oas-form。返回 Record<string, unknown> |
reset() | 全部步骤 form 重置回初始值、清错误态、current 回 0;不派发任何事件 |
oas-questionnaire
属性
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
animated | 题目切换过渡(opt-in,存在且非 "false" 时开启):新面板按导航方向滑入 + 淡入(只动 transform/opacity);prefers-reduced-motion: reduce 自动降级为无动画;RTL 滑入方向自动镜像 | boolean | — |
current | 当前步索引(0 起,受控双向:内部跳步写回、外部设置即时同步);非法值回落 0,越界夹取 | string | 0 |
finish-text | 末步主按钮文案(覆盖 locale 缺省「完成」) | — | — |
hide-header | 隐藏内置步骤头(宿主自组合) | boolean | — |
hide-nav | 隐藏内置导航区(宿主自组合) | boolean | — |
linear | 线性模式(默认开,linear="false" 关闭):未来步头部禁点;关闭后任意步可点(仍走 oas-before-change 拦截) | string | true |
next-text | 「下一步」按钮文案(覆盖 locale 缺省) | — | — |
prev-text | 「上一步」按钮文案(覆盖 locale 缺省) | — | — |
progress | 进度区显隐(progress="false" 隐藏) | string | true |
progress-variant | 进度形态:both(默认,文本+进度条)/ text / bar;非法值回落 both | string | both |
shortcuts | 键盘快捷导航(opt-in,存在且非 "false" 时开启):Alt+←/→ 在组件内任何位置切上/下一步(吞掉默认行为防浏览器历史导航);裸 ←/→ 仅在焦点不在输入/可编辑控件时生效(不劫持光标) | boolean | — |
size | 尺寸档位:xs/small/medium/large/xl(标题字号密度;非法值回落 medium + 告警一次(同值去重)) | string | medium |
skip-text | 「跳过本步」按钮文案(覆盖 locale 缺省) | — | — |
steps | 步骤数据 JSON [{ key?, title, description?, optional?, hidden? }];非法/空回落 [] | QuestionnaireStep[] | string | [] |
validation | 单步校验门控(默认开,validation="false" 纯导航;submit() 不受此开关影响、始终重校) | string | true |
事件
| 事件 | 说明 |
|---|---|
oas-before-change | 跳步前(按钮 / 头部点击 / next / prev / goto / 跳过)可取消;detail: { index, key?, from },preventDefault() 否决本次跳转 |
oas-change | 切步成功后;detail: { index, key? } |
oas-skip | 跳过可选步时;detail: { index, key? }(跳过不触发校验;被跳过的步退出取值/校验口径,重新进入恢复) |
oas-step-validate | 单步校验结束时;detail: { index, key?, valid, errors } |
oas-submit | 末步完成(全部参与步校验通过);detail: { values }(跨步汇总值,仅参与步——hidden / 已跳过步不计入) |
oas-values-change | 内层 oas-form 值变化事件自然冒泡转发(detail: { name, value, values },values 为该步快照);跨步汇总视图用 getValues() |
插槽
| 名称 | 说明 |
|---|---|
step-<index> | 步骤内容面板(无 key 的步骤按数组下标关联) |
step-<key> | 步骤内容面板(有 key 的步骤);面板内建议放一个 <oas-form> 承载该步字段与校验规则 |
CSS 变量
| CSS 变量 | 说明 | 默认值 |
|---|---|---|
--oas-questionnaire-anim-duration | 切步动画时长(animated 开启时生效) | var(--oas-transition-base, 180ms) |
--oas-questionnaire-nav-gap | 导航区按钮间距 | var(--oas-space-2) |
--oas-questionnaire-progress-bar-bg | 进度条填充色 | var(--oas-color-primary) |
--oas-questionnaire-progress-bg | 进度条轨道底色 | var(--oas-color-bg-hover) |