import { useRef, useState } from 'react'; import { ArrowRight, CalendarDays, Check, ChevronDown, ChevronLeft, ChevronRight, X } from 'lucide-react'; import { getRecentFlowRange, getWeeklyFlowRange, type DateRange } from '../../model'; const weekdays = ['一', '二', '三', '四', '五', '六', '日']; const ymd = (date: Date) => `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`; const parseDate = (value: string) => new Date(`${value}T12:00:00`); const shiftDay = (date: Date, offset: number) => new Date(date.getFullYear(), date.getMonth(), date.getDate() + offset); const dayCount = (start: string, end: string) => Math.round((Date.parse(`${end}T00:00:00Z`) - Date.parse(`${start}T00:00:00Z`)) / 86400000) + 1; export function FlowDateRangePicker({ value, onChange }: { value: DateRange; onChange: (range: DateRange) => void }) { const dialog = useRef(null); const [draft, setDraft] = useState(value); const [selecting, setSelecting] = useState<'start' | 'end'>('start'); const [month, setMonth] = useState(() => parseDate(value.start)); const [hovered, setHovered] = useState(''); const today = ymd(new Date()); const count = draft.end ? dayCount(draft.start, draft.end) : 0; const valid = count > 0 && count <= 370; const monthStart = new Date(month.getFullYear(), month.getMonth(), 1); const previewEnd = draft.end || hovered || draft.start; const rangeStart = draft.start < previewEnd ? draft.start : previewEnd; const rangeEnd = draft.start > previewEnd ? draft.start : previewEnd; const quickPicks = [ { label: '今天', range: { start: today, end: today } }, { label: '昨天', range: { start: ymd(shiftDay(new Date(), -1)), end: ymd(shiftDay(new Date(), -1)) } }, { label: '业务周', range: getWeeklyFlowRange() }, { label: '近 7 天', range: getRecentFlowRange() }, { label: '近 30 天', range: { start: ymd(shiftDay(new Date(), -29)), end: today } }, ]; const open = () => { setDraft(value); setSelecting('start'); setMonth(parseDate(value.start)); setHovered(''); dialog.current?.showModal(); }; const choose = (date: string) => { if (selecting === 'start') { setDraft({ start: date, end: '' }); setSelecting('end'); } else { setDraft(date < draft.start ? { start: date, end: draft.start } : { start: draft.start, end: date }); setSelecting('start'); } setHovered(''); }; const calendar = (offset: number) => { const current = new Date(monthStart.getFullYear(), monthStart.getMonth() + offset, 1); const year = current.getFullYear(), m = current.getMonth(); const padding = (current.getDay() + 6) % 7; const length = new Date(year, m + 1, 0).getDate(); return
{year} 年 {m + 1} 月
{weekdays.map(day =>
{day}
)} {Array.from({ length: padding }, (_, i) =>
)} {Array.from({ length }, (_, i) => { const date = ymd(new Date(year, m, i + 1)); const endpoint = date === draft.start || date === draft.end; const inRange = date >= rangeStart && date <= rangeEnd; return ; })}
; }; return <> { if (e.target === e.currentTarget) dialog.current?.close(); }} className="fixed inset-0 m-auto max-h-[90dvh] w-[calc(100%-24px)] max-w-[680px] overflow-y-auto rounded-3xl border-0 bg-white p-0 text-slate-700 shadow-2xl backdrop:bg-slate-950/30 backdrop:backdrop-blur-sm">

选择统计日期

先选开始日期,再选结束日期

{quickPicks.map(pick => )}
{(['start', 'end'] as const).map(field => )}
setHovered('')}>{calendar(0)}{calendar(1)}
370 ? 'text-red-500' : 'text-slate-400'}`}>{!draft.end ? '请选择结束日期' : count > 370 ? '最多选择 370 天' : `已选 ${count} 天(包含首尾)`}
; }