最近公司的产品需求要做一个数据采集流程,这个流程里面有比较多的表单数据,其中有一个输入数值范围的控件,这个功能还是很常见的,但是之前的开发没有封装成一个公共组件,特此自己造轮子。
需求原型
使用场景
当需求中需要录入数值范围的表单数据
实现思路
考虑到组件的共用性和拓展性,它应具备以下功能:
- 只能输入数字,选择 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 | 分隔符间距 | number | 15 | - |
placeholder | 占位符 | [string,string] | ['最小值', '最大值'] | - |
precision | 数值精度 | number | 2 | - |
min | 最小值 | number | 0 | - |
max | 最大值 | number | 99999999.99 | - |
suffix | 后缀 | string | - | - |
除此之外支持所有 InputNumber属性
效果预览
注意事项
- 组件是根据公司具体业务需求开发的,不一定符合每个人的要求
- 该组件只是提供一个思路,可在此拓展更复杂的业务场景