fix: 🐛 修复 InputNumber 微信小程序设置了precision后无法输入小数点的问题 (#902)

 Closes: #878
This commit is contained in:
不如摸鱼去 2025-02-19 15:36:07 +08:00 committed by GitHub
parent 604faebf4b
commit e3a03b1dbb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 107 additions and 33 deletions

View File

@ -22,7 +22,7 @@
</view>
</demo-block>
<demo-block title="设置小数精度">
<wd-input-number v-model="value6" @change="handleChange6" :precision="2" :step="0.1" />
<wd-input-number v-model="value6" @change="handleChange6" :precision="1" :step="0.1" />
</demo-block>
<demo-block title="输入严格为步数的倍数">
<wd-input-number v-model="value7" @change="handleChange7" step-strictly :step="2" />
@ -49,7 +49,7 @@ const value2 = ref<number>(1)
const value3 = ref<number>(1)
const value4 = ref<number>(2)
const value5 = ref<number>(1)
const value6 = ref<string>('1.205')
const value6 = ref<string>('1.2')
const value7 = ref<number>(1)
const value8 = ref<number>(2)
const value9 = ref<string>('')

View File

@ -1,7 +1,7 @@
/*
* @Author: weisheng
* @Date: 2024-03-15 20:40:34
* @LastEditTime: 2025-02-11 18:38:54
* @LastEditTime: 2025-02-19 12:47:54
* @LastEditors: weisheng
* @Description:
* @FilePath: /wot-design-uni/src/uni_modules/wot-design-uni/components/wd-input-number/types.ts
@ -10,8 +10,27 @@
import type { PropType } from 'vue'
import { baseProps, makeBooleanProp, makeNumberProp, makeNumericProp, makeRequiredProp, makeStringProp, numericProp } from '../common/props'
/**
*
* @param value
* @returns Promise<boolean>
*/
export type InputNumberBeforeChange = (value: number | string) => boolean | Promise<boolean>
/**
*
* Input: 用户输入事件
* Blur: 失焦事件
* Watch: 监听值变化事件
* Button: 按钮点击事件
*/
export enum InputNumberEventType {
Input = 'input',
Blur = 'blur',
Watch = 'watch',
Button = 'button'
}
export const inputNumberProps = {
...baseProps,
/**

View File

@ -42,21 +42,25 @@ export default {
import wdIcon from '../wd-icon/wd-icon.vue'
import { computed, nextTick, ref, watch } from 'vue'
import { isDef, isEqual } from '../common/util'
import { inputNumberProps } from './types'
import { inputNumberProps, InputNumberEventType } from './types'
import { callInterceptor } from '../common/interceptor'
const props = defineProps(inputNumberProps)
const emit = defineEmits(['focus', 'blur', 'change', 'update:modelValue'])
const inputValue = ref<string | number>(getInitValue()) //
//
/**
* 判断数字是否达到最小值限制
*/
const minDisabled = computed(() => {
const value = formatValue(inputValue.value)
const { disabled, min, step } = props
return disabled || Number(value) <= min || changeStep(value, -step) < min
})
//
/**
* 判断数字是否达到最大值限制
*/
const maxDisabled = computed(() => {
const value = formatValue(inputValue.value)
const { disabled, max, step } = props
@ -67,17 +71,22 @@ const maxDisabled = computed(() => {
watch(
() => props.modelValue,
(value) => {
updateValue(value)
updateValue(value, InputNumberEventType.Watch)
}
)
// max, min, precision
watch([() => props.max, () => props.min, () => props.precision], () => {
const value = formatValue(inputValue.value)
updateValue(value)
updateValue(value, InputNumberEventType.Watch)
})
//
/**
* 对比两个值是否相等
* @param value1 第一个值
* @param value2 第二个值
* @returns 是否相等
*/
function isValueEqual(value1: number | string, value2: number | string) {
return isEqual(String(value1), String(value2))
}
@ -97,7 +106,11 @@ function toPrecision(value: number) {
return Number(parseFloat(`${Math.round(value * Math.pow(10, precision)) / Math.pow(10, precision)}`).toFixed(precision))
}
//
/**
* 获取数字的小数位数
* @param value 需要计算精度的数字
* @returns 小数位数
*/
function getPrecision(value?: number) {
if (!isDef(value)) return 0
const valueString = value.toString()
@ -109,20 +122,19 @@ function getPrecision(value?: number) {
return precision
}
//
/**
* 按步进值严格递增或递减
* @param value 当前值
* @returns 按步进值调整后的值
*/
function toStrictlyStep(value: number | string) {
const stepPrecision = getPrecision(props.step)
const precisionFactory = Math.pow(10, stepPrecision)
return (Math.round(Number(value) / props.step) * precisionFactory * props.step) / precisionFactory
}
//
function updateValue(value: string | number, fromUser: boolean = false) {
if (isValueEqual(value, inputValue.value)) {
return
}
const update = () => {
//
function doUpdate(value: string | number) {
inputValue.value = value
const formatted = formatValue(value)
nextTick(() => {
@ -130,7 +142,55 @@ function updateValue(value: string | number, fromUser: boolean = false) {
emit('update:modelValue', inputValue.value)
emit('change', { value: inputValue.value })
})
}
/**
* 清理输入字符串中多余的小数点
* @param value 输入的字符串
* @returns 清理后的字符串
*/
function cleanExtraDecimal(value: string): string {
const precisionAllowed = Number(props.precision) > 0
if (precisionAllowed) {
const dotIndex = value.indexOf('.')
if (dotIndex === -1) {
return value
} else {
const integerPart = value.substring(0, dotIndex + 1)
// '.'
const decimalPart = value.substring(dotIndex + 1).replace(/\./g, '')
return integerPart + decimalPart
}
} else {
//
const dotIndex = value.indexOf('.')
return dotIndex !== -1 ? value.substring(0, dotIndex) : value
}
}
/**
* 更新输入框的值
* @param value 新的值
* @param eventType 触发更新的事件类型
*/
function updateValue(value: string | number, eventType: InputNumberEventType = InputNumberEventType.Input) {
const fromUser = eventType !== InputNumberEventType.Watch // watch
const forceFormat = eventType === InputNumberEventType.Blur || eventType === InputNumberEventType.Button
// Input Watch '.'
if ((eventType === InputNumberEventType.Input || eventType === InputNumberEventType.Watch) && String(value).endsWith('.') && props.precision) {
inputValue.value = value
nextTick(() => {
inputValue.value = cleanExtraDecimal(String(value))
emit('update:modelValue', inputValue.value)
emit('change', { value: inputValue.value })
})
return
}
if (!forceFormat && isValueEqual(value, inputValue.value)) {
return
}
const update = () => doUpdate(value)
if (fromUser) {
callInterceptor(props.beforeChange, {
@ -153,11 +213,10 @@ function changeStep(val: string | number, step: number) {
return toPrecision((val * precisionFactor + step * precisionFactor) / precisionFactor)
}
//
function changeValue(step: number) {
if ((step < 0 && (minDisabled.value || props.disableMinus)) || (step > 0 && (maxDisabled.value || props.disablePlus))) return
const value = changeStep(inputValue.value, step)
updateValue(value, true)
updateValue(value, InputNumberEventType.Button)
}
//
@ -170,10 +229,15 @@ function add() {
changeValue(props.step)
}
//
function handleInput(event: any) {
let value = event.detail.value || ''
updateValue(value, true)
const rawValue = event.detail.value || ''
updateValue(rawValue, InputNumberEventType.Input)
}
function handleBlur(event: any) {
const value = event.detail.value || ''
updateValue(value, InputNumberEventType.Blur)
emit('blur', { value })
}
//
@ -181,15 +245,6 @@ function handleFocus(event: any) {
emit('focus', event.detail)
}
//
function handleBlur(event: any) {
const value = event.detail.value || ''
updateValue(value, true)
emit('blur', {
value
})
}
//
function formatValue(value: string | number) {
if (props.allowNull && (!isDef(value) || value === '')) {