InputOTP 验证码输入
基础与分组
默认 6 位纯数字。groups 按「每组位数」分组,组间显示分隔符;没有可见标签时写 aria-label。
<pt-input-otp aria-label="验证码"></pt-input-otp>
<pt-input-otp groups="3-3" value="123" aria-label="分组验证码"></pt-input-otp>一排定长的格子,每格一个字符,用于短信 / 邮件 / 两步验证的一次性验证码。
格子只是画出来的,真正接收输入的是盖在格子上的一个透明原生 <input>。所以这些都是原生的: iOS / Android 从短信里自动填充验证码(默认 autocomplete="one-time-code")、粘贴、撤销、输入法; 读屏器看到的也是一个普通的文本框,而不是六个无名的小输入框。
位数与字符规则
位数与字符规则
length 决定位数;pattern 是单个字符要满足的正则,能收数字以外的字符时移动端弹全键盘。不合规则的字符键入时直接吞掉,粘贴时只过滤掉它们。
<pt-input-otp length="4" aria-label="4 位验证码"></pt-input-otp>
<pt-input-otp pattern="[0-9A-Za-z]" groups="4-4" length="8" aria-label="恢复码"></pt-input-otp>length是位数,默认 6。pattern是单个字符要满足的正则(写源码字符串,不带^$),默认[0-9]。写成[0-9]/\d(或[\d]、[0123456789])时移动端弹数字键盘,其余写法一律弹全键盘 —— 组件无法从任意正则证明它只收数字,宁可多给键也不能让键盘上缺了规则允许的字。- 键入不合规则的字符:直接吞掉,值不变。
- 粘贴:只过滤掉不合规则的字符再填入,
123-456得到123456;超出位数的截断。 - 程序赋的
value同样按规则过滤、截断(不发事件)。 - 输入、粘贴、程序赋值都先做 NFKC 归一:输入法打出的全角数字
123按123算。 - 每格只收占一个 UTF-16 码元的字符:emoji 这类字符不论 pattern 怎么写都不收(原生 input 的长度与选区按码元计,收进来会切坏值)。
- 输入法组合中(拼音、假名还没上屏)不过滤、不发事件,上屏后再统一处理。
编辑方式
- 聚焦时高亮下一个待填的格子;已填满时高亮最后一格。
- 输入覆盖当前格,然后跳到下一格。
- ← / → 一次移一格,Home / End 到两端;不能越过第一个空格子。
- Backspace 删当前格,当前格是空的就删前一格;Delete 删当前格,后面的往前补。
- 点哪格就编辑哪格。
格子顺序固定从左到右,RTL 页面里也一样:验证码是一串字符,不随书写方向排列。
事件
| 事件 | 何时触发 |
|---|---|
ptChange | 用户每改一次值(受控值的出口,v-model / ngModel 绑在它上面) |
ptComplete | 值从不满到刚好填满 length 位的那一下;已满时改其中一格不重复触发 |
常见写法是在 ptComplete 里自动提交。按 Enter 会提交所属表单,与原生单行输入框一致, 外层对这次 Enter preventDefault() 可以取消。
尺寸与状态
尺寸与状态
三档尺寸与其它控件同刻度(格子高 24 / 32 / 40)。出错态同时标注 aria-invalid。
<pt-input-otp size="sm" length="4" aria-label="小"></pt-input-otp>
<pt-input-otp size="lg" length="4" aria-label="大"></pt-input-otp>
<pt-input-otp invalid length="4" value="12" aria-label="出错"></pt-input-otp>
<pt-input-otp disabled length="4" value="1234" aria-label="禁用"></pt-input-otp>表单
- 表单里提交的是
value字符串,字段名取name。 required且为空:校验报「必填」。- 填了但不满
length位:不论是否必填,校验都报「请输入完整的 N 位验证码」(tooShort)。 invalid优先于「不满位数」,报自定义错误;空值且必填时仍报「必填」—— 与其它表单控件相同的优先级(必填 > 出错态 > 其余)。reset()回到初值。放进pt-field/pt-field-set时继承它们的禁用、必填与出错态。
无障碍
- 没有可见标签时写
aria-label(包装层里是ariaLabelText),它落在内部的原生 input 上;放进pt-field时自动关联。 - 格子与分隔符对读屏隐藏,读屏器读的是 input 里的完整值。
invalid同时标注aria-invalid,不只靠颜色。
已知限制
密码管理器(1Password、LastPass 等)有时会往输入框末端插一个图标,可能盖住最后一格的一部分;组件不做避让。 需要时按各密码管理器自己的约定关掉它(如 1Password 认宿主上的 data-1p-ignore)。
API
属性
| 属性 | Attribute | 类型 | 默认值 | 说明 |
|---|---|---|---|---|
ariaLabelText | aria-label | string | undefined | — | 无障碍名称:原生 HTML 里写 aria-label,React / Vue / Angular 包装层里属性名是 ariaLabelText。 没有可见 label 时必须给。 |
autocomplete | autocomplete | string | 'one-time-code' | 透传给原生 input 的 autocomplete。默认 `one-time-code`:iOS / Android 据此从短信里取验证码 |
disabled | disabled | boolean | false | 禁用。只反映使用方自己写的值:外层原生 `<fieldset disabled>`、pt-field / pt-field-set 的禁用另经继承通道生效,不改写它 |
groups | groups | string | undefined | — | 分组,写成用 `-` 连接的各组位数,如 `"3-3"`;组与组之间显示分隔符。 各组之和与 `length` 不符时忽略(控制台警告),按一组显示 |
inheritedDisabled | inherited-disabled | boolean | false | 外层 pt-field / pt-field-set 下发的禁用。内部继承通道:由它们以 attribute 维护,不建议使用方直接写。 有效禁用是 `disabled` 与它的并集 |
inheritedInvalid | inherited-invalid | boolean | false | 外层 pt-field 下发的出错态。内部继承通道:由 pt-field 维护,不建议使用方直接写。有效出错是 `invalid` 与它的并集 |
inheritedRequired | inherited-required | boolean | false | 外层 pt-field 下发的必填。内部继承通道:由 pt-field 维护,不建议使用方直接写。有效必填是 `required` 与它的并集 |
invalid | invalid | boolean | false | 出错态。校验信息由使用方展示,组件只负责视觉、aria-invalid 与表单校验状态(customError) |
length | length | number | 6 | 位数 |
name | name | string | undefined | — | 提交表单时的字段名 |
pattern | pattern | string | '[0-9]' | **单个字符**要满足的正则(源码字符串,不写 `^` `$`)。默认只收数字。写成 `[0-9]` / `\d`(或 `[\d]`、 `[0123456789]`)时移动端弹数字键盘,其余写法一律弹全键盘。字母数字写 `"[0-9A-Za-z]"`。 输入先做 NFKC 归一(全角 `1` 按 `1` 算);emoji 这类占两个 UTF-16 码元的字符不论 pattern 都不收 |
readonly | readonly | boolean | false | 只读:能聚焦、能移动高亮格,不能改。值照常随表单提交 |
required | required | boolean | false | 必填:值为空时表单校验报 valueMissing,并标注 aria-required |
size | size | "lg" | "md" | "sm" | 'md' | 尺寸档位。格子高度对应 24 / 32 / 40 的控件刻度 |
value | value | string | '' | 当前值。程序赋的值同样按 `pattern` 与 `length` 过滤 |
事件
| 事件 | detail 类型 | 说明 |
|---|---|---|
ptBlur | void | 内部控件失去焦点 |
ptChange | string | 值变化。用户每改一次就触发一次,程序赋值不触发 |
ptComplete | string | 用户把值填满 `length` 位的那一下触发,detail 是完整的值。已满时再改其中一格不会重复触发 |
ptFocus | void | 内部控件获得焦点 |
方法
| 方法 | 说明 |
|---|---|
setFocus(options?: FocusOptions) => Promise<void> | 聚焦到内部 input,并高亮下一个待填的格子 |
可定制的内部元素(::part())
| part | 说明 |
|---|---|
caret | 空格子上的假光标 |
group | 一组格子 |
input | 盖在格子上的透明原生 input |
separator | 组与组之间的分隔符 |
slot | 单个格子 |
slot-active | 当前高亮的格子(与 slot 同一元素) |
CSS 变量
| 变量 | 说明 |
|---|---|
--pt-input-otp-radius | 每组两端的圆角,默认 --pt-radius-md(sm 档为 --pt-radius-sm) |
--pt-input-otp-slot-width | 格子宽度,默认按「格子高 + 4px」的比例随高度走(默认档 28 / 36 / 44px) |