news 2026/9/24 17:17:58

shadcn-vue Input OTP 组件实战指南:从安装到表单集成的可访问一次性密码输入

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
shadcn-vue Input OTP 组件实战指南:从安装到表单集成的可访问一次性密码输入
  • UI组件
  • 前端

【免费下载链接】shadcn-vue

Vue port of shadcn-ui

项目地址:https://gitcode.com/gh_mirrors/sh/shadcn-vue
点击查看免费下载

本文围绕 shadcn-vue 官方文档 Input OTP 组件文档 展开,系统讲解基于vue-input-otp封装的一次性密码输入组件的完整用法。你将掌握 CLI/手动两种安装方式、四个子组件的组合规则、pattern自定义输入模式、v-model受控用法,以及如何与 VeeValidate 集成实现带校验的验证码表单,并深入理解其底层实现原理与可访问性设计。

组件概览:四个子组件如何协作

Input OTP 不是单个组件,而是一个由四个子组件组合而成的组件族,它们各司其职:

组件职责关键 Props
InputOTP根组件,承载整体输入逻辑(值管理、焦点、粘贴处理)maxlengthpatternv-modelclass
InputOTPGroup将多个输入槽位分组,控制组内水平排列class
InputOTPSlot单个字符槽位,负责渲染字符、激活态与假光标indexclass
InputOTPSeparator在分组之间插入分隔符(默认渲染减号图标)class

从源码结构看,根组件 InputOTP.vue 直接转发vue-input-otpOTPInputPropsOTPInputEmits,并通过 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?.charslot?.isActiveslot?.hasFakeCaret),并据此渲染字符、激活态边框和闪烁的光标动画animate-caret-blink

安装:CLI 与手动两种方式

方式一:CLI 一键安装(推荐)

在项目根目录执行:

npx shadcn-vue@latest add input-otp

该命令会自动将组件源码写入项目的components/ui/input-otp目录,并同步安装所需依赖。关于 CLI 的更多用法(如 registry 指定、覆盖策略等),可参考仓库中的 CLI 文档。

方式二:手动安装

  1. 安装底层依赖:本组件基于vue-input-otp封装,需要先安装它:

    npm install vue-input-otp

    同时确保项目中已具备 reka-ui(用于useForwardProps/useForwardPropsEmits)、@vueuse/core(用于reactiveOmit)以及@lucide/vue(用于分隔符图标)等运行时依赖。

  2. 复制源码:从仓库中复制 Input OTP 组件族的四个源文件到你的项目中,对应源码位于:

    • InputOTP.vue
    • InputOTPGroup.vue
    • InputOTPSlot.vue
    • InputOTPSeparator.vue
  3. 修正导入路径:将源码中的@/lib/utilscn工具函数)、@/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 个InputOTPSlotindex从 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-otpOTPInput组件,由它在输入阶段对每个字符做正则校验,不符合规则的按键会被拦截,从而在源头保证输入合法性。你也可以传入自定义正则(如/^[0-9]$/)实现更严格的纯数字限制。对应演示见 InputOTPPatternDemo.vue。

高级用法二:分组与分隔符(Separator)

当验证码位数较多时,通常按3-34-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-inputvalidate-on-model-updatesubmitCount > 0(即首次提交后)才开启实时校验,避免用户未输入就报错,这是表单体验设计的常见模式。
  • 无障碍联动:aria-invalid="!!errors.length"将校验状态同步到 ARIA 属性,配合InputOTPSlotaria-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-ringdata-[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

项目地址:https://gitcode.com/gh_mirrors/sh/shadcn-vue
点击查看免费下载
上一篇:Linux 内核 ARM 平台移植指南:解压器与内核符号地址约定详解
下一篇:9Router 配额跟踪与用量监控实战指南:实时追踪 Token 消耗、成本估算与告警机制

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/24 17:17:54

NVIDIA RTX Pro5500新卡上架,黄哥心里有我们吗?

2026 年 9 月&#xff0c;国内算力圈同时发生三件事&#xff1a;RTX 5090 32G 服务器版站上 5 万元、RTX PRO 6000 96G 服务器版报到 18 万元&#xff0c;而 NVIDIA 又静默上架了一张 84GB 的新卡 ——RTX PRO 5500 Blackwell&#xff0c;价格一栏写着"即将推出"。诶…

作者头像 李华
网站建设 2026/9/24 17:15:39

[Linux系统] 进程优先级 | 进程切换 | 内核进程O(1)调度队列

一、进程优先级&#xff1a;谁先拿到 CPU CPU 分配资源的先后顺序是进程优先权&#xff0c;在ps -l中可以看到描述优先级的两个值&#xff1a; PRI&#xff1a;进程优先级&#xff0c;值越小越早被执行NI&#xff1a;nice 值&#xff0c;优先级的修正数值&#xff0c;范围 -20 …

作者头像 李华
网站建设 2026/9/24 17:14:44

Spring Cloud Alibaba Nacos注册中心

一、前言 Nacos是一款集服务发现、服务健康监测、动态配置服务、动态 DNS 服务、服务及其元数据管理于一身的开源软件&#xff0c;这节主要记录Nacos的服务注册发现功能的使用。借助Spring Cloud Alibaba Nacos Discovery&#xff0c;我们可以轻松地使用Spring Cloud编程模型体…

作者头像 李华
网站建设 2026/9/24 17:14:37

Spring AI 中基于 Token 优先级的上下文管理

在Spring AI Agent实际开发中&#xff0c;绝大多数模型问答错乱、RAG检索失效、上下文超限报错、越聊越胡言乱语的问题&#xff0c;根源都不是模型能力不足&#xff0c;而是上下文Token管理失控。本文深度拆解LLM上下文Token四级优先级机制&#xff0c;结合Spring AI工程实战&a…

作者头像 李华
网站建设 2026/9/24 17:14:29

[论文分析]水印攻击水印:重水印作为一种通用移除策略

Watermarks Attack Watermarks: Re-Watermarking as a Generic Removal Strategy 论文重点 本文提出了一种反直觉的发现&#xff1a;水印本身可以成为攻击其他水印的最有效武器。研究者发现&#xff0c;对已加水印的图像简单地再次施加水印&#xff08;即“重水印”&#xff09…

作者头像 李华