网站LOGO
白雾茫茫丶
页面加载中
5月20日
网站LOGO 白雾茫茫丶
记录学习、生活和有趣的事
菜单
  • 白雾茫茫丶
    记录学习、生活和有趣的事
    用户的头像
    首次访问
    上次留言
    累计留言
    我的等级
    我的角色
    打赏二维码
    打赏博主
    React - 实现一个基于 Antd 的数值范围组件
    点击复制本页信息
    微信扫一扫
    文章二维码
    文章图片 文章标题
    创建时间
  • 一 言
    确认删除此评论么? 确认
  • 本弹窗介绍内容来自,本网站不对其中内容负责。
    • 复制图片
    • 复制图片地址
    • 百度识图
    按住ctrl可打开默认菜单

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

    谢明伟 · 原创 ·
    前端开发 · ReactAnt-Design
    共 5928 字 · 约 2 分钟 · 423
    本文最后更新于2023年12月28日,已经过了143天没有更新,若内容或图片失效,请留言反馈
    最近公司的产品需求要做一个数据采集流程,这个流程里面有比较多的表单数据,其中有一个输入数值范围的控件,这个功能还是很常见的,但是之前的开发没有封装成一个公共组件,特此自己造轮子。

    需求原型

    使用场景

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

    实现思路

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

    只能输入数字,选择 InputNumber 数字输入框,并继承该组件的所有API属性

    当最小值大于最大值,或者最大值小于最小值时,应调换位置

    基于 自定义表单控件 封装

    代码结构

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

    typescript 代码:
    /*
     * @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行,怎么样,是不是很容易?

    使用方式

    typescript 代码:
    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. 该组件只是提供一个思路,可在此拓展更复杂的业务场景
    声明:本文由 谢明伟(博主)原创,依据 CC-BY-NC-SA 4.0 许可协议 授权,转载请注明出处。

    还没有人喜爱这篇文章呢

    我要发表评论 我要发表评论
    博客logo 白雾茫茫丶 记录学习、生活和有趣的事 51统计 百度统计
    MOEICP 萌ICP备20236860号 ICP 粤ICP备2023007649号 ICP 粤公网安备44030402006402号

    💻️ 谢明伟 2天前 在线

    🕛

    本站已运行 2 年 140 天 1 小时 37 分

    🌳

    自豪地使用 Typecho 建站,并搭配 MyLife 主题
    白雾茫茫丶. © 2022 ~ 2024.
    网站logo

    白雾茫茫丶 记录学习、生活和有趣的事