Reactantd的datePicker自定义,封装成组件
- 电脑硬件
- 2025-09-13 21:33:01

一、antd的datePicker自定义 需求:用户需要为日期选择器的每个日期单元格添加一个Tooltip,当鼠标悬停时显示日期、可兑换流量余额和本公会可兑流量。这些数据需要从接口获取。我需要结合之前的代码,确保Tooltip正确显示,并且数据来自接口。
主要汉化点:
整个日期选择器面板中文化星期显示为中文(周一 到 周日)月份显示为中文格式操作按钮汉化("确定"、"现在" 等)日期格式统一使用中文年月日加载提示中文化Tooltip内容中文化效果包含:
月份显示为 "2024年5月"星期列显示为 "一、二、三、四、五、六、日"今天按钮显示为 "今天"确定按钮显示为 "确定"十年范围显示为 "2020-2029"时间列显示为 "时","分","秒" index.tsx文件 import React, { useState, useEffect } from 'react'; import { DatePicker, Tooltip, Spin, ConfigProvider } from 'antd'; import dayjs, { Dayjs } from 'dayjs'; import isSameOrAfter from 'dayjs/plugin/isSameOrAfter'; import isSameOrBefore from 'dayjs/plugin/isSameOrBefore'; import './custom-datepicker.css'; import 'dayjs/locale/zh-cn'; import zhCN from 'antd/locale/zh_CN'; dayjs.extend(isSameOrAfter); dayjs.extend(isSameOrBefore); dayjs.locale('zh-cn'); // 设置dayjs为中文 interface TrafficData { date: string; personal: string; guild: string; } const App: React.FC = () => { const [selectedDate, setSelectedDate] = useState<Dayjs | null>(null); const [trafficData, setTrafficData] = useState<Record<string, TrafficData>>({}); const [loading, setLoading] = useState(true); const today = dayjs().startOf('day'); const sevenDaysLater = today.add(6, 'day'); useEffect(() => { const mockApi = async () => { const data: Record<string, TrafficData> = {}; Array.from({ length: 7 }).forEach((_, i) => { const date = today.add(i, 'day').format('YYYY-MM-DD'); data[date] = { date, personal: `${[0, 20, 30, 40, 80, 100, 50][i]}%`, guild: `${Math.floor(Math.random() * 100000).toLocaleString()}`, }; }); await new Promise(resolve => setTimeout(resolve, 500)); setTrafficData(data); setLoading(false); }; mockApi(); }, []); const disabledDate = (current: Dayjs) => current.isBefore(today, 'day') || current.isAfter(sevenDaysLater, 'day'); if (loading) return <Spin tip="数据加载中..." />; return ( <ConfigProvider locale={zhCN}> {/* 设置Ant Design为中文 */} <DatePicker value={selectedDate} disabledDate={disabledDate} onChange={setSelectedDate} dropdownClassName="custom-picker-dropdown" dateRender={current => { const dateStr = current.format('YYYY-MM-DD'); const data = trafficData[dateStr]; const isInRange = current.isSameOrAfter(today) && current.isSameOrBefore(sevenDaysLater); const isSelected = selectedDate?.isSame(current, 'day'); return ( <div className="custom-cell-wrapper"> <div className="native-cell-content"> {current.date()} </div> <Tooltip title={data ? `${dayjs(dateStr).format('YYYY年M月D日')}\n可兑流量余额: ${data.personal}\n本公会可兑流量: ${data.guild}` : '无可用数据'} overlayStyle={{ whiteSpace: 'pre-line', pointerEvents: 'none', }} placement="bottom" mouseEnterDelay={0} mouseLeaveDelay={0.1} trigger={['hover']} getPopupContainer={trigger => trigger.parentElement!} > <div className={`custom-cell ${isInRange ? 'recent-date' : ''}`}> <div className={`date-number ${isSelected ? 'selected' : ''}`}> {current.date()} </div> {data && ( <div className={`availability ${data.personal === '0%' ? 'empty' : ''}`}> 余{data.personal} </div> )} </div> </Tooltip> </div> ); }} /> </ConfigProvider> ); }; export default App; custom-datepicker.css文件 /* custom-datepicker.css */ .custom-picker-dropdown { z-index: 1001; } .custom-cell-wrapper { position: relative; height: 100%; width: 100%; } .native-cell-content { visibility: hidden; } .custom-cell { position: absolute; top: 0; left: 0; width: 100%; height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; cursor: pointer; z-index: 2; padding: 3px 0; } .date-number { width: 24px; height: 24px; line-height: 24px; text-align: center; color: #000; transition: all 0.2s; border-radius: 50%; } .date-number.selected { background: #1890ff; color: white !important; } .recent-date:hover .date-number:not(.selected) { color: #1890ff; } .availability { font-size: 10px; line-height: 14px; color: #1890ff; margin-top: 2px; } .availability.empty { color: #ff4d4f !important; } .ant-picker-cell-inner { padding: 0 !important; height: 100% !important; } .ant-picker-cell:hover .ant-picker-cell-inner { background: transparent !important; } /* 添加中文面板样式调整 */ .ant-picker-date-panel { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif; } .ant-picker-header-view button { font-weight: 500; } .ant-picker-cell-inner::before { border-radius: 50% !important; }二、封装成组件
DateSelector.tsx文件 // DateSelector.tsx import React from 'react'; import {DatePicker, Tooltip, Spin, ConfigProvider} from 'antd'; import dayjs, { Dayjs } from 'dayjs'; import isSameOrAfter from 'dayjs/plugin/isSameOrAfter'; import isSameOrBefore from 'dayjs/plugin/isSameOrBefore'; import 'dayjs/locale/zh-cn'; import './custom-datepicker.css'; import zhCN from 'antd/locale/zh_CN'; dayjs.extend(isSameOrAfter); dayjs.extend(isSameOrBefore); export interface TrafficData { personal: string; guild: string; } interface DateSelectorProps { value?: Dayjs | null; trafficData: Record<string, TrafficData>; onChange?: (date: Dayjs | null) => void; loading?: boolean; } const DateSelector: React.FC<DateSelectorProps> = ({ value, trafficData, onChange, loading = false, }) => { const today = dayjs().startOf('day'); const sevenDaysLater = today.add(6, 'day'); const disabledDate = (current: Dayjs) => current.isBefore(today, 'day') || current.isAfter(sevenDaysLater, 'day'); const handleChange = (date: Dayjs | null) => { onChange?.(date); }; if (loading) { return <Spin tip="数据加载中..." style={{ padding: '8px 0' }} />; } return ( <ConfigProvider locale={zhCN}> {/* 设置Ant Design为中文 */} <DatePicker value={value} disabledDate={disabledDate} onChange={handleChange} dropdownClassName="custom-picker-dropdown" dateRender={current => { const dateStr = current.format('YYYY-MM-DD'); const data = trafficData[dateStr]; const isInRange = current.isSameOrAfter(today) && current.isSameOrBefore(sevenDaysLater); const isSelected = value?.isSame(current, 'day'); return ( <div className="custom-cell-wrapper"> <div className="native-cell-content">{current.date()}</div> <Tooltip title={data ? `${dayjs(dateStr).format('YYYY年M月D日')}\n可兑流量余额: ${data.personal}\n本公会可兑流量: ${data.guild}` : '无可用数据'} overlayStyle={{ whiteSpace: 'pre-line', pointerEvents: 'none', }} placement="bottom" mouseEnterDelay={0} mouseLeaveDelay={0.1} > <div className={`custom-cell ${isInRange ? 'recent-date' : ''}`}> <div className={`date-number ${isSelected ? 'selected' : ''}`}> {current.date()} </div> {data && ( <div className={`availability ${data.personal === '0%' ? 'empty' : ''}`}> 余{data.personal} </div> )} </div> </Tooltip> </div> ); }} /> </ConfigProvider> ); }; export default DateSelector; custom-datepicker.css /* custom-datepicker.css */ .custom-picker-dropdown { z-index: 1001; } .custom-cell-wrapper { position: relative; height: 100%; width: 100%; } .native-cell-content { visibility: hidden; } .custom-cell { position: absolute; top: 0; left: 0; width: 100%; height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; cursor: pointer; z-index: 2; padding: 3px 0; } .date-number { width: 24px; height: 24px; line-height: 24px; text-align: center; color: #000; transition: all 0.2s; border-radius: 50%; } .date-number.selected { background: #1890ff; color: white !important; } .recent-date:hover .date-number:not(.selected) { color: #1890ff; } .availability { font-size: 10px; line-height: 14px; color: #1890ff; margin-top: 2px; } .availability.empty { color: #ff4d4f !important; } .ant-picker-cell-inner { padding: 0 !important; height: 100% !important; } .ant-picker-cell:hover .ant-picker-cell-inner { background: transparent !important; } /* 添加中文面板样式调整 */ .ant-picker-date-panel { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif; } .ant-picker-header-view button { font-weight: 500; } .ant-picker-cell-inner::before { border-radius: 50% !important; } index.tsx文件 // 使用示例 ParentComponent.tsx import React, { useState, useEffect } from 'react'; import DateSelector, { TrafficData } from './DateSelector'; import dayjs from 'dayjs'; const ParentComponent: React.FC = () => { const [selectedDate, setSelectedDate] = useState<dayjs.Dayjs | null>(null); const [trafficData, setTrafficData] = useState<Record<string, TrafficData>>({}); const [loading, setLoading] = useState(true); const today = dayjs().startOf('day'); // useEffect(() => { // // 模拟API调用 // const mockFetchData = async () => { // const mockData = { // [dayjs().format('YYYY-MM-DD')]: { // personal: '80%', // guild: '100,000', // }, // [dayjs().add(1, 'day') // .format('YYYY-MM-DD')]: { // personal: '50%', // guild: '75,000', // }, // }; // // await new Promise(resolve => setTimeout(resolve, 800)); // setTrafficData(mockData); // setLoading(false); // }; // // mockFetchData(); // }, []); useEffect(() => { const mockApi = async () => { const data: Record<string, TrafficData> = {}; Array.from({ length: 7 }).forEach((_, i) => { const date = today.add(i, 'day').format('YYYY-MM-DD'); data[date] = { personal: `${[0, 20, 30, 40, 80, 100, 50][i]}%`, guild: `${Math.floor(Math.random() * 100000).toLocaleString()}`, }; }); await new Promise(resolve => setTimeout(resolve, 500)); setTrafficData(data); setLoading(false); }; mockApi(); }, []); return ( <div style={{ padding: 24 }}> <h2>日期选择器示例</h2> <div style={{ marginBottom: 16 }}> 当前选择:{selectedDate?.format('YYYY年M月D日') || '未选择'} </div> <DateSelector value={selectedDate} trafficData={trafficData} onChange={setSelectedDate} loading={loading} /> </div> ); }; export default ParentComponent;三、生效时间选择完日期后,还需填入具体时间(或提供一个时间选择器),精确到分,默认为00:00;如选择的日期为今天,则填写的时间不能早于当前时间
// index.tsx import React, { useState, useEffect } from 'react'; import { Row, Col, TimePicker, Spin, ConfigProvider } from 'antd'; import DateSelector, { TrafficData } from './DateSelector'; import dayjs, { Dayjs } from 'dayjs'; import zhCN from 'antd/locale/zh_CN'; declare module 'dayjs' { interface Dayjs { isToday(): boolean; } } dayjs.extend((o, c) => { c.prototype.isToday = function () { return this.isSame(dayjs(), 'day'); } }); const DateTimePicker: React.FC = () => { const [selectedDate, setSelectedDate] = useState<Dayjs | null>(null); const [selectedTime, setSelectedTime] = useState<Dayjs>(dayjs().startOf('minute')); const [trafficData, setTrafficData] = useState<Record<string, TrafficData>>({}); const [loading, setLoading] = useState(true); const today = dayjs().startOf('day'); const disabledTime = (current: Dayjs | null) => { if (!current || !selectedDate?.isToday()) { return { disabledHours: () => [], disabledMinutes: () => [] }; } const now = dayjs(); return { disabledHours: () => { const currentHour = now.hour(); return Array.from({ length: currentHour }, (_, i) => i); }, disabledMinutes: (selectedHour: number) => { if (selectedHour < now.hour()) return []; return Array.from({ length: now.minute() }, (_, i) => i); }, }; }; const handleDateChange = (date: Dayjs | null) => { setSelectedDate(date); setSelectedTime(date?.isToday() ? dayjs().startOf('minute') : dayjs().startOf('day')); }; // useEffect(() => { // // 模拟API请求 // const mockData = { // [dayjs().format('YYYY-MM-DD')]: { personal: '80%', guild: '100,000' }, // [dayjs().add(1, 'day') // .format('YYYY-MM-DD')]: { personal: '50%', guild: '75,000' }, // }; // // setTimeout(() => { // setTrafficData(mockData); // setLoading(false); // }, 800); // }, []); useEffect(() => { const mockApi = async () => { const data: Record<string, TrafficData> = {}; Array.from({ length: 7 }).forEach((_, i) => { const date = today.add(i, 'day').format('YYYY-MM-DD'); data[date] = { personal: `${[0, 20, 30, 40, 80, 100, 50][i]}%`, guild: `${Math.floor(Math.random() * 100000).toLocaleString()}`, }; }); await new Promise(resolve => setTimeout(resolve, 500)); setTrafficData(data); setLoading(false); }; mockApi(); }, []); return ( <ConfigProvider locale={zhCN}> {/* 设置Ant Design为中文 */} <div style={{ padding: 24, maxWidth: 380, margin: '0 auto' }}> <h2 style={{ marginBottom: 24 }}>预约时间选择</h2> <Row gutter={24} align="middle"> <Col span={12}> {/*<div style={{ marginBottom: 8 }}>选择日期</div>*/} <DateSelector value={selectedDate} trafficData={trafficData} onChange={handleDateChange} loading={loading} /> </Col> <Col span={12}> {/*<div style={{ marginBottom: 8 }}>选择时间</div>*/} <TimePicker value={selectedTime} format="HH:mm" minuteStep={1} disabledTime={disabledTime} onChange={time => setSelectedTime(time || dayjs().startOf('minute'))} placeholder="请选择时间" disabled={!selectedDate} allowClear={false} showNow={false} style={{ width: '100%' }} /> </Col> </Row> <div style={{ marginTop: 24, padding: 16, background: '#f5f5f5', borderRadius: 4 }}> 已选择时间: {selectedDate ? `${selectedDate.format('YYYY年MM月DD日')} ${selectedTime.format('HH:mm')}` : '请先选择日期'} </div> </div> </ConfigProvider> ); }; export default DateTimePicker;Reactantd的datePicker自定义,封装成组件由讯客互联电脑硬件栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“Reactantd的datePicker自定义,封装成组件”
上一篇
pgpg_prewarm用法