From d99d3d87a92dfa431f2cb533f15384016ac940b5 Mon Sep 17 00:00:00 2001 From: Ultrame <1019265060@qq.com> Date: Thu, 25 Sep 2025 00:10:24 +0800 Subject: [PATCH] =?UTF-8?q?=E6=97=A5=E6=9C=9F=E8=8C=83=E5=9B=B4=E9=80=89?= =?UTF-8?q?=E6=8B=A9=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CalendarDialog/DialogCalendarCard.tsx | 73 ++- .../Picker/CalendarUI/CalendarUI.tsx | 105 +++-- .../Picker/CalendarUI/index.module.scss | 446 ++++++++++-------- src/user_pages/downloadBill/index.tsx | 8 +- 4 files changed, 374 insertions(+), 258 deletions(-) diff --git a/src/components/Picker/CalendarDialog/DialogCalendarCard.tsx b/src/components/Picker/CalendarDialog/DialogCalendarCard.tsx index 665e7be..9961bc9 100644 --- a/src/components/Picker/CalendarDialog/DialogCalendarCard.tsx +++ b/src/components/Picker/CalendarDialog/DialogCalendarCard.tsx @@ -6,7 +6,7 @@ import { PickerCommon, PickerCommonRef } from '@/components/Picker' import dayjs from 'dayjs' import styles from './index.module.scss' export interface DialogCalendarCardProps { - value?: Date + value?: Date | Date[] searchType?: 'single' | 'range' | 'multiple' onChange?: (date: Date | Date[]) => void visible: boolean @@ -22,7 +22,7 @@ const DialogCalendarCard: React.FC = ({ value, onChange, }) => { - const [selected, setSelected] = useState(value || new Date()) + const [selected, setSelected] = useState(value || new Date()) const calendarRef = useRef(null); const [type, setType] = useState<'year' | 'month' | 'time'>('year'); const [selectedHour, setSelectedHour] = useState(8) @@ -32,6 +32,11 @@ const DialogCalendarCard: React.FC = ({ const [pendingJump, setPendingJump] = useState<{ year: number; month: number } | null>(null) const handleConfirm = () => { if (type === 'year') { + if (searchType === 'range') { + if (onChange) onChange(selected); + onClose(); + return; + } // 年份选择完成后,进入月份选择 setType('time') } else if (type === 'month') { @@ -40,7 +45,13 @@ const DialogCalendarCard: React.FC = ({ if (value) { const year = value[0] as number const month = value[1] as number - setSelected(new Date(year, month - 1, 1)) + if (searchType === 'range') { + const delta = calculateMonthDifference(selected as Date, new Date(year, month - 1, 1)) + console.log('xxxxx', calendarRef.current) + calendarRef.current?.gotoMonth(delta) + } else { + setSelected(new Date(year, month - 1, 1)) + } setPendingJump({ year, month }) } setType('year') @@ -54,18 +65,31 @@ const DialogCalendarCard: React.FC = ({ setSelectedMinute(minute) const hours = hour.toString().padStart(2, '0') const minutes = minute.toString().padStart(2, '0') - const finalDate = new Date(dayjs(selected).format('YYYY-MM-DD') + ' ' + hours + ':' + minutes) + const finalDate = new Date(dayjs(selected as Date).format('YYYY-MM-DD') + ' ' + hours + ':' + minutes) if (onChange) onChange(finalDate) } onClose() } } + const calculateMonthDifference = (date1, date2) => { + if (!(date1 instanceof Date) || !(date2 instanceof Date)) { + throw new Error('Both arguments must be Date objects'); + } + + let months = (date2.getFullYear() - date1.getFullYear()) * 12; + months -= date1.getMonth(); + months += date2.getMonth(); + + return months; + } + const handleChange = (d: Date | Date[]) => { + console.log('handleChange', d) if (searchType === 'range') { if (Array.isArray(d)) { if (d.length === 2) { - onChange?.(d as Date[]) + setSelected(d as Date[]) return } } @@ -77,6 +101,7 @@ const DialogCalendarCard: React.FC = ({ } } const onHeaderClick = (date: Date) => { + console.log('onHeaderClick', date) setSelected(date) setType('month') } @@ -101,8 +126,8 @@ const DialogCalendarCard: React.FC = ({ useEffect(() => { if (visible && value) { setSelected(value || new Date()) - setSelectedHour(value ? dayjs(value).hour() : 8) - setSelectedMinute(value ? dayjs(value).minute() : 0) + setSelectedHour(value ? dayjs(value as Date).hour() : 8) + setSelectedMinute(value ? dayjs(value as Date).minute() : 0) } }, [value, visible]) @@ -132,32 +157,32 @@ const DialogCalendarCard: React.FC = ({ { type === 'year' && - + } - { + { type === 'month' && - } - { + } + { type === 'time' && + ref={hourMinutePickerRef} + type="hour" + value={[selectedHour, selectedMinute]} + /> - } + } ) } diff --git a/src/components/Picker/CalendarUI/CalendarUI.tsx b/src/components/Picker/CalendarUI/CalendarUI.tsx index 3454626..701d6fa 100644 --- a/src/components/Picker/CalendarUI/CalendarUI.tsx +++ b/src/components/Picker/CalendarUI/CalendarUI.tsx @@ -5,6 +5,7 @@ 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[] @@ -16,7 +17,8 @@ interface NutUICalendarProps { } export interface CalendarUIRef { - jumpTo: (year: number, month: number) => void + jumpTo: (year: number, month: number) => void, + gotoMonth: (delta: number) => void, } const NutUICalendar = React.forwardRef(({ @@ -28,27 +30,27 @@ const NutUICalendar = React.forwardRef(({ onHeaderClick }, ref) => { // 根据类型初始化选中值 -// const getInitialValue = (): Date | Date[] => { -// console.log(value,defaultValue,'today') + // 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 -// } + // 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() @@ -59,23 +61,25 @@ const NutUICalendar = React.forwardRef(({ // 当外部 value 变化时更新内部状态 useEffect(() => { if (Array.isArray(value) && value.length > 0) { - setSelectedValue(value.map(item => new Date(item))) - setCurrent(new Date(value[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)) + 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('xxxxxxxxxxxxxxxxxxxxxx', newValue) setSelectedValue(newValue) onChange?.(newValue as any) } @@ -87,6 +91,7 @@ const NutUICalendar = React.forwardRef(({ } 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) @@ -101,6 +106,7 @@ const NutUICalendar = React.forwardRef(({ } const handleHeaderClick = () => { + console.log('handleHeaderClick', current) onHeaderClick && onHeaderClick(current) setvisible(true) } @@ -115,11 +121,11 @@ const NutUICalendar = React.forwardRef(({ } } const renderDay = (day: any) => { - const { date, month, year} = day; + const { date, month, year } = day; const today = new Date() if (date === today.getDate() && month === today.getMonth() + 1 && year === today.getFullYear()) { return ( - + {date} ) @@ -160,12 +166,21 @@ const NutUICalendar = React.forwardRef(({ { showQuickActions && - 本周末 - 一周内 - 一个月 - + 本周末 + 一周内 + 一个月 + } + { + type === 'range' && ( + + {value?.[0] ? dayjs(value?.[0] as Date).format('YYYY-MM-DD') : '起始时间'} + + {value?.[1] ? dayjs(value?.[1] as Date).format('YYYY-MM-DD') : '结束时间'} + + ) + } {/* 自定义头部显示周一到周日 */} @@ -173,16 +188,16 @@ const NutUICalendar = React.forwardRef(({ gotoMonth(1)} /> - gotoMonth(-1)}> - - - gotoMonth(1)}> - - + gotoMonth(-1)}> + + + gotoMonth(1)}> + + - {[ '周日', '周一', '周二', '周三', '周四', '周五', '周六'].map((day) => ( + {['周日', '周一', '周二', '周三', '周四', '周五', '周六'].map((day) => ( {day} @@ -199,12 +214,12 @@ const NutUICalendar = React.forwardRef(({ onPageChange={handlePageChange} /> - { visible && handleMonthChange(value)}/> } + {visible && handleMonthChange(value)} />} ) }) diff --git a/src/components/Picker/CalendarUI/index.module.scss b/src/components/Picker/CalendarUI/index.module.scss index ce79dc7..ec0717a 100644 --- a/src/components/Picker/CalendarUI/index.module.scss +++ b/src/components/Picker/CalendarUI/index.module.scss @@ -1,177 +1,217 @@ .calendar-card { - background: #fff; - border-radius: 16px; - &.border{ - border-radius: 12px; - border: 0.5px solid rgba(0, 0, 0, 0.12); - margin-bottom: 6px; - padding: 12px 12px 8px; + background: #fff; + border-radius: 16px; + + &.border { + border-radius: 12px; + border: 0.5px solid rgba(0, 0, 0, 0.12); + margin-bottom: 6px; + padding: 12px 12px 8px; - } } - - .header { +} + +.header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 9px 4px 11px 4px; + height: 24px; +} + +.date-range-container { + height: 55px; + display: flex; + justify-content: space-between; + align-items: center; + border-bottom: 1px solid rgba(0, 0, 0, 0.05); + color: #000; + padding: 0 4px; + font-size: 17.68px; +} + +.date-text-placeholder { + font-family: PingFang SC; + font-weight: 600; + font-style: Semibold; + font-size: 17.68px; + color: #3C3C4399; +} + +.date-text { + color: #000; +} + +.header-left { + display: flex; + align-items: center; + gap: 6px; +} + +.header-text { + font-size: 17px; + font-weight: 600; + color: #000; +} + +.header-actions { + display: flex; + width: 60px; + + .arrow-left-container { display: flex; align-items: center; - justify-content: space-between; - padding: 9px 4px 11px 4px; - height: 24px; - } - .header-left { - display: flex; - align-items: center; - gap: 6px; - } - .header-text { - font-size: 17px; - font-weight: 600; - color: #000; - } - .header-actions { - display: flex; - width: 60px; - .arrow-left-container { - display: flex; - align-items: center; - justify-content: flex-start; - width: 50%; - flex: 1; - } - .arrow-right-container { - display: flex; - align-items: center; - justify-content: flex-end; - width: 50%; - flex: 1; - } - } - .month-arrow{ - width: 8px; - height: 24px; - } - .arrow { - width: 10px; - height: 24px; - position: relative; - } - .arrow.left { - left: 9px; - transform: rotate(-180deg); - } - - .week-row { - display: grid; - grid-template-columns: repeat(7, 1fr); - padding: 0 0 4px 0; - } - .week-item { - text-align: center; - color: rgba(60, 60, 67, 0.30); - font-size: 13px; - } - - // 新增的周一到周日头部样式 - .week-header { - display: grid; - grid-template-columns: repeat(7, 1fr); - padding: 8px 0; - } - .week-day { - text-align: center; - color: rgba(60, 60, 67, 0.30); - font-size: 14px; - font-weight: 500; - } - - .grid { - display: grid; - grid-template-columns: repeat(7, 1fr); - gap: 8px 0; - padding: 4px 0 16px; - } - .cell { - height: 44px; - display: flex; - align-items: center; - justify-content: center; - font-size: 20px; - position: relative; - } - .cell.empty { - opacity: 0; - } - .cell.disabled { - color: rgba(0,0,0,0.2); - } - .cell-text.selected { - width: 44px; - height: 44px; - border-radius: 22px; - background: rgba(0,0,0,0.9); - color: #fff; - display: flex; - align-items: center; - justify-content: center; - } - - // 时间段选择样式 - .cell-text.range-start { - width: 44px; - height: 44px; - border-radius: 22px; - background: rgba(0,0,0,0.9); - color: #fff; - display: flex; - align-items: center; - justify-content: center; - } - - .cell-text.range-end { - width: 44px; - height: 44px; - border-radius: 22px; - background: rgba(0,0,0,0.9); - color: #fff; - display: flex; - align-items: center; - justify-content: center; - } - - .cell-text.in-range { - width: 44px; - height: 44px; - border-radius: 22px; - background: rgba(0,0,0,0.1); - color: #000; - display: flex; - align-items: center; - justify-content: center; - } - - .footer { - display: flex; - gap: 12px; - } - .btn { + justify-content: flex-start; + width: 50%; flex: 1; - height: 44px; - border-radius: 22px; - background: rgba(0,0,0,0.06); + } + + .arrow-right-container { display: flex; align-items: center; - justify-content: center; - } - .btn.primary { - background: #000; - color: #fff; - } - - .hm-placeholder { - height: 240px; - display: flex; - align-items: center; - justify-content: center; + justify-content: flex-end; + width: 50%; + flex: 1; } +} + +.month-arrow { + width: 8px; + height: 24px; +} + +.arrow { + width: 10px; + height: 24px; + position: relative; +} + +.arrow.left { + left: 9px; + transform: rotate(-180deg); +} + +.week-row { + display: grid; + grid-template-columns: repeat(7, 1fr); + padding: 0 0 4px 0; +} + +.week-item { + text-align: center; + color: rgba(60, 60, 67, 0.30); + font-size: 13px; +} + +// 新增的周一到周日头部样式 +.week-header { + display: grid; + grid-template-columns: repeat(7, 1fr); + padding: 8px 0; +} + +.week-day { + text-align: center; + color: rgba(60, 60, 67, 0.30); + font-size: 14px; + font-weight: 500; +} + +.grid { + display: grid; + grid-template-columns: repeat(7, 1fr); + gap: 8px 0; + padding: 4px 0 16px; +} + +.cell { + height: 44px; + display: flex; + align-items: center; + justify-content: center; + font-size: 20px; + position: relative; +} + +.cell.empty { + opacity: 0; +} + +.cell.disabled { + color: rgba(0, 0, 0, 0.2); +} + +.cell-text.selected { + width: 44px; + height: 44px; + border-radius: 22px; + background: rgba(0, 0, 0, 0.9); + color: #fff; + display: flex; + align-items: center; + justify-content: center; +} + +// 时间段选择样式 +.cell-text.range-start { + width: 44px; + height: 44px; + border-radius: 22px; + background: rgba(0, 0, 0, 0.9); + color: #fff; + display: flex; + align-items: center; + justify-content: center; +} + +.cell-text.range-end { + width: 44px; + height: 44px; + border-radius: 22px; + background: rgba(0, 0, 0, 0.9); + color: #fff; + display: flex; + align-items: center; + justify-content: center; +} + +.cell-text.in-range { + width: 44px; + height: 44px; + border-radius: 22px; + background: rgba(0, 0, 0, 0.1); + color: #000; + display: flex; + align-items: center; + justify-content: center; +} + +.footer { + display: flex; + gap: 12px; +} + +.btn { + flex: 1; + height: 44px; + border-radius: 22px; + background: rgba(0, 0, 0, 0.06); + display: flex; + align-items: center; + justify-content: center; +} + +.btn.primary { + background: #000; + color: #fff; +} + +.hm-placeholder { + height: 240px; + display: flex; + align-items: center; + justify-content: center; +} // CalendarRange 组件样式 .calendar-range { @@ -234,50 +274,63 @@ flex: 1; } - + // 隐藏 CalendarCard 默认头部 :global { .nut-calendarcard { .nut-calendarcard-header { display: none !important; } - .nut-calendarcard-content{ - .nut-calendarcard-days{ - &:first-child{ + + .nut-calendarcard-content { + .nut-calendarcard-days { + display: grid; + grid-template-columns: repeat(7, 1fr); + gap: 4px; + justify-items: center; + + &:first-child { display: none !important; } } } - + } - .nut-calendarcard-day{ - margin-bottom:0px!important; + + .nut-calendarcard-day { + margin-bottom: 0px !important; height: 44px; - width: 44px!important; - &.active{ - background-color: #000!important; - color: #fff!important; + width: 44px !important; + + &.active { + background-color: #000 !important; + color: #fff !important; height: 44px; - border-radius: 22px!important; + border-radius: 22px !important; display: flex; align-items: center; justify-content: center; - width: 44px!important; - font-size: 24px!important; - .day-container{ - background-color: transparent!important; + width: 44px !important; + font-size: 24px !important; + + .day-container { + background-color: transparent !important; } } - &.weekend{ - color: rgb(0,0,0)!important; - &.active{ - color: #fff!important; + + &.weekend { + color: rgb(0, 0, 0) !important; + + &.active { + color: #fff !important; } } } - .nut-calendarcard-day-inner{ + + .nut-calendarcard-day-inner { font-size: 20px; - .day-container{ + + .day-container { background-color: #f5f5f5; border-radius: 22px; width: 44px; @@ -288,5 +341,22 @@ font-size: 24px; } } -} + .nut-calendarcard-day.start, + .nut-calendarcard-day.end { + background-color: #000; + border-radius: 50%; + color: #fff !important; + } + + .nut-calendarcard-day-inner .day-container { + background-color: unset; + color: unset; + } + + .nut-calendarcard-day.mid { + background-color: rgba(0, 0, 0, 0.12); + color: #000; + border-radius: 50%; + } +} \ No newline at end of file diff --git a/src/user_pages/downloadBill/index.tsx b/src/user_pages/downloadBill/index.tsx index b39b3fc..cee7a4e 100644 --- a/src/user_pages/downloadBill/index.tsx +++ b/src/user_pages/downloadBill/index.tsx @@ -1,6 +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 "./index.scss"; import { DialogCalendarCard } from "@/components/index"; @@ -56,8 +57,13 @@ const DownloadBill: React.FC = () => { } }; const [currentTimeValue, setCurrentTimeValue] = useState(new Date()); - const handleConfirm = (val) => { + const handleConfirm = (val: Date[]) => { setCurrentTimeValue(val); + const [start, end] = val; + setDateRange({ + start: dayjs(start).format("YYYY-MM-DD"), + end: dayjs(end).format("YYYY-MM-DD"), + }); }; return (