React - 实现一个基于 Antd 的数值范围组件

最近公司的产品需求要做一个数据采集流程,这个流程里面有比较多的表单数据,其中有一个输入数值范围的控件,这个功能还是很常见的,但是之前的开发没有封装成一个公共组件,特此自己造轮子。

需求原型

在这里插入图片描述

使用场景

当需求中需要录入数值范围的表单数据

实现思路

考虑到组件的共用性和拓展性,它应具备以下功能:

代码结构

由于这个功能实现还是比较简单的,组件的细节便不多描述,直接上代码:

/*
 * @Author: 白雾茫茫丶<baiwumm.com>
 * @Date: 2023-12-13 14:34:55
 * @LastEditors: 白雾茫茫丶<baiwumm.com>
 * @LastEditTime: 2023-12-13 18:08:35
 * @Description: 数字范围输入组件
 */
import { Col, InputNumber, Row } from 'antd'
import type { InputNumberProps } from 'antd/es/input-number'
import { gt, toNumber } from 'lodash'
import React, { FC, FocusEventHandler } from 'react'

import type { EnumValues } from '@/utils/types'

enum INPUT_TYPE {
  MIN, // 最小值
  MAX, // 最大值
}

type InputType = EnumValues<typeof INPUT_TYPE>

type ValuePair = (string | number | undefined)[];

type FormDigitRangeProps = {
  value?: ValuePair; // 表单控件的值
  onChange?: (value: ValuePair) => void; // 表单控件改变值的回调
  separator: string; // 分割线
  separatorGap: number; // 分割线和数据框的 gap
  placeholder: [string, string]; // 占位符
  suffix: string; // 后缀,不传则不显示
} & InputNumberProps

const FormDigitRange: FC<FormDigitRangeProps> = ({
  value = [],
  onChange,
  separator = '~',
  separatorGap = 15,
  placeholder = ['最小值', '最大值'],
  precision = 2,
  min = 0,
  max = 99999999.99,
  suffix,
  ...inputNumberProps
}) => {
  // 输入值失去焦点回调
  const handleChangeValue = (e: FocusEventHandler<HTMLInputElement>, type: InputType) => {
    // 获取输入框的值,这里转成 number 类型
    const result = e.target.value !== '' ? toNumber(e.target.value) : undefined;
    // 解构获取最值
    const [min, max] = value;
    switch (type) {
      case INPUT_TYPE.MIN:
        // 判断最小值是否大于最大值,为真就调换位置
        onChange?.(gt(result, max) ? [max, result] : [result, max])
        break;
      case INPUT_TYPE.MAX:
        // 判断最大值是否小于最小值,为真就调换位置
        onChange?.(gt(min, result) ? [result, min] : [min, result])
        break;
    }
  }
  // 渲染输入框
  const renderInputNumber = (type: InputType) => (
    <InputNumber
      {...inputNumberProps}
      min={min}
      max={max}
      value={toNumber(value[type])}
      precision={precision}
      placeholder={placeholder[type]}
      onBlur={(e) => handleChangeValue(e, type)}
      style={{ width: '100%' }}
    />
  )

  return (
    <Row gutter={separatorGap} align='middle' wrap={false}>
      <Col flex={1}>
        {renderInputNumber(INPUT_TYPE.MIN)}
      </Col>
      <Col flex="none">
        <div>{separator}</div>
      </Col>
      <Col flex={1}>
        {renderInputNumber(INPUT_TYPE.MAX)}
      </Col>
      {
        suffix && (
          <Col flex="none">{suffix}</Col>
        )
      }
    </Row>
  )
}
export default FormDigitRange

代码不到100行,怎么样,是不是很容易?

使用方式

import { Button, Col, Form, Row, Space } from 'antd'
import { compact, isNumber } from 'lodash'
import React, { FC, useEffect, useState } from 'react'

import PageContainer from '@/components/PageContainer'

import FormDigitRange from './components/FormDigitRange'


const DataAcquisition: FC = () => {
  const [form] = Form.useForm();
  const [fields, setFields] = useState({});
  const onFinish = (values: any) => {
    setFields(values)
  };

  useEffect(() => {
    form.setFieldValue('money', undefined)
  }, [])
  return (
    <PageContainer title="数字范围输入组件">
      <Form form={form} onFinish={onFinish}>
        <Row>
          <Col span={12}>
            <Form.Item
              name="money"
              label="租金涨跌金额"
              rules={[
                { type: 'array', required: true, message: '' },
                () => ({
                  validator(_, value) {
                    if (!value || !compact(value).length) {
                      return Promise.reject(new Error('请输入租金涨跌金额'));
                    } else if (!isNumber(value[0])) {
                      return Promise.reject(new Error('请输入最小值'));
                    } else if (!isNumber(value[1])) {
                      return Promise.reject(new Error('请输入最大值'));
                    }
                    return Promise.resolve();
                  },
                }),
              ]}
            >
              <FormDigitRange suffix="元/㎡/月" />
            </Form.Item>
          </Col>
        </Row>
        <Row>
          <Col span={12}>
            <Space direction="vertical" size="middle" style={{ display: 'flex' }} align='center'>
              <pre style={{ background: '#f5f5f5', padding: '12px 20px', width: 400 }}>
                {JSON.stringify(fields, null, 2)}
              </pre>
              <Button htmlType="submit" type="primary">提交</Button>
            </Space>
          </Col>
        </Row>
      </Form>
    </PageContainer>
  )
}
export default DataAcquisition

参数说明

参数说明类型默认值是否必传
separator分隔符string~-
separatorGap分隔符间距number15-
placeholder占位符[string,string][‘最小值’, ‘最大值’]-
precision数值精度number2-
min最小值number0-
max最大值number99999999.99-
suffix后缀string--

除此之外支持所有 InputNumber属性

效果预览

在这里插入图片描述

注意事项

  1. 组件是根据公司具体业务需求开发的,不一定符合每个人的要求
  2. 该组件只是提供一个思路,可在此拓展更复杂的业务场景
  • 9
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

白雾茫茫丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值