Vue3数字输入框(InputNumber) 您所在的位置:网站首页 vue加减1 Vue3数字输入框(InputNumber)

Vue3数字输入框(InputNumber)

2024-07-15 19:35| 来源: 网络整理| 查看: 265

可自定义设置以下属性:

输入框宽度(width),类型:string|number,单位px,默认 90

最小值(min),类型:number,默认 -Infinity

最大值(max),类型:number,默认 Infinity

每次改变步数,可以为小数(step),类型:number,默认 1

数值精度(precision),类型:number,默认 0

前缀图标(prefix),类型:string | slot,默认''

指定展示值的格式(formatter),类型:Function,默认 (value: string) => value

是否启用键盘快捷键行为(上方向键增,下方向键减)(keyboard),类型:boolean,默认 true

是否禁用(disabled),类型:boolean,默认 false

当前值(v-model:value),类型:number|null,默认 null

效果如下图:在线预览

其中引入使用了 消除js精度的加法函数(add) 

①创建数组输入框组件InputNumber.vue:

/* 一个根节点时,禁用组件根节点自动继承 attribute,必须使用这种写法!然后在要继承 attribute 的节点上绑定 v-bind="$attrs" 即可 多个根节点时,只需在要继承 attribute 的节点上绑定 v-bind="$attrs" 即可 */ export default { inheritAttrs: false } import { ref, computed, watch, useSlots } from 'vue' import { add } from '../utils' interface Props { width?: string | number // 输入框宽度 min?: number // 最小值 max?: number // 最大值 step?: number // 每次改变步数,可以为小数 precision?: number // 数值精度 prefix?: string // 前缀图标 string | slot formatter?: Function // 指定展示值的格式 keyboard?: boolean // 是否启用键盘快捷键行为(上方向键增,下方向键减) disabled?: boolean // 是否禁用 value?: number | null // 当前值(v-model) } const props = withDefaults(defineProps(), { width: 90, min: -Infinity, max: Infinity, step: 1, precision: 0, prefix: '', formatter: (value: string) => value, keyboard: true, disabled: false, value: null }) const inputWidth = computed(() => { if (typeof props.width === 'number') { return props.width + 'px' } return props.width }) const precision = computed(() => { // 数值精度取步长和精度中较大者 const stepPrecision = String(props.step).split('.')[1]?.length || 0 return Math.max(props.precision, stepPrecision) }) const slots = useSlots() const showPrefix = computed(() => { const prefixSlots = slots.prefix?.() if (prefixSlots) { return Boolean(prefixSlots[0].children !== 'v-if' && prefixSlots?.length) } return props.prefix }) const numValue = ref(props.formatter(props.value?.toFixed(precision.value))) watch( () => props.value, (to) => { numValue.value = to === null ? null : props.formatter(to?.toFixed(precision.value)) } ) watch(numValue, (to) => { if (!to) { emitValue(null) } }) const emits = defineEmits(['update:value', 'change']) function emitValue(value: number | null) { emits('change', value) emits('update:value', value) } function onChange(e: Event) { const value = (e.target as HTMLInputElement).value.replace(/,/g, '') if (!Number.isNaN(parseFloat(value))) { // Number.isNaN() 判断传递的值是否为NaN,并检测器类型是否为 Number if (parseFloat(value) > props.max) { emitValue(props.max) return } if (parseFloat(value) < props.min) { emitValue(props.min) return } if (parseFloat(value) !== props.value) { emitValue(parseFloat(value)) } else { numValue.value = props.value?.toFixed(precision.value) } } else { numValue.value = props.value?.toFixed(precision.value) } } function onKeyboard(e: KeyboardEvent) { if (e.key === 'ArrowUp') { onUp() } if (e.key === 'ArrowDown') { onDown() } } function onUp() { const res = parseFloat(Math.min(props.max, add(props.value || 0, +props.step)).toFixed(precision.value)) emitValue(res) } function onDown() { const res = parseFloat(Math.max(props.min, add(props.value || 0, -props.step)).toFixed(precision.value)) emitValue(res) } {{ prefix }} .m-input-number { position: relative; display: inline-block; height: 32px; font-size: 14px; color: rgba(0, 0, 0, 0.88); line-height: 1.5714285714285714; padding: 0 11px; background-color: #ffffff; border-radius: 6px; border: 1px solid #d9d9d9; transition: all 0.2s; &:hover { border-color: @themeColor; .m-handler-wrap { background: #fff; opacity: 1; } } &:focus-within { // 激活时样式 border-color: @themeColor; box-shadow: 0 0 0 2px fade(@themeColor, 20%); } .m-input-wrap { height: 100%; display: flex; .u-input-prefix { pointer-events: none; margin-inline-end: 4px; display: inline-flex; align-items: center; } .u-input-number { font-size: 14px; color: rgba(0, 0, 0, 0.88); width: 100%; height: 100%; background: transparent; border-radius: 6px; transition: all 0.2s linear; appearance: textfield; border: none; outline: none; } input::-webkit-input-placeholder { color: rgba(0, 0, 0, 0.25); } input:-moz-placeholder { color: rgba(0, 0, 0, 0.25); } input::-moz-placeholder { color: rgba(0, 0, 0, 0.25); } input:-ms-input-placeholder { color: rgba(0, 0, 0, 0.25); } } .m-handler-wrap { position: absolute; top: 0; right: 0; width: 22px; height: 100%; background: transparent; border-radius: 0 6px 6px 0; opacity: 0; display: flex; flex-direction: column; align-items: stretch; // 默认值,元素被拉伸以适应容器 transition: all 0.2s linear 0.2s; .u-icon { width: 7px; height: 7px; fill: rgba(0, 0, 0, 0.45); user-select: none; } .m-arrow { display: flex; align-items: center; justify-content: center; flex: auto; height: 40%; border-left: 1px solid #d9d9d9; cursor: pointer; transition: all 0.2s linear; &:hover { height: 60%; .u-icon { fill: @themeColor; } } } .up-arrow { border-top-right-radius: 6px; } .down-arrow { border-top: 1px solid #d9d9d9; border-bottom-right-radius: 6px; } .disabled { cursor: not-allowed; } } } .input-number-disabled { color: rgba(0, 0, 0, 0.25); background-color: rgba(0, 0, 0, 0.04); border-color: #d9d9d9; box-shadow: none; cursor: not-allowed; opacity: 1; &:hover { border-color: #d9d9d9; } &:focus-within { // 激活时样式 border-color: #d9d9d9; box-shadow: none; } .m-input-wrap .u-input-number { cursor: not-allowed; } .m-handler-wrap { display: none; } }

②在要使用的页面引入:

其中 moneyFormat() 数值格式化方法请参考:日期格式化 | Vue Amazing UI

import InputNumber from './InputNumber.vue' import { ref, watchEffect } from 'vue' import { formatNumber } from 'vue-amazing-ui' const value = ref(3) const formatValue = ref(1000) watchEffect(() => { console.log('value:', value.value) }) watchEffect(() => { console.log('formatValue:', formatValue.value) }) function formatter(num: string): string { return formatNumber(num, 2) } function onChange(number: number | null) { console.log('change:', number) } {{ $route.name }} {{ $route.meta.title }} 基本使用 步数为小数 设置数值精度 格式化展示 自定义最大最小值 添加前缀图标 $ 禁用


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有