基于antd Input组件封装最小值和最大值输入,只能输入自然数

2,418 阅读2分钟

动图.gif

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的数
    $                 // 匹配结尾