import { useMemo, useState } from 'react'; import { CalendarDays, ChevronLeft, ChevronRight, X } from 'lucide-react'; export type DateRangeValue = { start?: string; end?: string; }; type DateRangeInputProps = { label?: string; value: DateRangeValue; onChange: (value: DateRangeValue) => void; }; const weekDays = ['一', '二', '三', '四', '五', '六', '日']; const presets = [ { label: '今天', days: 0 }, { label: '近7天', days: 6 }, { label: '近30天', days: 29 }, ]; function formatDate(date: Date) { const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); return `${year}-${month}-${day}`; } function parseDate(value?: string) { if (!value) { return undefined; } const [year, month, day] = value.split('-').map(Number); return new Date(year, month - 1, day); } function getMonthLabel(date: Date) { return `${date.getFullYear()}年${date.getMonth() + 1}月`; } function isSameDate(left: Date, right?: Date) { if (!right) { return false; } return formatDate(left) === formatDate(right); } function getCalendarDays(viewDate: Date) { const year = viewDate.getFullYear(); const month = viewDate.getMonth(); const firstDate = new Date(year, month, 1); const firstWeekday = (firstDate.getDay() + 6) % 7; const startDate = new Date(year, month, 1 - firstWeekday); return Array.from({ length: 42 }, (_, index) => { const date = new Date(startDate); date.setDate(startDate.getDate() + index); return { date, dateString: formatDate(date), inCurrentMonth: date.getMonth() === month, }; }); } export function DateRangeInput({ label, value, onChange }: DateRangeInputProps) { const [open, setOpen] = useState(false); const [viewDate, setViewDate] = useState(() => parseDate(value.start) ?? new Date()); const startDate = parseDate(value.start); const endDate = parseDate(value.end); const calendarDays = useMemo(() => getCalendarDays(viewDate), [viewDate]); const displayValue = useMemo(() => { if (value.start && value.end) { return `${value.start} 至 ${value.end}`; } if (value.start) { return `${value.start} 至 ...`; } return '选择提交时间区间'; }, [value.end, value.start]); function moveMonth(offset: number) { setViewDate((current) => new Date(current.getFullYear(), current.getMonth() + offset, 1)); } function applyPreset(days: number) { const end = new Date(); const start = new Date(); start.setDate(end.getDate() - days); onChange({ start: formatDate(start), end: formatDate(end) }); setViewDate(start); } function selectDate(dateString: string) { if (!value.start || value.end) { onChange({ start: dateString, end: undefined }); return; } if (dateString < value.start) { onChange({ start: dateString, end: value.start }); return; } onChange({ start: value.start, end: dateString }); } return (
{label ? {label} : null} {open ? (
{presets.map((item) => ( ))}
{getMonthLabel(viewDate)}
{weekDays.map((day) => {day})}
{calendarDays.map((item) => { const isStart = isSameDate(item.date, startDate); const isEnd = isSameDate(item.date, endDate); const isInRange = Boolean(startDate && endDate && item.date > startDate && item.date < endDate); return ( ); })}
) : null}
); }