- UI组件
- 前端
【免费下载链接】shadcn-vue
Vue port of shadcn-ui
本文围绕 shadcn-vue 官方文档 Input OTP 组件文档 展开,系统讲解基于vue-input-otp封装的一次性密码输入组件的完整用法。你将掌握 CLI/手动两种安装方式、四个子组件的组合规则、pattern自定义输入模式、v-model受控用法,以及如何与 VeeValidate 集成实现带校验的验证码表单,并深入理解其底层实现原理与可访问性设计。
组件概览:四个子组件如何协作
Input OTP 不是单个组件,而是一个由四个子组件组合而成的组件族,它们各司其职:
| 组件 | 职责 | 关键 Props |
|---|---|---|
InputOTP | 根组件,承载整体输入逻辑(值管理、焦点、粘贴处理) | maxlength、pattern、v-model、class |
InputOTPGroup | 将多个输入槽位分组,控制组内水平排列 | class |
InputOTPSlot | 单个字符槽位,负责渲染字符、激活态与假光标 | index、class |
InputOTPSeparator | 在分组之间插入分隔符(默认渲染减号图标) | class |
从源码结构看,根组件 InputOTP.vue 直接转发vue-input-otp的OTPInputProps与OTPInputEmits,并通过 reka-ui 的useForwardPropsEmits做属性/事件转发,同时将class单独剥离后拼接到容器类上:
const props = defineProps<OTPInputProps & { class?: HTMLAttributes["class"] }>() const emits = defineEmits<OTPInputEmits>() const delegatedProps = reactiveOmit(props, "class") const forwarded = useForwardPropsEmits(delegatedProps, emits)而 InputOTPSlot.vue 通过useVueOTPContext()获取 OTP 上下文,依据传入的index读取对应槽位的状态(slot?.char、slot?.isActive、slot?.hasFakeCaret),并据此渲染字符、激活态边框和闪烁的光标动画animate-caret-blink。
安装:CLI 与手动两种方式
方式一:CLI 一键安装(推荐)
在项目根目录执行:
npx shadcn-vue@latest add input-otp该命令会自动将组件源码写入项目的components/ui/input-otp目录,并同步安装所需依赖。关于 CLI 的更多用法(如 registry 指定、覆盖策略等),可参考仓库中的 CLI 文档。
方式二:手动安装
安装底层依赖:本组件基于
vue-input-otp封装,需要先安装它:npm install vue-input-otp同时确保项目中已具备 reka-ui(用于
useForwardProps/useForwardPropsEmits)、@vueuse/core(用于reactiveOmit)以及@lucide/vue(用于分隔符图标)等运行时依赖。复制源码:从仓库中复制 Input OTP 组件族的四个源文件到你的项目中,对应源码位于:
- InputOTP.vue
- InputOTPGroup.vue
- InputOTPSlot.vue
- InputOTPSeparator.vue
修正导入路径:将源码中的
@/lib/utils(cn工具函数)、@/registry/new-york-v4/ui/input-otp等导入路径改为与你项目结构一致的相对路径。
基本用法:六位验证码
最典型的使用方式是分组渲染槽位,InputOTP通过maxlength决定总位数,InputOTPGroup负责将槽位分组(组间可插入分隔符):
<script setup lang="ts"> import { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, } from '@/components/ui/input-otp' </script> <template> <InputOTP v-model="value" :maxlength="6"> <InputOTPGroup> <InputOTPSlot :index="0" /> <InputOTPSlot :index="1" /> <InputOTPSlot :index="2" /> </InputOTPGroup> <InputOTPSeparator /> <InputOTPGroup> <InputOTPSlot :index="3" /> <InputOTPSlot :index="4" /> <InputOTPSlot :index="5" /> </InputOTPGroup> </InputOTP> </template>要点说明:
maxlength与槽位数需一致:maxlength="6"表示验证码长度为 6,因此渲染了 6 个InputOTPSlot(index从 0 到 5)。index必须唯一且连续:每个槽位通过index关联到底层 OTP 上下文中的对应位置,重复或跳号会导致输入错位。v-model双向绑定:绑定值即用户最终输入的验证码字符串,提交表单时直接读取即可。- 完整的可运行示例可参考 InputOTPDemo.vue。
高级用法一:自定义输入模式(pattern)
默认情况下槽位接受任意字符。当业务要求限制字符类型(如只允许数字、或数字+字母)时,可通过pattern属性传入正则。vue-input-otp内置了常用正则常量,例如REGEXP_ONLY_DIGITS_AND_CHARS(仅数字和字母):
<script setup lang="ts"> import { REGEXP_ONLY_DIGITS_AND_CHARS } from 'vue-input-otp' // ... </script> <template> <InputOTP maxlength="6" :pattern="REGEXP_ONLY_DIGITS_AND_CHARS" > <InputOTPGroup> <InputOTPSlot :index="0" /> <!-- ... --> </InputOTPGroup> </InputOTP> </template>从底层实现看,pattern会透传到vue-input-otp的OTPInput组件,由它在输入阶段对每个字符做正则校验,不符合规则的按键会被拦截,从而在源头保证输入合法性。你也可以传入自定义正则(如/^[0-9]$/)实现更严格的纯数字限制。对应演示见 InputOTPPatternDemo.vue。
高级用法二:分组与分隔符(Separator)
当验证码位数较多时,通常按3-3或4-4分组以提升可读性。使用<InputOTPSeparator />在组间插入分隔元素:
<template> <InputOTP maxlength="4"> <InputOTPGroup> <InputOTPSlot :index="0" /> <InputOTPSlot :index="1" /> </InputOTPGroup> <InputOTPSeparator /> <InputOTPGroup> <InputOTPSlot :index="2" /> <InputOTPSlot :index="3" /> </InputOTPGroup> </InputOTP> </template>从源码看,InputOTPSeparator.vue 渲染一个带role="separator"的容器,默认通过MinusIcon(来自@lucide/vue)展示减号分隔符;你也可以通过默认插槽传入自定义分隔内容(如/或-文本)。完整演示见 InputOTPSeparatorDemo.vue。
高级用法三:受控组件(v-model)
InputOTP支持标准的v-model指令,你可以完全掌控输入值,例如在值变化时触发校验、联动「重发验证码」倒计时,或实现「输入满 6 位自动提交」等交互:
<script setup lang="ts"> import { ref, watch } from 'vue' const value = ref('') watch(value, (v) => { if (v.length === 6) { // 自动提交验证码 } }) </script>对应演示见 InputOTPControlledDemo.vue。
高级用法四:接入表单校验(VeeValidate)
Input OTP 可以无缝嵌入表单校验体系。官方文档以 VeeValidate 为例:通过VeeField包裹输入,将componentField透传给InputOTP,配合 Zod schema 实现「最少 6 位」等校验规则,错误信息通过FieldError展示:
<script setup lang="ts"> import { toTypedSchema } from '@vee-validate/zod' import { useForm, Field as VeeField } from 'vee-validate' import { z } from 'zod' const formSchema = toTypedSchema( z.object({ pin: z.string().min(6, { message: 'Your one-time password must be 6 characters.', }), }), ) const { handleSubmit, submitCount } = useForm({ validationSchema: formSchema, initialValues: { pin: '' }, }) const onSubmit = handleSubmit((data) => { // 提交 data.pin }) </script> <template> <form class="space-y-6 w-sm" @submit="onSubmit"> <VeeField v-slot="{ componentField, errors }" name="pin" :validate-on-blur="false" :validate-on-input="submitCount > 0" :validate-on-model-update="submitCount > 0" > <Field :data-invalid="!!errors.length"> <FieldLabel for="form-otp-demo-pin"> One-Time Password </FieldLabel> <InputOTP id="form-otp-demo-pin" v-bind="componentField" :maxlength="6" :aria-invalid="!!errors.length" > <InputOTPGroup> <InputOTPSlot :index="0" /> <InputOTPSlot :index="1" /> <InputOTPSlot :index="2" /> <InputOTPSlot :index="3" /> <InputOTPSlot :index="4" /> <InputOTPSlot :index="5" /> </InputOTPGroup> </InputOTP> <FieldDescription> Please enter the one-time password sent to your phone. </FieldDescription> <FieldError v-if="errors.length" :errors="errors" /> </Field> </VeeField> <Button type="submit" form="form-otp-demo"> Submit </Button> </form> </template>实现细节值得注意:
- 校验时机:
validate-on-blur="false"关闭失焦校验,配合validate-on-input与validate-on-model-update在submitCount > 0(即首次提交后)才开启实时校验,避免用户未输入就报错,这是表单体验设计的常见模式。 - 无障碍联动:
:aria-invalid="!!errors.length"将校验状态同步到 ARIA 属性,配合InputOTPSlot中aria-invalid相关的错误态样式(红色边框),让屏幕阅读器与视觉提示保持一致。 - 错误展示:
FieldError仅在存在错误时渲染错误消息,与FieldDescription(正常提示文案)互不干扰。
完整的表单集成示例见 InputOTPFormDemo.vue,它使用了仓库中同目录下的 Field 组件族 与 Button 组件。
可访问性与交互细节:源码层面的实现
Input OTP 的价值不仅在于视觉,更在于「复制粘贴可用、键盘可操作、屏幕阅读器可感知」。这些能力大多由底层vue-input-otp提供,并由 shadcn-vue 的封装层以样式和语义完整承接:
- 粘贴支持:底层
OTPInput默认支持将整串验证码一次性粘贴并自动分配到各槽位,无需逐位手动输入。 - 自动前进/回退:输入一位后焦点自动移到下一位,退格时空位会自动回到前一位。
- 假光标(Fake Caret):在空槽位中渲染一个闪烁的竖线光标(见 InputOTPSlot.vue 中基于
slot?.hasFakeCaret渲染的animate-caret-blink元素),让用户明确感知当前输入位置。 - 激活态样式:当前激活槽位通过
data-active属性驱动高亮(data-[active=true]:border-ring、data-[active=true]:ring-3等),禁用状态通过has-disabled:opacity-50统一降透明度。 - 语义化角色:分隔符带
role="separator",根容器带data-slot="input-otp",便于开发者与测试工具精确定位。
如果你需要完全定制视觉风格,可以直接修改槽位的 Tailwind 类(如调整h-9 w-9的尺寸、圆角rounded-l-md/rounded-r-md的分组首尾处理),分组与分隔逻辑保持不变即可。
小结
shadcn-vue 的 Input OTP 组件以vue-input-otp为内核,通过InputOTP/InputOTPGroup/InputOTPSlot/InputOTPSeparator四个子组件提供了声明式、可组合、可访问的验证码输入方案。无论是简单的六位数字验证码,还是带分隔符、自定义字符模式、实时校验的复杂表单场景,都可以用本文介绍的方式快速落地。官方文档入口位于 Input OTP 文档,源码与演示分别位于 registry/new-york-v4/ui/input-otp 与 components/demo。
- UI组件
- 前端
【免费下载链接】shadcn-vue
Vue port of shadcn-ui
相关推荐
shadcn-svelte Input OTP 组件指南:基于 Bits UI PinInput 的可访问一次性密码输入
shadcn svelte Input OTP 组件指南:基于 Bits UI PinInput 的可访问一次性密码输入 导读 Input OTP 是 shad
UI组件前端CLI开发工具shadcn-vue PIN Input 组件完全指南:从安装到表单集成的实战详解
shadcn vue PIN Input 组件完全指南:从安装到表单集成的实战详解 PIN Input 是 shadcn vue 中用于输入一序列单字符(字母或
UI组件前端Vuetify VOtpInput 组件深度指南:用 v-otp-input 构建 MFA 一次性密码输入
Vuetify VOtpInput 组件深度指南:用 v otp input 构建 MFA 一次性密码输入 本指南围绕 Vuetify 的 VOtpInput
前端UI组件
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考