NumberInput 数字输入
数字输入框,带 +/− 步进按钮、min/max/step 约束、键盘上下步进、自动 clamp。
基础用法
绑定 number | null —— null 代表空值,便于跟「未填写」区分开。min / max 约束,离焦时自动 clamp。
背景
<script setup lang="ts">
import { ref } from 'vue';
import { CfNumberInput } from '@chufix-design/vue';
const value = ref<number | null>(10);
</script>
<template>
<div style="max-width: 16rem;">
<CfNumberInput v-model="value" :min="0" :max="100" />
</div>
</template>
<CfNumberInput value={value} onChange={setValue} min={0} max={100} /> 步进与精度
step 是按钮 / 键盘每次的步进单位;小数 step 会自动推断 precision(step=0.05 → 显示 2 位小数)。也可以显式传 precision 强制位数。空值 null 时占位文本生效。
背景
<script setup lang="ts">
import { ref } from 'vue';
import { CfNumberInput } from '@chufix-design/vue';
const ratio = ref<number | null>(0.25);
const price = ref<number | null>(99.9);
const optional = ref<number | null>(null);
</script>
<template>
<div class="demo-stack">
<div class="demo-row" style="gap: 1rem;">
<CfNumberInput v-model="ratio" :step="0.05" :min="0" :max="1" />
<CfNumberInput v-model="price" :step="0.1" :precision="2" />
<CfNumberInput v-model="optional" placeholder="留空表示不限" />
</div>
</div>
</template>
<CfNumberInput value={ratio} onChange={setRatio} step={0.05} min={0} max={1} />
<CfNumberInput value={price} onChange={setPrice} step={0.1} precision={2} />
<CfNumberInput value={optional} onChange={setOptional} placeholder="留空表示不限" /> 三档尺寸
size 控制控件高度,与 Input / Select 保持一致 — sm 紧凑型 / md 默认 / lg 大号触摸友好。
背景
<script setup lang="ts">
import { ref } from 'vue';
import { CfNumberInput } from '@chufix-design/vue';
const a = ref<number | null>(50);
const b = ref<number | null>(50);
const c = ref<number | null>(50);
</script>
<template>
<div class="demo-row" style="gap: 1rem;">
<CfNumberInput v-model="a" size="sm" />
<CfNumberInput v-model="b" size="md" />
<CfNumberInput v-model="c" size="lg" />
</div>
</template>
<CfNumberInput value={a} onChange={setA} size="sm" />
<CfNumberInput value={b} onChange={setB} size="md" />
<CfNumberInput value={c} onChange={setC} size="lg" /> 隐藏步进按钮 / 禁用
hideSteppers 隐藏右侧 +/− 按钮但保留键盘步进 —— 在表格内或紧凑表单里更清爽。disabled 完全禁用交互。
背景
默认(带 +/− 按钮)
hideSteppers
disabled
<script setup lang="ts">
import { ref } from 'vue';
import { CfNumberInput } from '@chufix-design/vue';
const a = ref<number | null>(42);
const b = ref<number | null>(42);
</script>
<template>
<div class="demo-stack">
<div>
<div style="font-size: 12px; color: var(--fg-3); margin-bottom: 4px;">默认(带 +/− 按钮)</div>
<CfNumberInput v-model="a" />
</div>
<div>
<div style="font-size: 12px; color: var(--fg-3); margin-bottom: 4px;">hideSteppers</div>
<CfNumberInput v-model="b" hide-steppers />
</div>
<div>
<div style="font-size: 12px; color: var(--fg-3); margin-bottom: 4px;">disabled</div>
<CfNumberInput :model-value="42" disabled />
</div>
</div>
</template>
<CfNumberInput value={a} onChange={setA} />
<CfNumberInput value={b} onChange={setB} hideSteppers />
<CfNumberInput value={42} disabled /> 事件与表单
NumberInput 会区分输入中的原始文本、提交后的数字值、步进来源和非法输入。传入 name / id 后会直接落到原生 input,适合放进表单或自动化测试。
背景
ready
输入、步进或提交后会显示事件流。<script setup lang="ts">
import { ref } from 'vue';
import { CfBadge, CfNumberInput, type NumberInputChangeReason } from '@chufix-design/vue';
const value = ref<number | null>(25);
const phase = ref('ready');
const logs = ref(['输入、步进或提交后会显示事件流。']);
function record(name: string, detail: string) {
logs.value = [`${name}: ${detail}`, ...logs.value].slice(0, 5);
}
function onChange(next: number | null, meta: { raw: string; reason: NumberInputChangeReason }) {
value.value = next;
phase.value = meta.reason;
record('change', `${String(next ?? 'null')} / raw=${meta.raw || 'empty'} / ${meta.reason}`);
}
</script>
<template>
<div class="number-events">
<CfNumberInput
:model-value="value"
name="retry-budget"
:min="0"
:max="100"
:step="5"
placeholder="0 - 100"
@input="(raw) => record('input', raw || 'empty')"
@change="onChange"
@step="(next, meta) => record('step', `${next} / direction=${meta.direction}`)"
@invalid="(meta) => {
phase = 'invalid';
record('invalid', `${meta.raw} 不是有效数字`);
}"
@focus="record('focus', 'focused')"
@blur="record('blur', 'committed')"
/>
<div class="number-events__status">
<CfBadge tone="info" :content="phase" />
<div class="number-events__log" aria-live="polite">
<code v-for="entry in logs" :key="entry">{{ entry }}</code>
</div>
</div>
</div>
</template>
<style scoped>
.number-events {
display: grid;
gap: 12px;
width: min(100%, 420px);
}
.number-events__status {
display: flex;
align-items: flex-start;
gap: 10px;
}
.number-events__log {
display: grid;
gap: 4px;
min-width: 0;
}
.number-events__log code {
white-space: normal;
}
</style>
<CfNumberInput
value={value}
name="retry-budget"
min={0}
max={100}
step={5}
onInput={(raw) => console.log('input', raw)}
onChange={(value, meta) => console.log('change', value, meta.reason)}
onStep={(value, meta) => console.log('step', value, meta.direction)}
onInvalid={(meta) => console.log('invalid', meta.raw)}
/> 键盘交互
↑/↓—— 按 step 步进PageUp/PageDown—— 按 10 倍 step 步进Home/End—— 跳到 min / max(存在边界时)Enter—— 提交并 clamp- 离焦 —— 自动 clamp 到 [min, max],不在范围内的输入会被纠正
- 非数字输入会触发
invalid并回滚到上一个有效值
API
| Prop | 类型 | 默认值 | 说明 |
|---|---|---|---|
modelValue (Vue) / value (React) | number | null | null | 当前值;null 表示空 |
min | number | — | 最小值 |
max | number | — | 最大值 |
step | number | 1 | 步进 |
precision | number | 推断 | 小数位数;不传则从 step 推断 |
size | 'sm' | 'md' | 'lg' | 'md' | 高度 |
hideSteppers | boolean | false | 隐藏 +/− 按钮(仍可键盘步进) |
placeholder | string | — | 占位文本(值为 null 时显示) |
disabled | boolean | false | 禁用 |
name | string | — | 原生 input name,参与表单提交 |
id | string | — | 原生 input id |
Events
| Vue 事件 | React 回调 | payload | 说明 |
|---|---|---|---|
input | onInput | raw | 输入中的原始字符串 |
change | onChange | (value, { raw, reason }) | 提交后的数字值,reason 为 blur / enter / step / home / end 等 |
step | onStep | (value, { direction }) | 点击按钮或方向键步进 |
invalid | onInvalid | { raw, reason } | 提交非法数字时触发 |
focus / blur | onFocus / onBlur | FocusEvent | 原生焦点事件 |
反馈与讨论
NumberInput 数字输入 的讨论