常用二次封装Ant-design组件(2)--- DatePicker

前言:依赖DatePicker二开的一个组件,主要是对Datepicker默认的展示形态进行一个处理(强烈建议Antd后续出自定义),如果React有现成能达到这种要求的包,求大佬告知(感觉肯定是有的,就是我不知道,焯)。

设计需求

需求展示形态单个模块,这里实现的时候用了Radio.Group作形态主体,antd的DatePicker组件里其实有一个api为variant(形态变体),只不过只能调整边框和背景,类似ghost效果,但感觉后面版本会支持自定义。

代码实现(ts)

这两个代码是放在一个文件里的,这里拆开是方便阅读。

使用

<RadioTimePciker
    onFinished={(date, dateString, type) => {
				console.log(date, dateString, type);
			}}
/>

RadioGroup形态代码


const raditoStyle: React.CSSProperties = {
	width: "60px",
	height: "32px",
	textAlign: "center",
	paddingInline: "2px",
}


interface timeProps {
	onFinished: (date: dayjs.Dayjs | dayjs.Dayjs[] | undefined, dateString: string | string[], dateType: string) => void | undefined
}
// 日历形态
const RadioTimePciker = ({ onFinished }: timeProps) => {
	const SubtitleRef = useRef(null);
	const [dateValue, setDateValue] = useState<dayjs.Dayjs | dayjs.Dayjs[] | undefined>(undefined)
	const [left, SetLeft] = useState(0)  //pup偏移量
	// 存放各个时间值 
	const [state, setState] = useSet({
		picker_show: false,
		picker_type: "30day",
		timeString: undefined
	})
	const { picker_type, picker_show, } = state

	useEffect(() => {
		switch (picker_type) {
			case "7day":
				onFinished(dateValue, "7day", picker_type)
				break;
			case "30day":
				onFinished(dateValue, "30day", picker_type)
				break;
			case "week":
				const week = dateValue as dayjs.Dayjs
				onFinished(dateValue, getWeekRange(week), picker_type)
				break;
			case "month":
				const month = dateValue as dayjs.Dayjs
				onFinished(dateValue, getMonthRange(month), picker_type)
				break;
			case "custom":
				const custom = dateValue as dayjs.Dayjs[]
				const dateString = [custom[0].format("YYYY-MM-DD"), custom[1].format("YYYY-MM-DD")]
				onFinished(dateValue, dateString, picker_type)
				break;
			case "date":
				const date = dateValue as dayjs.Dayjs
				onFinished(dateValue, date.format("YYYY-MM-DD"), picker_type)
				break;
		}
		// console.log(dateValue.subtract(7, "day").format('YYYY-MM-DD'));

	}, [dateValue])


	// 日期选择完成
	const onDateChange = (date: any) => {
		setDateValue(date)
		setState({
			picker_show: false
		})
	};

	// tab点击
	const radioClick = (e: any) => {
		const type = e.target.value
		setState({ picker_type: type })
		if (type !== "7day" && type !== "30day") {
			setState({ picker_show: true, })
			const left_list: { [key: string]: number } = {
				"date": -108,
				"week": -48,
				"month": 12,
				"custom": -216,
			}
			SetLeft(left_list[type])
		} else {
			setState({ picker_show: false, })
		}
	}

	const cancel = () => {
		setState({ picker_show: false, })
	}

	// tab切换
	const onRadioChange = (e: any) => {
		const type = e.target.value
		if (type === "month") {
			setDateValue(dayjs().subtract(1, "month"))
		} else if (type === "week") {
			setDateValue(dayjs().subtract(1, "week"))
		} else if (type === "date") {
			setDateValue(dayjs().subtract(1, "day"))
		}
	}

	return <Radio.Group
		ref={SubtitleRef}
		defaultValue={"30day"}
		style={{ position: "relative" }}
		className={styles.radioGroup}
		onChange={onRadioChange}
	>
		<Radio.Button onClick={radioClick} style={raditoStyle} value="7day">7天</Radio.Button>
		<Radio.Button onClick={radioClick} style={raditoStyle} value="30day">30天</Radio.Button>
		<Radio.Button onClick={radioClick} style={raditoStyle} value="date">日</Radio.Button>
		<Radio.Button onClick={radioClick} style={raditoStyle} value="week">周</Radio.Button>
		<Radio.Button onClick={radioClick} style={raditoStyle} value="month">月</Radio.Button>
		<Radio.Button onClick={radioClick} style={raditoStyle} value="custom">自定义</Radio.Button>
		<PopupPicker
			left={left}
			top={picker_type === "custom" ? 25 : 35}
			type={picker_type as any}
			dateValue={dateValue}
			onDateChange={onDateChange}
			show={picker_show} // 控制日历是显示隐藏
			onCancel={cancel}
			Node={SubtitleRef.current}
		/>
	</Radio.Group>

}

export default RadioTimePciker;

两个format代码(上次忘记补充了,dayjs天下无敌哈哈哈)

// 根据日期  获取当前日所在周的开始和结束
export const getWeekRange = (date: Date | dayjs.Dayjs | undefined) => {
	const startOfWeek = dayjs(date).startOf('week').format('YYYY-MM-DD');
	const endOfWeek = dayjs(date).endOf('week').format('YYYY-MM-DD');
	return [startOfWeek, endOfWeek];
}

// 获取上一个月的开始和结束
export const getMonthRange = (date: Date | dayjs.Dayjs | undefined) => {
	const startOfWeek = dayjs(date).startOf('month').format('YYYY-MM-DD');
	const endOfWeek = dayjs(date).endOf('month').format('YYYY-MM-DD');
	return [startOfWeek, endOfWeek];
}

Popup面板代码

import React, { useEffect, useState, useRef } from 'react';
import { DatePicker, Button, DatePickerProps, Radio, message } from 'antd';
import type { RangePickerProps } from 'antd/es/date-picker';
import dayjs from 'dayjs';
import 'dayjs/locale/zh-cn';
import styles from "./index.less"
import { useSet } from '@/utils/hooks';
import { getMonthRange, getWeekRange } from '@/utils/format';

dayjs.locale('zh-cn');
const { RangePicker } = DatePicker

interface Date {
	style?: any;
	className?: any;
	dateValue: any
	top: number; // 绝对定位距离面板的top
	left: number; // 绝对定位距离面板的left
	onDateChange: any; // 确定时间回调事件
	show: boolean; // 显示弹窗
	Node?: any; // 定义浮层的容器
	onCancel: () => void
	type: "date" | "week" | "month" | "year" | "7day" | "30day" | "custom"
}

// 日历面板
export const PopupPicker = ({ top, left, onDateChange, show, onCancel, Node, dateValue, type = "date" }: Date) => {
	const [dateState, setdateState] = useState(false);
	const [keyValue, setKeyValue] = useState('');
	const [isRange, setIsRange] = useState(false)
	const [dateType, setType] = useState<"date" | "week" | "month" | "year">("date")
	const [rangeValue, setRangeValue] = useState<any>([])
	const picker: React.MutableRefObject<any> = useRef();

	useEffect(() => {
		if (type !== "7day" && type !== "30day") {
			switch (type) {
				case "custom":
					setType("date")
					setIsRange(true);
					break;
				default:
					setType(type)
					setIsRange(false)
					break;
			}
		}
	}, [type])

	useEffect(() => {
		setdateState(show);
		// console.log(show);
	}, [show]);

	const reDate = () => {
		setKeyValue(dayjs().format());
	};

	const disabledDate: RangePickerProps['disabledDate'] = (current) => {
		if (type === "month") {
			return current && current >= dayjs().startOf('month')
		} else if (type === "week") {
			return current && current >= dayjs().startOf('week')
		}
		return current && current >= dayjs().startOf('day');
	};

	// datepickerChange
	const handleCalendarChange: DatePickerProps['onChange'] = (
		date,
		dateString,
	) => {
		onDateChange(date);
		setdateState(false);
		picker.current.blur();
		reDate();
	};

	// rangePickerChange
	const rangePickerChange = (dates: any, dateString: any) => {
		setRangeValue(dates)
	}

	const confirm = () => {
		if (rangeValue.length !== 0) {
			onDateChange(rangeValue);
			setdateState(false);
			picker.current.blur();
			reDate();
		} else {
			message.destroy()
			message.info("请先选择日期范围")
		}
	}

	return (
		<>
			{isRange ?
				<RangePicker
					key={keyValue}
					value={rangeValue}
					style={{ transform: "scale(0)", width: 0, height: 0, padding: 0, margin: 0, border: "none" }}
					renderExtraFooter={() => {
						return <>
							<Button onClick={confirm} type='link' style={{ width: "50px" }}>确认</Button>
							<Button onClick={() => setRangeValue([])} type='link' style={{ width: "50px", color: "red" }}>重选</Button>
							<Button onClick={() => { onCancel() }} type='link' style={{}}>取消</Button>
						</>
					}}
					open={dateState}
					ref={picker}
					disabledDate={disabledDate}
					onChange={rangePickerChange}
					popupClassName={styles.rangedropdownClassName}
					popupStyle={{
						left: `${left}px`,
						top: `${top}px`,
						position: "absolute",
					}}
					getPopupContainer={() => Node}
				/>
				: <DatePicker
					key={keyValue}
					value={dateValue}
					picker={dateType}
					style={{ display: 'none' }}
					open={dateState}
					ref={picker}
					showNow={false}
					// showWeek={true}
					disabledDate={disabledDate}
					onChange={handleCalendarChange}
					popupClassName={styles.datedropdownClassName}
					renderExtraFooter={() => {
						return <>
							<Button onClick={() => { onCancel() }} type='link' style={{}}>取消</Button>
						</>
					}}
					popupStyle={{
						left: `${left}px`,
						top: `${top}px`,
						position: "absolute",
					}}
					getPopupContainer={() => Node}
				/>}
		</>
	);
};

less样式代码

.rangedropdownClassName {
  :global {
    .ant-picker-date-range-wrapper {
      .ant-picker-range-arrow {
        display: none;
      }
    }
    .ant-picker-footer-extra {
      direction: rtl;
    }
  }
}

.datedropdownClassName {
  // transform: scale(1);
  // animation-name: antSlideUpIn;
  // position: absolute;
  :global {
    .ant-picker-footer-extra {
      text-align: center;
    }
  }
}

.radioGroup {
  :global {
    .ant-radio-button-wrapper {
      border-color: #e5e5e5;
      color: #333333;
      background-color: #f8f9fa;
    }
    .ant-radio-button-wrapper-checked {
      color: #5050e6;
      border-color: #5050e6;
    }
  }
}

实现效果

备注:7天和30天的value暂时没确定,根据服务端入参自己调整是否需要处理dateValue数值,我觉得应该是不需要,和服务端约定好就行。

组件目前我就写了一个回调(返回date,dateString,type),用来处理获取到的时间类型和时间值string,进行数据处理。

自定义

注意点

实现的思路就是把原来的DatePicker这个组件去掉,只保留日期popup面板来进行依附(还好antd能单独对日历面板自定义),但是有个注意点是在使用RangePicker的时候就不能用同样的方法去实现了,不能再将display设置为none,rangePicker在使用的时候面板会对形态获取焦点,获取不到无法填入值是不能进行范围选择的,我这里是用了取巧的方法,样式代码放在下面了。隐藏的那三个方式都不大行,反正只要看不见不影响操作就行,还有一点就是如果还有别的rangePicker样式可能会覆盖,建议采用我这种样式引入方式,global可以控制为局部样式

	style={{ transform: "scale(0)", width: 0, height: 0, padding: 0, margin: 0, border: "none" }}
  • 8
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值