121 lines
3.6 KiB
TypeScript
121 lines
3.6 KiB
TypeScript
import { CustomWrapper, Input, View } from '@tarojs/components'
|
|
import { memo, useEffect, useMemo, useRef, useState } from 'react'
|
|
import Big from 'big.js'
|
|
import styles from './index.module.scss'
|
|
type params = {
|
|
minNum?: number //最小值
|
|
maxNum?: number //最大值
|
|
step?: number //步长
|
|
defaultNum?: number //默认值
|
|
digits?: number //多少位小数
|
|
onChange?: (val: number) => void
|
|
onBlue?: (val: number) => void //失去焦点触发
|
|
onClickBtn?: (val: number) => void
|
|
unit?: string
|
|
disabled?: true | false //是否禁用
|
|
}
|
|
export default memo((props: params) => {
|
|
return <Counter {...props}></Counter>
|
|
})
|
|
|
|
const Counter = memo((props: params) => {
|
|
let { minNum = 0, maxNum = 10000, step = 1, digits = 0, defaultNum = 0, onChange, onBlue, onClickBtn, unit = '', disabled = false } = props
|
|
const [value, setValue] = useState<any>({ count: defaultNum })
|
|
useEffect(() => {
|
|
setValue({ count: defaultNum })
|
|
}, [defaultNum])
|
|
const onPlus = () => {
|
|
if (disabled) return false
|
|
let count = value.count
|
|
let num_res = Big(count).add(step).toNumber()
|
|
num_res = num_res >= maxNum ? maxNum : num_res
|
|
num_res = formatDigits(num_res)
|
|
setValue({ ...value, count: num_res })
|
|
onChange?.(parseFloat(num_res))
|
|
onClickBtn?.(parseFloat(num_res))
|
|
}
|
|
const minus = () => {
|
|
if (disabled) return false
|
|
let count = value.count
|
|
let num_res = Big(count).minus(step).toNumber()
|
|
num_res = num_res < minNum ? minNum : num_res
|
|
setValue({ ...value, count: num_res })
|
|
onChange?.(parseFloat(num_res))
|
|
onClickBtn?.(parseFloat(num_res))
|
|
}
|
|
|
|
//保留小数
|
|
const formatDigits = (num) => {
|
|
num = num + ''
|
|
if (num.includes('.')) {
|
|
let res = num.split('.')
|
|
if (digits > 0) {
|
|
let last_num = res[1].substr(0, digits)
|
|
return res[0] + '.' + last_num
|
|
} else {
|
|
return res[0]
|
|
}
|
|
}
|
|
|
|
return parseFloat(num)
|
|
}
|
|
|
|
//检查数据
|
|
const checkData = (val) => {
|
|
let num = parseFloat(val)
|
|
if (num > maxNum) return maxNum
|
|
if (num < minNum) return minNum
|
|
return val
|
|
}
|
|
|
|
const onInputEven = (e) => {
|
|
let res = e.detail.value
|
|
if (res === '') {
|
|
onChange?.(minNum)
|
|
} else if (!isNaN(Number(res))) {
|
|
let count = formatDigits(res)
|
|
count = checkData(count)
|
|
onChange?.(parseFloat(count as string))
|
|
} else {
|
|
let num = parseFloat(res)
|
|
if (!isNaN(num)) {
|
|
let count = formatDigits(num)
|
|
count = checkData(count)
|
|
onChange?.(count as number)
|
|
} else {
|
|
onChange?.(defaultNum)
|
|
}
|
|
}
|
|
}
|
|
|
|
const onBluerEven = (e) => {
|
|
let num = parseFloat(e.detail.value)
|
|
if (e.detail.value == '') {
|
|
onBlue?.(minNum)
|
|
setValue({ count: minNum })
|
|
} else if (!isNaN(num)) {
|
|
let count = formatDigits(num)
|
|
count = checkData(count)
|
|
setValue({ count })
|
|
onBlue?.(count as number)
|
|
} else {
|
|
setValue({ count: minNum })
|
|
onBlue?.(minNum)
|
|
}
|
|
}
|
|
return (
|
|
<View className={styles.main} onClick={(e) => e.stopPropagation()}>
|
|
<View className={styles.reduce} onClick={() => minus()}>
|
|
-
|
|
</View>
|
|
<View className={styles.input}>
|
|
<Input type='digit' value={value.count} onInput={onInputEven} onBlur={onBluerEven} disabled={disabled} alwaysEmbed={true} cursorSpacing={150} />
|
|
<View className={styles.unit}>{unit}</View>
|
|
</View>
|
|
<View className={styles.plus} onClick={() => onPlus()}>
|
|
+
|
|
</View>
|
|
</View>
|
|
)
|
|
})
|