diff --git a/src/components/Picker/CalendarDialog/DialogCalendarCard.tsx b/src/components/Picker/CalendarDialog/DialogCalendarCard.tsx index 35d07c5..449b28c 100644 --- a/src/components/Picker/CalendarDialog/DialogCalendarCard.tsx +++ b/src/components/Picker/CalendarDialog/DialogCalendarCard.tsx @@ -1,17 +1,19 @@ -import React, { useState, useEffect, useRef } from 'react' -import CommonPopup from '@/components/CommonPopup' -import { View } from '@tarojs/components' -import CalendarUI, { CalendarUIRef } from '@/components/Picker/CalendarUI/CalendarUI' -import { PickerCommon, PickerCommonRef } from '@/components/Picker' -import dayjs from 'dayjs' -import styles from './index.module.scss' +import React, { useState, useEffect, useRef } from "react"; +import CommonPopup from "@/components/CommonPopup"; +import { View } from "@tarojs/components"; +import CalendarUI, { + CalendarUIRef, +} from "@/components/Picker/CalendarUI/CalendarUI"; +import { PickerCommon, PickerCommonRef } from "@/components/Picker"; +import dayjs from "dayjs"; +import styles from "./index.module.scss"; export interface DialogCalendarCardProps { - value?: Date | Date[] - searchType?: 'single' | 'range' | 'multiple' - onChange?: (date: Date | Date[]) => void - visible: boolean - onClose: () => void - title?: React.ReactNode + value?: Date | Date[]; + searchType?: "single" | "range" | "multiple"; + onChange?: (date: Date | Date[]) => void; + visible: boolean; + onClose: () => void; + title?: React.ReactNode; } const DialogCalendarCard: React.FC = ({ @@ -22,61 +24,73 @@ const DialogCalendarCard: React.FC = ({ value, onChange, }) => { - const [selected, setSelected] = useState(value || new Date()) + const [selected, setSelected] = useState(value || new Date()); + const [selectedBackup, setSelectedBackup] = useState( + Array.isArray(value) ? [...(value as Date[])] : [value as Date] + ); const calendarRef = useRef(null); - const [type, setType] = useState<'year' | 'month' | 'time'>('year'); - const [selectedHour, setSelectedHour] = useState(8) - const [selectedMinute, setSelectedMinute] = useState(0) + const [type, setType] = useState<"year" | "month" | "time">("year"); + const [selectedHour, setSelectedHour] = useState(8); + const [selectedMinute, setSelectedMinute] = useState(0); const pickerRef = useRef(null); const hourMinutePickerRef = useRef(null); - const [pendingJump, setPendingJump] = useState<{ year: number; month: number } | null>(null) + const [pendingJump, setPendingJump] = useState<{ + year: number; + month: number; + } | null>(null); const handleConfirm = () => { - if (type === 'year') { - if (searchType === 'range') { + if (type === "year") { + if (searchType === "range") { if (onChange) onChange(selected); onClose(); return; } // 年份选择完成后,进入月份选择 - setType('time') - } else if (type === 'month') { + setType("time"); + } else if (type === "month") { // 月份选择完成后,进入时间选择 - const value = pickerRef.current?.getValue() + const value = pickerRef.current?.getValue(); if (value) { - const year = value[0] as number - const month = value[1] as number - if (searchType === 'range') { - const delta = calculateMonthDifference(selected as Date, new Date(year, month - 1, 1)) - console.log('xxxxx', calendarRef.current) - setTimeout(() => { - calendarRef.current?.gotoMonth(delta) - }, 50) - } else { - setSelected(new Date(year, month - 1, 1)) + const year = value[0] as number; + const month = value[1] as number; + setPendingJump({ year, month }); + setType("year"); + if (searchType === "range") { + const delta = calculateMonthDifference( + selected as Date, + new Date(year, month - 1, 1) + ); + calendarRef.current?.gotoMonth(delta); + return; } - setPendingJump({ year, month }) + setSelected(new Date(year, month - 1, 1)); } - setType('year') - } else if (type === 'time') { + } else if (type === "time") { // 时间选择完成后,调用onNext回调 - const value = hourMinutePickerRef.current?.getValue() + const value = hourMinutePickerRef.current?.getValue(); if (value) { - const hour = value[0] as number - const minute = value[1] as number - setSelectedHour(hour) - setSelectedMinute(minute) - const hours = hour.toString().padStart(2, '0') - const minutes = minute.toString().padStart(2, '0') - const finalDate = new Date(dayjs(selected as Date).format('YYYY-MM-DD') + ' ' + hours + ':' + minutes) - if (onChange) onChange(finalDate) + const hour = value[0] as number; + const minute = value[1] as number; + setSelectedHour(hour); + setSelectedMinute(minute); + const hours = hour.toString().padStart(2, "0"); + const minutes = minute.toString().padStart(2, "0"); + const finalDate = new Date( + dayjs(selected as Date).format("YYYY-MM-DD") + + " " + + hours + + ":" + + minutes + ); + if (onChange) onChange(finalDate); } - onClose() + onClose(); } - } + }; const calculateMonthDifference = (date1, date2) => { if (!(date1 instanceof Date) || !(date2 instanceof Date)) { - throw new Error('Both arguments must be Date objects'); + throw new Error("Both arguments must be Date objects"); } let months = (date2.getFullYear() - date1.getFullYear()) * 12; @@ -84,63 +98,76 @@ const DialogCalendarCard: React.FC = ({ months += date2.getMonth(); return months; - } + }; const handleChange = (d: Date | Date[]) => { - console.log('handleChange', d) - if (searchType === 'range') { + if (searchType === "range") { if (Array.isArray(d)) { if (d.length === 2) { - setSelected(d as Date[]) - return + return; + } else if (d.length === 1) { + debugger; + if (selectedBackup.length === 0 || selectedBackup.length === 2) { + setSelected([]); + setSelectedBackup([...d]); + } else { + setSelected( + [...selectedBackup, d[0]].sort( + (a, b) => a.getTime() - b.getTime() + ) + ); + setSelectedBackup([]); + } } + return; } } if (Array.isArray(d)) { - setSelected(d[0]) + setSelected(d[0]); } else { - setSelected(d) + setSelected(d); } - } + }; const onHeaderClick = (date: Date) => { - console.log('onHeaderClick', date) - setSelected(date) - setType('month') - } + console.log("onHeaderClick", date); + setSelected(date); + setType("month"); + }; const getConfirmText = () => { - if (type === 'time' || type === 'month') return '完成' - return '下一步' - } + if (type === "time" || type === "month" || searchType === "range") + return "完成"; + return "下一步"; + }; const handleDateTimePickerChange = (value: (string | number)[]) => { - const year = value[0] as number - const month = value[1] as number - setSelected(new Date(year, month - 1, 1)) - } + const year = value[0] as number; + const month = value[1] as number; + setSelected(new Date(year, month - 1, 1)); + }; const dialogClose = () => { - if (type === 'month') { - setType('year') - } else if (type === 'time') { - setType('year') + if (type === "month") { + setType("year"); + } else if (type === "time") { + setType("year"); } else { - onClose() + onClose(); } - } + }; useEffect(() => { if (visible && value) { - setSelected(value || new Date()) - setSelectedHour(value ? dayjs(value as Date).hour() : 8) - setSelectedMinute(value ? dayjs(value as Date).minute() : 0) + setSelected(value || new Date()); + setSelectedHour(value ? dayjs(value as Date).hour() : 8); + setSelectedMinute(value ? dayjs(value as Date).minute() : 0); } - }, [value, visible]) + }, [value, visible]); useEffect(() => { - if (type === 'year' && pendingJump && calendarRef.current) { - calendarRef.current.jumpTo(pendingJump.year, pendingJump.month) - setPendingJump(null) + if (type === "year" && pendingJump && calendarRef.current) { + calendarRef.current.jumpTo(pendingJump.year, pendingJump.month); + setPendingJump(null); } - }, [type, pendingJump]) + }, [type, pendingJump]); - console.log([selectedHour, selectedMinute], 'selectedHour, selectedMinute'); + console.log([selectedHour, selectedMinute], "selectedHour, selectedMinute"); return ( = ({ showHeader={!!title} title={title} hideFooter={false} - cancelText='取消' + cancelText="取消" confirmText={getConfirmText()} onConfirm={handleConfirm} - position='bottom' + position="bottom" round zIndex={1000} > - { - type === 'year' && - + {type === "year" && ( + = ({ onChange={handleChange} showQuickActions={false} onHeaderClick={onHeaderClick} - /> - } - { - type === 'month' && + + )} + {type === "month" && ( + - - } - { - type === 'time' && - - } + )} - ) -} + ); +}; -export default DialogCalendarCard +export default DialogCalendarCard; diff --git a/src/components/Picker/CalendarUI/CalendarUI.tsx b/src/components/Picker/CalendarUI/CalendarUI.tsx index 701d6fa..08f2d4a 100644 --- a/src/components/Picker/CalendarUI/CalendarUI.tsx +++ b/src/components/Picker/CalendarUI/CalendarUI.tsx @@ -1,227 +1,291 @@ -import React, { useState, useEffect, useRef, useImperativeHandle } from 'react' -import { CalendarCard } from '@nutui/nutui-react-taro' -import { View, Text, Image } from '@tarojs/components' -import images from '@/config/images' -import styles from './index.module.scss' -import { getMonth, getWeekend, getWeekendOfCurrentWeek } from '@/utils/timeUtils' -import { PopupPicker } from '@/components/Picker/index' -import dayjs from 'dayjs' +import React, { useState, useEffect, useRef, useImperativeHandle } from "react"; +import { CalendarCard } from "@nutui/nutui-react-taro"; +import { View, Text, Image } from "@tarojs/components"; +import images from "@/config/images"; +import styles from "./index.module.scss"; +import { + getMonth, + getWeekend, + getWeekendOfCurrentWeek, +} from "@/utils/timeUtils"; +import { PopupPicker } from "@/components/Picker/index"; +import dayjs from "dayjs"; interface NutUICalendarProps { - type?: 'single' | 'range' | 'multiple' - value?: string | Date | String[] | Date[] - defaultValue?: string | string[] - onChange?: (value: Date | Date[]) => void, - isBorder?: boolean - showQuickActions?: boolean, - onHeaderClick?: (date: Date) => void + type?: "single" | "range" | "multiple"; + value?: string | Date | String[] | Date[]; + defaultValue?: string | string[]; + onChange?: (value: Date | Date[]) => void; + isBorder?: boolean; + showQuickActions?: boolean; + onHeaderClick?: (date: Date) => void; } export interface CalendarUIRef { - jumpTo: (year: number, month: number) => void, - gotoMonth: (delta: number) => void, + jumpTo: (year: number, month: number) => void; + gotoMonth: (delta: number) => void; } -const NutUICalendar = React.forwardRef(({ - type = 'single', - value, - onChange, - isBorder = false, - showQuickActions = true, - onHeaderClick -}, ref) => { - // 根据类型初始化选中值 - // const getInitialValue = (): Date | Date[] => { - // console.log(value,defaultValue,'today') - - // if (typeof value === 'string' && value) { - // return new Date(value) - // } - // if (Array.isArray(value) && value.length > 0) { - // return value.map(item => new Date(item)) - // } - // if (typeof defaultValue === 'string' && defaultValue) { - // return new Date(defaultValue) - // } - // if (Array.isArray(defaultValue) && defaultValue.length > 0) { - // return defaultValue.map(item => new Date(item)) - // } - // const today = new Date(); - // if (type === 'multiple') { - // return [today] - // } - // return today - // } - const startOfMonth = (date: Date) => new Date(date.getFullYear(), date.getMonth(), 1) - - const [selectedValue, setSelectedValue] = useState() - const [current, setCurrent] = useState(startOfMonth(new Date())) - const calendarRef = useRef(null) - const [visible, setvisible] = useState(false) - console.log('current', current) - // 当外部 value 变化时更新内部状态 - useEffect(() => { - if (Array.isArray(value) && value.length > 0) { - setSelectedValue(value.map(item => new Date(item))) - setCurrent(new Date(value[0] as Date)) - } - if ((typeof value === 'string' || value instanceof Date) && value) { - setSelectedValue(new Date(value)) - setCurrent(new Date(value)) - } - }, [value]) - - useImperativeHandle(ref, () => ({ - jumpTo: (year: number, month: number) => { - calendarRef.current?.jumpTo(year, month) +const NutUICalendar = React.forwardRef( + ( + { + type = "single", + value, + onChange, + isBorder = false, + showQuickActions = true, + onHeaderClick, }, - gotoMonth: (delta: number) => { - gotoMonth(delta) - } - })) + ref + ) => { + // 根据类型初始化选中值 + // const getInitialValue = (): Date | Date[] => { + // console.log(value,defaultValue,'today') - const handleDateChange = (newValue: any) => { - setSelectedValue(newValue) - onChange?.(newValue as any) - } - const formatHeader = (date: Date) => `${getMonth(date)}` + // if (typeof value === 'string' && value) { + // return new Date(value) + // } + // if (Array.isArray(value) && value.length > 0) { + // return value.map(item => new Date(item)) + // } + // if (typeof defaultValue === 'string' && defaultValue) { + // return new Date(defaultValue) + // } + // if (Array.isArray(defaultValue) && defaultValue.length > 0) { + // return defaultValue.map(item => new Date(item)) + // } + // const today = new Date(); + // if (type === 'multiple') { + // return [today] + // } + // return today + // } + const startOfMonth = (date: Date) => + new Date(date.getFullYear(), date.getMonth(), 1); - const handlePageChange = (data: { year: number; month: number }) => { - // 月份切换时的处理逻辑,如果需要的话 - console.log('月份切换:', data) - } - - const gotoMonth = (delta: number) => { - console.log('aaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbb', delta) - const base = current instanceof Date ? new Date(current) : new Date() - base.setMonth(base.getMonth() + delta) - const next = startOfMonth(base) - setCurrent(next) - // 同步底部 CalendarCard 的月份 - try { - calendarRef.current?.jump?.(delta) - } catch (e) { - console.warn('CalendarCardRef jump 调用失败', e) - } - handlePageChange({ year: next.getFullYear(), month: next.getMonth() + 1 }) - } - - const handleHeaderClick = () => { - console.log('handleHeaderClick', current) - onHeaderClick && onHeaderClick(current) - setvisible(true) - } - - - - const syncMonthTo = (anchor: Date) => { - // 计算从 current 到目标 anchor 所在月份的偏移,调用 jump(delta) - const monthsDelta = (anchor.getFullYear() - current.getFullYear()) * 12 + (anchor.getMonth() - current.getMonth()) - if (monthsDelta !== 0) { - gotoMonth(monthsDelta) - } - } - const renderDay = (day: any) => { - const { date, month, year } = day; - const today = new Date() - if (date === today.getDate() && month === today.getMonth() + 1 && year === today.getFullYear()) { - return ( - - {date} - - ) - } - return date - } - - const selectWeekend = () => { - const [start, end] = getWeekend() - setSelectedValue([start, end]) - syncMonthTo(start) - onChange?.([start, end]) - } - const selectWeek = () => { - const dayList = getWeekendOfCurrentWeek(7) - setSelectedValue(dayList) - syncMonthTo(dayList[0]) - onChange?.(dayList) - } - const selectMonth = () => { - const dayList = getWeekendOfCurrentWeek(30) - setSelectedValue(dayList) - syncMonthTo(dayList[0]) - onChange?.(dayList) - } - - const handleMonthChange = (value: any) => { - const [year, month] = value; - const newDate = new Date(year, month - 1, 1); - setCurrent(newDate); - calendarRef.current?.jumpTo(year, month) - } - - - return ( - - {/* 快速操作行 */} - { - showQuickActions && - - 本周末 - 一周内 - 一个月 - + const [selectedValue, setSelectedValue] = useState(); + const [current, setCurrent] = useState(startOfMonth(new Date())); + const calendarRef = useRef(null); + const [visible, setvisible] = useState(false); + console.log("current", current); + // 当外部 value 变化时更新内部状态 + useEffect(() => { + if (Array.isArray(value) && value.length > 0) { + setSelectedValue(value.map((item) => new Date(item))); + setCurrent(new Date(value[0] as Date)); } - - { - type === 'range' && ( - - {value?.[0] ? dayjs(value?.[0] as Date).format('YYYY-MM-DD') : '起始时间'} + if ((typeof value === "string" || value instanceof Date) && value) { + setSelectedValue(new Date(value)); + setCurrent(new Date(value)); + } + }, [value]); + + useImperativeHandle(ref, () => ({ + jumpTo: (year: number, month: number) => { + calendarRef.current?.jumpTo(year, month); + }, + gotoMonth: (delta: number) => { + gotoMonth(delta); + }, + })); + + const handleDateChange = (newValue: any) => { + console.log("aaaaaaaaaaaaaaaaaaaaaa", newValue); + setSelectedValue(newValue); + onChange?.(newValue as any); + }; + const formatHeader = (date: Date) => `${getMonth(date)}`; + + const handlePageChange = (data: { year: number; month: number }) => { + // 月份切换时的处理逻辑,如果需要的话 + console.log("月份切换:", data); + }; + + const handleDayClick = (day: any) => { + const { type, year, month, date } = day; + if (type === "next") return; + onChange?.([new Date(year, month - 1, date)]); + }; + + const gotoMonth = (delta: number) => { + const base = current instanceof Date ? new Date(current) : new Date(); + base.setMonth(base.getMonth() + delta); + const next = startOfMonth(base); + setCurrent(next); + // 同步底部 CalendarCard 的月份 + try { + calendarRef.current?.jump?.(delta); + } catch (e) { + console.warn("CalendarCardRef jump 调用失败", e); + } + handlePageChange({ + year: next.getFullYear(), + month: next.getMonth() + 1, + }); + }; + + const handleHeaderClick = () => { + onHeaderClick && onHeaderClick(current); + setvisible(true); + }; + + const syncMonthTo = (anchor: Date) => { + // 计算从 current 到目标 anchor 所在月份的偏移,调用 jump(delta) + const monthsDelta = + (anchor.getFullYear() - current.getFullYear()) * 12 + + (anchor.getMonth() - current.getMonth()); + if (monthsDelta !== 0) { + gotoMonth(monthsDelta); + } + }; + const renderDay = (day: any) => { + const { date, month, year } = day; + const today = new Date(); + if ( + date === today.getDate() && + month === today.getMonth() + 1 && + year === today.getFullYear() + ) { + return {date}; + } + return date; + }; + + const selectWeekend = () => { + const [start, end] = getWeekend(); + setSelectedValue([start, end]); + syncMonthTo(start); + onChange?.([start, end]); + }; + const selectWeek = () => { + const dayList = getWeekendOfCurrentWeek(7); + setSelectedValue(dayList); + syncMonthTo(dayList[0]); + onChange?.(dayList); + }; + const selectMonth = () => { + const dayList = getWeekendOfCurrentWeek(30); + setSelectedValue(dayList); + syncMonthTo(dayList[0]); + onChange?.(dayList); + }; + + const handleMonthChange = (value: any) => { + const [year, month] = value; + const newDate = new Date(year, month - 1, 1); + setCurrent(newDate); + calendarRef.current?.jumpTo(year, month); + }; + + return ( + + {/* 快速操作行 */} + {showQuickActions && ( + + + 本周末 + + + 一周内 + + + 一个月 + + + )} + + {type === "range" && ( + + + {(value as Date[]).length === 2 + ? dayjs(value?.[0] as Date).format("YYYY-MM-DD") + : "起始时间"} + - {value?.[1] ? dayjs(value?.[1] as Date).format('YYYY-MM-DD') : '结束时间'} + + {(value as Date[]).length === 2 + ? dayjs(value?.[1] as Date).format("YYYY-MM-DD") + : "结束时间"} + - ) - } - {/* 自定义头部显示周一到周日 */} - - - {formatHeader(current as Date)} - gotoMonth(1)} /> - - - gotoMonth(-1)}> - + )} + {/* 自定义头部显示周一到周日 */} + + + + {formatHeader(current as Date)} + + gotoMonth(1)} + /> - gotoMonth(1)}> - + + gotoMonth(-1)} + > + + + gotoMonth(1)} + > + + - - - {['周日', '周一', '周二', '周三', '周四', '周五', '周六'].map((day) => ( - - {day} - - ))} - + + {["周日", "周一", "周二", "周三", "周四", "周五", "周六"].map( + (day) => ( + + {day} + + ) + )} + - {/* NutUI CalendarCard 组件 */} - + {/* NutUI CalendarCard 组件 */} + + + {visible && ( + handleMonthChange(value)} + /> + )} - {visible && handleMonthChange(value)} />} - - ) -}) + ); + } +); -export default NutUICalendar +export default NutUICalendar; diff --git a/src/user_pages/downloadBill/index.tsx b/src/user_pages/downloadBill/index.tsx index cee7a4e..0dd43fd 100644 --- a/src/user_pages/downloadBill/index.tsx +++ b/src/user_pages/downloadBill/index.tsx @@ -1,7 +1,7 @@ import React, { useState, useEffect } from "react"; import { View, Text, Button } from "@tarojs/components"; import Taro, { useDidShow } from "@tarojs/taro"; -import dayjs from 'dayjs' +import dayjs from "dayjs"; import "./index.scss"; import { DialogCalendarCard } from "@/components/index"; @@ -40,6 +40,13 @@ const DownloadBill: React.FC = () => { }; const selectDateRange = (range: string) => { + if (dateType === range) { + if (range === "custom") { + setVisible(true); + return; + } + return; + } switch (range) { case "week": setDateType("week"); @@ -56,7 +63,9 @@ const DownloadBill: React.FC = () => { break; } }; - const [currentTimeValue, setCurrentTimeValue] = useState(new Date()); + const [currentTimeValue, setCurrentTimeValue] = useState( + new Date() + ); const handleConfirm = (val: Date[]) => { setCurrentTimeValue(val); const [start, end] = val;