1、NumericInput.tsx
import React from 'react'
import { Input } from 'antd'
interface ParamsType {
value?: string // 值都是string类型,判断大小时要注意先转为数字再进行判断
onChange: (value: string) => void
onBlur?: (value: string) => void
placeholder?: string // 默认值:请输入数字
maxLength?: number // 默认值:5
type?: string // 类型,默认值:number
}
const NumericInput: React.FC<ParamsType> = (props: any) => {
const { value, onChange, onBlur, type = 'number', ...restProps } = props
let reg: RegExp
switch (type) {
case 'naturalNumber': // 自然数:0 1 2 3 ... // 正整数 /^[1-9]\d*$/ 整数 Number.isInteger(-2)
reg = /^\d+?$/
break
case 'nonnegative': // 非负数(零、正数、小数):0 0.1 1 1.5 2 ...
reg = /^\d*(\.\d*)?$/
break
case 'money': // 大于0的数字,小数位为2位
reg = /^\s*(?=.*[1-9])\d*(?:\.\d{1,2})?\s*$/
break
case 'number': // 数字(负数、零、正数、小数):-1.2 -1 0 1 2 3.5 ...
reg = /^-?\d*(\.\d*)?$/
break
}
const handleChange = (e: any) => {
const { value } = e.target
if (
(!Number.isNaN(value) && reg.test(value)) ||
value === '' ||
((reg + '').includes('-') && value === '-')
) {
onChange(value)
}
}
const handleBlur = () => {
let valueTemp = value
if (value.charAt(value.length - 1) === '.' || value === '-') {
valueTemp = value.slice(0, -1)
}
onChange(valueTemp.replace(/0*(\d+)/, '$1'))
onBlur && onBlur(value)
}
NumericInput.defaultProps = { placeholder: '请输入数字', maxLength: 5 }
return (
<Input {...restProps} value={value} onChange={handleChange} onBlur={handleBlur} allowClear />
)
}
export default NumericInput
2、Counts/index.tsx
import React, { useState, useEffect } from 'react'
import NumericInput from '../NumericInput'
import './index.less'
interface ParamsType {
onChange: (val: string) => void
}
const Counts: React.FC<ParamsType> = (props: any) => {
const { onChange } = props
const [minValue, setMinValue] = useState<string>('') // 最小值
const [maxValue, setMaxValue] = useState<string>('') // 最大值
const [errorText, setErrorText] = useState<string>('') // 错误提示文案
useEffect(() => {
onChange([minValue, maxValue])
}, [minValue, maxValue])
return (
<div className="count">
<div>
<NumericInput
value={minValue}
onChange={(val) => {
setErrorText(maxValue && +val > +maxValue ? '最小值不能大于最大值' : '')
setMinValue(val)
}}
placeholder="最小值"
type="naturalNumber"
/>
<i></i>
<NumericInput
value={maxValue}
onChange={(val) => {
setErrorText(minValue && val && +val < +minValue ? '最大值不能小于最小值' : '')
setMaxValue(val)
}}
placeholder="最大值"
type="naturalNumber"
/>
</div>
<span className={errorText && 'errorText'}>{errorText}</span>
</div>
)
}
export default Counts
Counts/index.less
点击查看详细内容
.count {
> div {
position: absolute;
top: 0;
z-index: 1;
display: flex;
align-items: center;
justify-content: space-between;
background-color: var(--theme-pro-table-bk);
> .ant-input-affix-wrapper, // 具有allowClear属性时
> .ant-input { // 不具有allowClear属性时
width: calc(50% - 20px);
}
> i {
display: inline-block;
width: 15px;
height: 1px;
background-color: #dde6f7;
}
}
> span {
position: absolute;
top: 0;
left: 0;
color: #ff4d4f;
transition: top 0.2s;
&.errorText {
top: 32px;
}
}
}
3、使用
const [beginPersonTotal, setBeginPersonTotal] = useState<string>('') // 最小值
const [endPersonTotal, setEndPersonTotal] = useState<string>('') // 最大值
// columns:
{
title: '项目人数',
key: 'count',
hideInTable: true,
renderFormItem: () => (
<Counts
onChange={([min, max]) => {
setBeginPersonTotal(min)
setEndPersonTotal(max)
}}
/>
)
}
// 点击查询
const fetchData = async (params: any) => {
if (beginPersonTotal && endPersonTotal && +beginPersonTotal > +endPersonTotal) {
message.warning('最小值不能大于最大值')
return { data: prevResult.data, total: prevResult.totalRecords, success: prevResult.success }
} else {
const { current, pageSize, detailID, ...restParams } = params
let deptId = ''
if (detailID?.length) {
deptId = detailID[detailID.length - 1]
}
const requestParams = {
page: current,
pageSize,
schoolYear: currentYear,
beginPersonTotal,
endPersonTotal,
deptId,
...restParams
}
const { success, data, totalRecords } = await listApi(requestParams)
setListParams(requestParams) // 保存搜索项参数(导出时使用)
setPrevResult({ success, data: processData(data), totalRecords }) // 保存一份数据(上一次搜索的结果,搜索时最小值大于最大值时使用)
return { data, total: totalRecords, success }
}
}
4、正则解释
1、大于0的数字,小数位为2位:/^\s*(?=.*[1-9])\d*(?:\.\d{1,2})?\s*$/
^ # Start of string
\s* # Optional whitespace
(?=.*[1-9]) # Assert that at least one digit > 0 is present in the string
\d* # integer part (optional)
(?: # decimal part:
\. # dot
\d{1,2} # plus one or two decimal digits
)? # (optional)
\s* # Optional whitespace
$ # End of string
2、大于0且能包含小数(过滤掉0、00、001、0.0、01.1、01.0等,'1a'过滤不掉,工作中不要用这个):/^([1-9]\d*(\.\d*[1-9][0-9])?)|(0\.\d*[1-9][0-9])|(0\.\d*[1-9])$/或/^([1-9]\d*(\.\d*)?)|(0\.\d*[1-9][0-9])|(0\.\d*[1-9])$/
/^([1-9]\d*(\.\d*[1-9][0-9])?)|(0\.\d*[1-9][0-9])|(0\.\d*[1-9])$/
^ // 匹配开头
[1-9] // 表示第一个数为1~9中的任意一个(不能为0开头,0开头的在后半部分):匹配1~9
\d* // 表示0-9的任意一个数字,可以为多位数 :匹配除开头数字外的整数部分
(\.\d*)? // 表示跟小数点,以及任意数字:匹配小数点,以及小数点后面可以为任意数
| // 表示选择,即“或”,第二种可能
0\.\d*[1-9][0-9] // 表示以0开头,后面接小数点,小数点后面第一位数在1~9之间,第二位数可以在0~9之间
| // 表示选择,即“或”,第三种可能
0\.\d*[1-9] // 表示以0开头,后面接小数点,小数点后面只有一位不为0的数
$ // 匹配结尾