<script setup lang="ts">
import { NumberInput } from '@hina-ui/vue'
</script>
<template>
<NumberInput :default-value="3" :min="0" :max="10" aria-label="数量" class="w-40" />
</template>
vue
用法
import { NumberInput } from '@hina-ui/vue'
ts
数值输入框由输入区与一组步进按钮组成,v-model 绑定数值。方向键、Page Up 与 Page Down、Home 与 End 以及聚焦时的滚轮都能调整数值。未声明的属性都会传给内部的 input。
<script setup lang="ts">
import { ref } from 'vue'
import { NumberInput } from '@hina-ui/vue'
const quantity = ref(1)
</script>
<template>
<NumberInput v-model="quantity" aria-label="数量" placeholder="数量" class="w-40" />
</template>
vue
示例
范围与步长
min 与 max 限定范围,step 决定每次增减的幅度。默认把输入的值对齐到步长,关闭 stepSnapping 后只限制在范围内。
<script setup lang="ts">
import { ref } from 'vue'
import { NumberInput, Stack } from '@hina-ui/vue'
const rating = ref(2.5)
const pages = ref(12)
</script>
<template>
<Stack class="w-40">
<NumberInput v-model="rating" :min="0" :max="10" :step="0.5" aria-label="评分" />
<NumberInput v-model="pages" :min="1" :step="5" :step-snapping="false" aria-label="页数" />
</Stack>
</template>
vue
格式
formatOptions 接受 Intl.NumberFormat 的选项,货币、百分比与单位都能显示。locale 决定分隔符与符号的写法,未设置时跟随语言包的语言。
<script setup lang="ts">
import { ref } from 'vue'
import { NumberInput, Stack } from '@hina-ui/vue'
const price = ref(1280)
const discount = ref(0.35)
const euro = ref(1234.5)
</script>
<template>
<Stack class="w-48">
<NumberInput
v-model="price"
:format-options="{ style: 'currency', currency: 'CNY' }"
aria-label="价格"
/>
<NumberInput
v-model="discount"
:step="0.01"
:min="0"
:max="1"
:format-options="{ style: 'percent' }"
aria-label="折扣"
/>
<NumberInput
v-model="euro"
locale="de-DE"
:format-options="{ style: 'currency', currency: 'EUR' }"
aria-label="欧元价格"
/>
</Stack>
</template>
vue
尺寸
三档尺寸与输入框相同。
<script setup lang="ts">
import { NumberInput, Stack } from '@hina-ui/vue'
</script>
<template>
<Stack class="w-40">
<NumberInput size="sm" :default-value="1" aria-label="小号" />
<NumberInput size="md" :default-value="1" aria-label="中号" />
<NumberInput size="lg" :default-value="1" aria-label="大号" />
</Stack>
</template>
vue
形态
primary 带边框、背景与阴影;secondary 使用浅色背景;bare 背景透明,不绘制边框、阴影、悬停底色或容器聚焦环,尺寸与内边距仍由原有设置控制。
bare 保留禁用状态与 aria-invalid;错误信息可由 FormField 显示。步进按钮之间及其与输入区之间的分隔线也会隐藏,按钮仍可正常操作。
<script setup lang="ts">
import { NumberInput, Stack } from '@hina-ui/vue'
</script>
<template>
<Stack class="w-full max-w-xs">
<NumberInput variant="primary" aria-label="primary" :default-value="1" />
<NumberInput variant="secondary" aria-label="secondary" :default-value="1" />
<NumberInput variant="bare" aria-label="bare" :default-value="1" />
</Stack>
</template>
vue
状态
invalid 标出校验未通过,disabled 不可编辑,readonly 可以聚焦与复制但不能修改。
<script setup lang="ts">
import { NumberInput, Stack } from '@hina-ui/vue'
</script>
<template>
<Stack class="w-40">
<NumberInput invalid :default-value="120" :max="99" aria-label="校验未通过" />
<NumberInput disabled :default-value="3" aria-label="已禁用" />
<NumberInput readonly :default-value="3" aria-label="只读" />
</Stack>
</template>
vue
不带步进按钮
关闭 controls 后只保留输入区,键盘与滚轮仍然有效。
<script setup lang="ts">
import { NumberInput } from '@hina-ui/vue'
</script>
<template>
<NumberInput :controls="false" :min="1" placeholder="页码" aria-label="页码" class="w-40" />
</template>
vue
在表单中
放进 FormField 后,标签指向输入框,说明与错误信息由字段渲染;校验规则与提交交给 Form。值是数字或者 null,范围规则写在数字层。
<script setup lang="ts">
import { reactive, ref } from 'vue'
import * as v from 'valibot'
import { Button, Form, FormField, NumberInput, Text } from '@hina-ui/vue'
const schema = v.object({
price: v.pipe(
v.number('请输入价格'),
v.minValue(1, '价格至少 1 元'),
v.maxValue(9999, '价格不超过 9999 元'),
),
})
const values = reactive({ price: null as number | null })
const saved = ref('')
async function save(data: unknown) {
await new Promise(resolve => setTimeout(resolve, 600))
saved.value = JSON.stringify(data)
}
</script>
<template>
<Form v-slot="{ submitting }" :values="values" :rules="schema" class="w-80" @submit="save">
<FormField name="price" label="价格" description="1 到 9999 元" required>
<NumberInput v-model="values.price" :min="0" :step="1" />
</FormField>
<Button type="submit" :loading="submitting" class="self-start">上架</Button>
<Text v-if="saved" tone="muted" size="sm">已上架:{{ saved }}</Text>
</Form>
</template>
vue
行为
- 失焦或者按 Enter 时解析输入。无法解析时恢复为上一个值,超出范围的值限制到边界。
- 输入时拒绝不能构成数字的字符。
- 步进按钮支持按住连续增减,到达边界的一侧会禁用。
- 悬停、聚焦、错误与禁用的表现与 Input 相同。
无障碍
- 根元素为
role="group",输入区为role="spinbutton",带aria-valuenow、aria-valuemin与aria-valuemax。 - 步进按钮不进入 Tab 序列,键盘用户用方向键调整。按钮名称随语言包本地化。
- 应当配合
label元素或者aria-label提供名称。invalid同时设置aria-invalid。
API
Props
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
modelValue | number | null | — | 数值 |
defaultValue | number | — | 非受控时的初始值 |
min | number | — | 最小值 |
max | number | — | 最大值 |
step | number | 1 | 步长 |
stepSnapping | boolean | true | 是否把值对齐到步长 |
formatOptions | Intl.NumberFormatOptions | — | 显示格式 |
locale | string | — | 格式化所用的语言 |
controls | boolean | true | 是否显示步进按钮 |
variant | 'primary' | 'secondary' | 'bare' | 'primary' | 形态 |
size | 'sm' | 'md' | 'lg' | 'md' | 尺寸 |
invalid | boolean | false | 是否校验未通过 |
disabled | boolean | false | 是否禁用 |
readonly | boolean | false | 是否只读 |
class | string | — | 追加至根元素的类名 |
| 事件 | 参数 | 说明 |
|---|---|---|
update:modelValue | value: number | undefined | 数值变化 |