import { useState, useRef, useEffect } from 'react'; import styles from './DatePicker.module.css'; interface DatePickerProps { value: string; // 'YYYY-MM-DD' or '' onChange: (value: string) => void; placeholder?: string; } export function DatePicker({ value, onChange, placeholder = '选择日期' }: DatePickerProps) { const [open, setOpen] = useState(false); const wrapperRef = useRef(null); const today = new Date(); const selected = value ? new Date(value + 'T00:00:00') : null; const [viewYear, setViewYear] = useState(selected?.getFullYear() ?? today.getFullYear()); const [viewMonth, setViewMonth] = useState(selected?.getMonth() ?? today.getMonth()); // Close on outside click useEffect(() => { if (!open) return; const handle = (e: MouseEvent) => { if (wrapperRef.current && !wrapperRef.current.contains(e.target as Node)) { setOpen(false); } }; document.addEventListener('mousedown', handle); return () => document.removeEventListener('mousedown', handle); }, [open]); const prevMonth = () => { if (viewMonth === 0) { setViewMonth(11); setViewYear(viewYear - 1); } else setViewMonth(viewMonth - 1); }; const nextMonth = () => { if (viewMonth === 11) { setViewMonth(0); setViewYear(viewYear + 1); } else setViewMonth(viewMonth + 1); }; // Build calendar grid const firstDay = new Date(viewYear, viewMonth, 1).getDay(); // 0=Sun const daysInMonth = new Date(viewYear, viewMonth + 1, 0).getDate(); const daysInPrevMonth = new Date(viewYear, viewMonth, 0).getDate(); const cells: { day: number; month: number; year: number; isOther: boolean }[] = []; // Previous month padding for (let i = firstDay - 1; i >= 0; i--) { const m = viewMonth === 0 ? 11 : viewMonth - 1; const y = viewMonth === 0 ? viewYear - 1 : viewYear; cells.push({ day: daysInPrevMonth - i, month: m, year: y, isOther: true }); } // Current month for (let d = 1; d <= daysInMonth; d++) { cells.push({ day: d, month: viewMonth, year: viewYear, isOther: false }); } // Next month padding const remaining = 42 - cells.length; for (let d = 1; d <= remaining; d++) { const m = viewMonth === 11 ? 0 : viewMonth + 1; const y = viewMonth === 11 ? viewYear + 1 : viewYear; cells.push({ day: d, month: m, year: y, isOther: true }); } const fmt = (y: number, m: number, d: number) => `${y}-${String(m + 1).padStart(2, '0')}-${String(d).padStart(2, '0')}`; const todayStr = fmt(today.getFullYear(), today.getMonth(), today.getDate()); const weekDays = ['日', '一', '二', '三', '四', '五', '六']; const monthNames = ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']; return (
)} {open && (
{viewYear}年 {monthNames[viewMonth]}
{weekDays.map((d) => {d})}
{cells.map((c, i) => { const dateStr = fmt(c.year, c.month, c.day); const isToday = dateStr === todayStr; const isSelected = dateStr === value; return ( ); })}
)}
); }