表单

Input OTP 验证码输入

用于验证码输入场景的 MoriUI 组件。

资源

导入

import { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot } from 'moriui'

组合方式

InputOTP 基于 Reka PinInput,按索引组合 Slot,并可用 Group 与 Separator 建立视觉分段。

输入类型

MoriUI 使用 type="number" 限制数字,使用 type="text" 接受字母数字;不公开 input-otp 的 pattern/maxLength 字符串 API。

受控状态

v-model 是 string[] 或 number[];每个 InputOTPSlot 的 index 决定对应数组位置。

无效与禁用

disabled 设置在 InputOTP 根;无效样式和辅助技术状态设置在各 InputOTPSlot 的 aria-invalid。

RTL

dir 由 Reka UI 用于左右方向键顺序和输入组阅读方向。

示例

概览

六位数字验证码。

<script setup lang="ts">
  import { ref } from 'vue'
  import { InputOTP, InputOTPGroup, InputOTPSlot } from 'moriui'

  const code = ref<number[]>([1, 2, 3, 4, 5, 6])
</script>

<template>
  <InputOTP v-model="code" type="number" aria-label="六位验证码">
    <InputOTPGroup><InputOTPSlot v-for="index in 6" :key="index" :index="index - 1" /></InputOTPGroup>
  </InputOTP>
</template>

仅数字

type="number" 过滤非数字输入。

<script setup lang="ts">
  import { ref } from 'vue'
  import { InputOTP, InputOTPGroup, InputOTPSlot } from 'moriui'

  const code = ref<number[]>([])
</script>

<template>
  <InputOTP id="digits-only" v-model="code" type="number">
    <InputOTPGroup><InputOTPSlot v-for="index in 6" :key="index" :index="index - 1" /></InputOTPGroup>
  </InputOTP>
</template>

分隔符

Group 与 Separator 将验证码分段。

<script setup lang="ts">
  import { ref } from 'vue'
  import { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot } from 'moriui'

  const code = ref<string[]>([])
</script>

<template>
  <InputOTP v-model="code" aria-label="分组验证码">
    <InputOTPGroup><InputOTPSlot :index="0" /><InputOTPSlot :index="1" /></InputOTPGroup><InputOTPSeparator /><InputOTPGroup><InputOTPSlot :index="2" /><InputOTPSlot :index="3" /></InputOTPGroup><InputOTPSeparator /><InputOTPGroup><InputOTPSlot :index="4" /><InputOTPSlot :index="5" /></InputOTPGroup>
  </InputOTP>
</template>

禁用

disabled 阻止所有验证码格交互。

<script setup lang="ts">
  import { ref } from 'vue'
  import { InputOTP, InputOTPGroup, InputOTPSlot } from 'moriui'

  const code = ref<number[]>([1, 2, 3, 4, 5, 6])
</script>

<template>
  <InputOTP
    v-model="code"
    disabled
    type="number"
    aria-label="禁用验证码"
  >
    <InputOTPGroup><InputOTPSlot v-for="index in 6" :key="index" :index="index - 1" /></InputOTPGroup>
  </InputOTP>
</template>

受控

数组 v-model 实时展示当前值。

请输入一次性验证码。

<script setup lang="ts">
  import { ref } from 'vue'
  import { InputOTP, InputOTPGroup, InputOTPSlot } from 'moriui'

  const code = ref<number[]>([])
</script>

<template>
  <div class="space-y-2">
    <InputOTP v-model="code" type="number" aria-label="受控验证码">
      <InputOTPGroup><InputOTPSlot v-for="index in 6" :key="index" :index="index - 1" /></InputOTPGroup>
    </InputOTP><p class="text-center text-sm">
      {{ code.length ? `已输入:${code.join('')}` : '请输入一次性验证码。' }}
    </p>
  </div>
</template>

无效

每个 Slot 使用 aria-invalid。

<script setup lang="ts">
  import { ref } from 'vue'
  import { InputOTP, InputOTPGroup, InputOTPSlot } from 'moriui'

  const code = ref<number[]>([0, 0, 0, 0, 0, 0])
</script>

<template>
  <InputOTP v-model="code" type="number" aria-label="无效验证码">
    <InputOTPGroup>
      <InputOTPSlot
        v-for="index in 6"
        :key="index"
        :index="index - 1"
        aria-invalid="true"
      />
    </InputOTPGroup>
  </InputOTP>
</template>

四位 PIN

四个索引组成常见 PIN 输入。

<script setup lang="ts">
  import { ref } from 'vue'
  import { InputOTP, InputOTPGroup, InputOTPSlot } from 'moriui'

  const code = ref<number[]>([])
</script>

<template>
  <InputOTP v-model="code" type="number" aria-label="四位 PIN">
    <InputOTPGroup><InputOTPSlot v-for="index in 4" :key="index" :index="index - 1" /></InputOTPGroup>
  </InputOTP>
</template>

字母数字

type="text" 接受字母与数字。

<script setup lang="ts">
  import { ref } from 'vue'
  import { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot } from 'moriui'

  const code = ref<string[]>([])
</script>

<template>
  <InputOTP v-model="code" type="text" aria-label="字母数字验证码">
    <InputOTPGroup><InputOTPSlot v-for="index in 3" :key="index" :index="index - 1" /></InputOTPGroup><InputOTPSeparator /><InputOTPGroup><InputOTPSlot v-for="index in 3" :key="index + 3" :index="index + 2" /></InputOTPGroup>
  </InputOTP>
</template>

表单

验证卡片组合标签、重发与提交操作。

验证登录
输入发送到 [email protected] 的验证码。

无法访问邮箱?请联系支持团队。

<script setup lang="ts">
  import { ref } from 'vue'
  import { RefreshCw } from '@lucide/vue'
  import {
    Button,
    Card,
    CardContent,
    CardDescription,
    CardFooter,
    CardHeader,
    CardTitle,
    Field,
    FieldDescription,
    FieldLabel,
    InputOTP,
    InputOTPGroup,
    InputOTPSeparator,
    InputOTPSlot
  } from 'moriui'

  const code = ref<number[]>([])
</script>

<template>
  <Card class="w-full max-w-md">
    <CardHeader><CardTitle>验证登录</CardTitle><CardDescription>输入发送到 [email protected] 的验证码。</CardDescription></CardHeader><CardContent>
      <Field>
        <div class="flex items-center justify-between">
          <FieldLabel for="verification-code">
            验证码
          </FieldLabel><Button size="xs" variant="outline">
            <RefreshCw />重新发送
          </Button>
        </div><InputOTP
          id="verification-code"
          v-model="code"
          required
          type="number"
        >
          <InputOTPGroup><InputOTPSlot v-for="index in 3" :key="index" :index="index - 1" /></InputOTPGroup><InputOTPSeparator /><InputOTPGroup><InputOTPSlot v-for="index in 3" :key="index + 3" :index="index + 2" /></InputOTPGroup>
        </InputOTP><FieldDescription>无法访问邮箱?请联系支持团队。</FieldDescription>
      </Field>
    </CardContent><CardFooter>
      <Button class="w-full" type="submit">
        验证
      </Button>
    </CardFooter>
  </Card>
</template>

RTL

验证码在 RTL 方向中保持正确键盘顺序。

<script setup lang="ts">
  import { ref } from 'vue'
  import { Field, FieldLabel, InputOTP, InputOTPGroup, InputOTPSlot } from 'moriui'

  const code = ref<number[]>([1, 2, 3, 4, 5, 6])
</script>

<template>
  <Field dir="rtl" class="w-full max-w-xs">
    <FieldLabel for="otp-rtl">
      رمز التحقق
    </FieldLabel><InputOTP
      id="otp-rtl"
      v-model="code"
      dir="rtl"
      type="number"
    >
      <InputOTPGroup><InputOTPSlot v-for="index in 6" :key="index" :index="index - 1" /></InputOTPGroup>
    </InputOTP>
  </Field>
</template>

API

属性 类型 默认值 说明
InputOTP v-modelstring[] | number[][]按 Slot index 排列的验证码值。
InputOTP type'text' | 'number''text'文本或数字输入模式,并决定 v-model 数组类型。
InputOTP otpbooleantrue启用一次性验证码自动填充与顺序输入。
InputOTP disabled / requiredbooleanfalse整体禁用与表单必填语义。
InputOTP maskbooleanfalse以密码形式隐藏每格内容。
InputOTP @complete(value: string[] | number[]) => void所有已渲染 Slot 填满时触发。
InputOTPSlot indexnumber必填的数组位置。

无障碍说明

InputOTP 复用 Reka PinInput 的粘贴分发、自动前进、退格、方向键与隐藏表单输入。根组件需要标签或 aria-label;禁用设置在根,无效状态设置在对应 Slot,并提供可读错误说明。

关联组件