react antd 是一个基于 react 的 UI 组件库,提供了丰富的组件和设计规范。但是,有时候我们需要对它的组件进行二次封装,以适应不同的业务场景和需求。
一、二次封装的目的
1、增加或修改组件的功能和样式
2、统一组件的使用方式和风格
3、简化组件的调用和传参
二、二次封装的方法
1、创建一个自定义组件,引入 antd 的组件
2、定义自定义组件的类型和属性,可以继承或扩展 antd 组件的类型和属性
3、在自定义组件中实现自己的渲染逻辑,可以使用 antd 组件提供的 API 和属性
4、将自定义组件需要传递给 antd 组件的属性,通过 rest 参数或解构赋值的方式传递给 antd 组件
三、示例:基于 Antd 的 DatePicker 组件的二次封装
1、实现的功能:
(1)预设 昨天、今天、上周、本周、上月、本月 时间范围快捷选择
(2)设置只能选择某天数范围之内的日期,默认无限制
(3)设置某天以后的时间不能选择,默认今天
2、代码实现
(1)封装组件:RangeDatePicker.js
import React, { useState } from 'react'
import { DatePicker } from 'antd'
import 'moment/locale/zh-cn'
import locale from 'antd/lib/date-picker/locale/zh_CN'
import moment from 'moment'
import { cloneDeep } from 'lodash'
const RangeDatePicker = ({
value, // 日期
onChange, // 时间发生变化的回调,发生在用户选择时间时
showTime = false, // 是否增加时间选择功能(时分秒)
limitDay = 0, // 只能选择limitDay天数范围之内的日期,默认无限制
endOfDay = moment().endOf('day'), // endOfDay以后的时间不能选择,默认今天
...rest // 其他参数
}) => {
const { RangePicker } = DatePicker
const startOfDay = moment()
.subtract(limitDay, 'days')
.endOf('day') // 今天之前limitDay天
const [selectDate, setSelectDate] = useState([startOfDay, endOfDay])
const [selectvalue, setSelectValue] = useState([startOfDay, endOfDay])
// 不可选择的日期
const disabledTaskDate = current => {
let date = cloneDeep(selectDate)
if (selectDate && selectDate?.length > 0 && limitDay > 0) {
// 控制只能选择limitDay天数范围之内的日期
let startOfStage = date?.[1]?.subtract(limitDay, 'days').endOf('day')
let endOfStage = date?.[0]?.add(limitDay, 'days').endOf('day')
if (endOfStage > endOfDay) {
endOfStage = endOfDay
}
return current < startOfStage || current > endOfStage
} else {
// endOfDay以后的时间不能选择
return current > endOfDay
}
}
// 待选日期发生变化的回调
const onDateChange = dates => {
if (!dates || !dates?.length) return
setSelectDate([cloneDeep(dates?.[0]), cloneDeep(dates?.[1])])
}
// 弹出日历和关闭日历的回调
const onDateOpenChange = open => {
if (open) {
if (selectvalue && selectvalue?.length) return
setSelectDate([])
}
}
// 时间发生变化的回调
const onPickerChange = value => {
setSelectValue(value)
onChange(value)
}
// 获取预设时间范围快捷选择
const getRanges = () => {
// 今天
const toDayRange =
moment().startOf('day') <= endOfDay
? { 今天: [moment().startOf('day'), moment().endOf('day')] }
: {}
// 本周
const thisWeekRange =
moment().startOf('week') <= endOfDay ? { 本周: [moment().startOf('week'), endOfDay] } : {}
// 本月
const thisMonthRange =
moment().startOf('month') <= endOfDay ? { 本月: [moment().startOf('month'), endOfDay] } : {}
return {
// 昨天
昨天: [
moment()
.subtract(1, 'days')
.startOf('day'),
moment()
.subtract(1, 'days')
.endOf('day'),
],
// 今天
...toDayRange,
// 上周
上周: [
moment(new Date())
.subtract(1, 'week')
.startOf('week'),
moment(new Date())
.subtract(1, 'week')
.endOf('week'),
],
// 本周
...thisWeekRange,
// 上月
上月: [
moment(new Date())
.subtract(1, 'month')
.startOf('month'),
moment(new Date())
.subtract(1, 'month')
.endOf('month'),
],
// 本月
...thisMonthRange,
}
}
return (
<RangePicker
locale={locale} // 国际化配置(汉化)
getPopupContainer={triggerNode => triggerNode.parentNode} // 定义浮层的容器(解决选项框随页面滚动分离问题)
showTime={
showTime
? {
hideDisabledOptions: true, // 隐藏禁止选择的选项
defaultValue: [moment('00:00:00', 'HH:mm:ss'), moment('23:59:59', 'HH:mm:ss')], // 默认时间
}
: null
}
format={showTime ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD'} // 日期格式
ranges={{
...getRanges(),
}}
value={value} // 日期
onChange={onPickerChange} // 时间发生变化的回调
disabledDate={disabledTaskDate} // 不可选择的日期
onCalendarChange={onDateChange} // 待选日期发生变化的回调
onOpenChange={onDateOpenChange} // 弹出日历和关闭日历的回调
{...rest}
/>
)
}
export default RangeDatePicker
(2)默认使用
import React from 'react';
import RangeDatePicker from "../custom-component/RangeDatePicker";
class GoodsInfo extends React.Component{
constructor(props) {
super(props);
}
render() {
return (
<>
<RangeDatePicker onChange={()=>{}}></RangeDatePicker> //默认使用
</>
)
}
}
export default GoodsInfo
效果:
(3)带时间选择功能
render() {
return (
<>
<RangeDatePicker onChange={()=>{}} showTime={true}></RangeDatePicker> //带时间选择功能
</>
)
}
效果:
(4)设置天数选择范围
render() {
return (
<>
<RangeDatePicker onChange={()=>{}} limitDay={7}></RangeDatePicker> //设置天数选择范围
</>
)
}