From 2b2f34e7b1a5733da2f9800c003cc3ae5b71dbef Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Thu, 20 Feb 2025 11:41:14 +0800 Subject: [PATCH 01/10] =?UTF-8?q?fix:=20datepicker=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/packages/datepicker/datepicker.tsx | 401 ++++++++++--------------- src/packages/datepicker/utils.ts | 247 +++++++++++++++ 2 files changed, 414 insertions(+), 234 deletions(-) create mode 100644 src/packages/datepicker/utils.ts diff --git a/src/packages/datepicker/datepicker.tsx b/src/packages/datepicker/datepicker.tsx index 47011cdc27..8fffbbf4f4 100644 --- a/src/packages/datepicker/datepicker.tsx +++ b/src/packages/datepicker/datepicker.tsx @@ -1,30 +1,36 @@ -import React, { FunctionComponent, useState, useEffect } from 'react' -import Picker from '@/packages/picker' -import { PickerOption, PickerProps } from '@/packages/picker/index' -import { useConfig } from '@/packages/configprovider' -import { usePropsValue } from '@/hooks/use-props-value' -import { BasicComponent, ComponentDefaults } from '@/utils/typings' -import { isDate } from '@/utils/is-date' -import { padZero } from '@/utils/pad-zero' +import React, { FunctionComponent, useState, useEffect } from 'react' // 导入 React 和相关钩子 +import Picker from '@/packages/picker' // 导入 Picker 组件 +import { PickerOption, PickerProps } from '@/packages/picker/index' // 导入 Picker 的类型定义 +import { useConfig } from '@/packages/configprovider' // 导入配置钩子 +import { usePropsValue } from '@/hooks/use-props-value' // 导入自定义钩子,用于管理属性值 +import { BasicComponent, ComponentDefaults } from '@/utils/typings' // 导入基础组件类型和默认值 +import { isDate } from '@/utils/is-date' // 导入日期验证工具函数 +import { + generateDatePickerRanges, + generatePickerColumnWithCallback, + getDatePartValue, + getLastDayOfMonth, +} from './utils' +import { PickerValue } from '../pickerview/types' export interface DatePickerProps extends BasicComponent { - value?: Date - defaultValue?: Date - visible: boolean - title: string - type: - | 'date' + value?: Date // 当前选中的日期 + defaultValue?: Date // 默认选中的日期 + visible: boolean // 是否显示 Picker + title: string // Picker 的标题 + type: // 日期选择器的类型 + | 'date' | 'time' | 'year-month' | 'month-day' | 'datehour' | 'datetime' | 'hour-minutes' - showChinese: boolean - minuteStep: number - startDate: Date - endDate: Date - threeDimensional: boolean + showChinese: boolean // 是否显示中文文本 + minuteStep: number // 分钟步长 + startDate: Date // 日期范围的开始日期 + endDate: Date // 日期范围的结束日期 + threeDimensional: boolean // 是否启用 3D 效果 pickerProps: Partial< Omit< PickerProps, @@ -38,38 +44,44 @@ export interface DatePickerProps extends BasicComponent { | 'onChange' > > - formatter: (type: string, option: PickerOption) => PickerOption - filter: (type: string, option: PickerOption[]) => PickerOption[] - onClose: () => void - onCancel: () => void + formatter: (type: string, option: PickerOption) => PickerOption // 格式化选项的函数 + filter: (type: string, option: PickerOption[]) => PickerOption[] // 过滤选项的函数 + onClose: () => void // Picker 关闭时的回调 + onCancel: () => void // Picker 取消时的回调 onConfirm: ( + // Picker 确认时的回调 selectedOptions: PickerOption[], selectedValue: (string | number)[] ) => void onChange?: ( + // Picker 值变化时的回调 selectedOptions: PickerOption[], selectedValue: (string | number)[], columnIndex: number ) => void } +// 获取当前年份 const currentYear = new Date().getFullYear() + +// 定义默认属性 const defaultProps = { - ...ComponentDefaults, - visible: false, - title: '', - type: 'date', - showChinese: false, - threeDimensional: true, - minuteStep: 1, - startDate: new Date(currentYear - 10, 0, 1), - endDate: new Date(currentYear + 10, 11, 31), + ...ComponentDefaults, // 继承基础组件的默认值 + visible: false, // 默认不显示 Picker + title: '', // 默认标题为空 + type: 'date', // 默认类型为日期选择器 + showChinese: false, // 默认不显示中文文本 + threeDimensional: true, // 默认启用 3D 效果 + minuteStep: 1, // 默认分钟步长为 1 + startDate: new Date(currentYear - 10, 0, 1), // 默认开始日期为当前年份的前 10 年 + endDate: new Date(currentYear + 10, 11, 31), // 默认结束日期为当前年份的后 10 年 } as DatePickerProps export const DatePicker: FunctionComponent< Partial & Omit, 'onChange' | 'defaultValue'> > = (props) => { + // 解构传入的属性,并合并默认属性 const { startDate, endDate, @@ -94,8 +106,12 @@ export const DatePicker: FunctionComponent< ...defaultProps, ...props, } + + // 获取语言配置 const { locale } = useConfig() const lang = locale.datepicker + + // 定义中文文本映射 const zhCNType: { [key: string]: string } = { day: lang.day, year: lang.year, @@ -104,296 +120,213 @@ export const DatePicker: FunctionComponent< minute: lang.min, seconds: lang.seconds, } + + // 定义 Picker 的值和选项的状态 const [pickerValue, setPickerValue] = useState<(string | number)[]>([]) const [pickerOptions, setPickerOptions] = useState([]) + + // 格式化日期值,确保其在 startDate 和 endDate 之间 const formatValue = (value: Date | null) => { if (!value || (value && !isDate(value))) { - value = startDate + value = startDate // 如果值无效,使用 startDate } return Math.min( Math.max(value.getTime(), startDate.getTime()), endDate.getTime() - ) + ) // 确保日期在范围内 } + + // 使用 usePropsValue 管理选中的日期值 const [selectedDate, setSelectedDate] = usePropsValue({ - value: props.value && formatValue(props.value), - defaultValue: props.defaultValue && formatValue(props.defaultValue), - finalValue: 0, + value: props.value && formatValue(props.value), // 当前值 + defaultValue: props.defaultValue && formatValue(props.defaultValue), // 默认值 + finalValue: 0, // 最终值 }) - function getMonthEndDay(year: number, month: number): number { - return new Date(year, month, 0).getDate() - } - - const getBoundary = (type: string, value: Date) => { - const boundary = type === 'min' ? startDate : endDate - const year = boundary.getFullYear() - let month = 1 - let date = 1 - let hour = 0 - let minute = 0 - - if (type === 'max') { - month = 12 - date = getMonthEndDay(value.getFullYear(), value.getMonth() + 1) - hour = 23 - minute = 59 - } - const seconds = minute - if (value.getFullYear() === year) { - month = boundary.getMonth() + 1 - if (value.getMonth() + 1 === month) { - date = boundary.getDate() - if (value.getDate() === date) { - hour = boundary.getHours() - if (value.getHours() === hour) { - minute = boundary.getMinutes() - } - } - } - } - return { - [`${type}Year`]: year, - [`${type}Month`]: month, - [`${type}Date`]: date, - [`${type}Hour`]: hour, - [`${type}Minute`]: minute, - [`${type}Seconds`]: seconds, - } - } - const ranges = () => { - const selected = new Date(selectedDate) - if (!selected) return [] - const { maxYear, maxDate, maxMonth, maxHour, maxMinute, maxSeconds } = - getBoundary('max', selected) - const { minYear, minDate, minMonth, minHour, minMinute, minSeconds } = - getBoundary('min', selected) - const result = [ - { - type: 'year', - range: [minYear, maxYear], - }, - { - type: 'month', - range: [minMonth, maxMonth], - }, - { - type: 'day', - range: [minDate, maxDate], - }, - { - type: 'hour', - range: [minHour, maxHour], - }, - { - type: 'minute', - range: [minMinute, maxMinute], - }, - { - type: 'seconds', - range: [minSeconds, maxSeconds], - }, - ] - - switch (type.toLocaleLowerCase()) { - case 'date': - return result.slice(0, 3) - case 'datetime': - return result.slice(0, 5) - case 'time': - return result.slice(3, 6) - case 'year-month': - return result.slice(0, 2) - case 'hour-minutes': - return result.slice(3, 5) - case 'month-day': - return result.slice(1, 3) - case 'datehour': - return result.slice(0, 4) - default: - return result - } - } - - const compareDateChange = ( + /** + * 比较新旧日期,如果不同则更新选中日期并触发 onChange 回调 + * @param currentDate 当前选中的日期时间戳 + * @param newDate 新选中的日期对象 + * @param selectedOptions 选中的选项数组 + * @param index 当前列的索引 + */ + const handleDateComparison = ( currentDate: number, newDate: Date | null, selectedOptions: PickerOption[], index: number ) => { + // 比较当前日期和新日期的时间戳是否相同 const isEqual = new Date(currentDate)?.getTime() === newDate?.getTime() + + // 如果新日期有效且与当前日期不同 if (newDate && isDate(newDate)) { if (!isEqual) { - setSelectedDate(formatValue(newDate as Date)) + // 更新选中的日期 + setSelectedDate(formatValue(newDate)) } + + // 触发 onChange 回调,传递选中的选项和日期信息 onChange?.( selectedOptions, [ - String(newDate.getFullYear()), - String(newDate.getMonth() + 1), - String(newDate.getDate()), + String(newDate.getFullYear()), // 年份 + String(newDate.getMonth() + 1), // 月份(注意:getMonth() 返回 0-11,需要加 1) + String(newDate.getDate()), // 日期 ], - index + index // 当前列的索引 ) } } - const handlePickerChange = ( + + /** + * 处理 Picker 值变化的逻辑 + * @param selectedOptions 选中的选项数组 + * @param selectedValue 选中的值数组 + * @param index 当前列的索引 + */ + const handlePickerValueChange = ( selectedOptions: PickerOption[], selectedValue: (number | string)[], index: number ) => { - const rangeType = type.toLocaleLowerCase() + const rangeType = type.toLocaleLowerCase() // 获取日期选择器的类型并转换为小写 + + // 处理日期相关的类型(如 'date', 'datetime', 'datehour' 等) if ( ['date', 'datetime', 'datehour', 'month-day', 'year-month'].includes( rangeType ) ) { - const formatDate: (number | string)[] = [] + const formattedDate: PickerValue[] = [] + + // 将选中的值转换为数组 selectedValue.forEach((item) => { - formatDate.push(item) + formattedDate.push(item) }) - if (rangeType === 'month-day' && formatDate.length < 3) { - formatDate.unshift( + + // 如果类型是 'month-day' 且缺少年份,补充当前年份 + if (rangeType === 'month-day' && formattedDate.length < 3) { + formattedDate.unshift( new Date(defaultValue || startDate || endDate).getFullYear() ) } - if (rangeType === 'year-month' && formatDate.length < 3) { - formatDate.push( + // 如果类型是 'year-month' 且缺少日期,补充当前日期 + if (rangeType === 'year-month' && formattedDate.length < 3) { + formattedDate.push( new Date(defaultValue || startDate || endDate).getDate() ) } - const year = Number(formatDate[0]) - const month = Number(formatDate[1]) - 1 + // 解析年、月、日 + const year = Number(formattedDate[0]) + const month = Number(formattedDate[1]) - 1 // 月份从 0 开始 const day = Math.min( - Number(formatDate[2]), - getMonthEndDay(Number(formatDate[0]), Number(formatDate[1])) + Number(formattedDate[2]), + getLastDayOfMonth(year, month + 1) // 获取当前月份的最后一天 ) + let date: Date | null = null + + // 根据类型创建日期对象 if ( rangeType === 'date' || rangeType === 'month-day' || rangeType === 'year-month' ) { - date = new Date(year, month, day) + date = new Date(year, month, day) // 仅包含年、月、日 } else if (rangeType === 'datetime') { date = new Date( year, month, day, - Number(formatDate[3]), - Number(formatDate[4]) - ) + Number(formattedDate[3]), + Number(formattedDate[4]) + ) // 包含年、月、日、时、分 } else if (rangeType === 'datehour') { - date = new Date(year, month, day, Number(formatDate[3])) + date = new Date(year, month, day, Number(formattedDate[3])) // 包含年、月、日、时 } - compareDateChange(selectedDate, date, selectedOptions, index) + // 比较并处理日期变化 + handleDateComparison(selectedDate, date, selectedOptions, index) } else { - // 'hour-minutes' 'time' + // 处理时间相关的类型(如 'hour-minutes', 'time') const [hour, minute, seconds] = selectedValue + + // 获取当前日期的年、月、日 const currentDate = new Date(selectedDate) const year = currentDate.getFullYear() const month = currentDate.getMonth() const day = currentDate.getDate() + // 创建日期对象 const date = new Date( year, month, day, Number(hour), Number(minute), - rangeType === 'time' ? Number(seconds) : 0 + rangeType === 'time' ? Number(seconds) : 0 // 如果是 'time' 类型,包含秒数 ) - compareDateChange(selectedDate, date, selectedOptions, index) - } - } - const formatOption = (type: string, value: string | number) => { - if (formatter) { - return formatter(type, { - text: padZero(value, 2), - value: padZero(value, 2), - }) + // 比较并处理日期变化 + handleDateComparison(selectedDate, date, selectedOptions, index) } - const padMin = padZero(value, 2) - const fatter = showChinese ? zhCNType[type] : '' - return { text: padMin + fatter, value: padMin } } - const generateColumn = ( - min: number, - max: number, - val: number | string, - type: string, - columnIndex: number - ) => { - let cmin = min - const arr: Array = [] - let index = 0 - while (cmin <= max) { - arr.push(formatOption(type, cmin)) - - if (type === 'minute') { - cmin += minuteStep - } else { - cmin++ - } - - if (cmin <= Number(val)) { - index++ - } - } + /** + * 生成 Picker 的列数据 + * @returns 返回生成的 Picker 列数据数组 + */ + const generatePickerColumns = (): PickerOption[][] => { + // 生成日期选择器的范围配置 + const dateRanges = generateDatePickerRanges( + type, + selectedDate, + startDate, + endDate + ) - pickerValue[columnIndex] = arr[index]?.value - setPickerValue([...pickerValue]) + // 遍历范围配置,生成每一列的选项 + const columns = dateRanges.map((rangeConfig, columnIndex) => { + // 获取当前列的类型和选中值 + const { type: columnType, range } = rangeConfig + const selectedValue = getDatePartValue(columnType, selectedDate) - if (filter?.(type, arr)) { - return filter?.(type, arr) - } - return arr - } + // 生成当前列的选项,并设置选中值 + const pickerColumn = generatePickerColumnWithCallback( + range[0], // 最小值 + range[1], // 最大值 + selectedValue, // 当前选中的值 + columnType, // 列的类型 + minuteStep, // 分钟步长 + (selectedIndex, options) => { + // 更新 pickerValue 中对应列的值 + pickerValue[columnIndex] = options[selectedIndex]?.value + setPickerValue([...pickerValue]) + }, + showChinese, // 是否显示中文 + zhCNType, // 中文文本映射 + formatter // 自定义格式化函数 + ) - const getDateIndex = (type: string) => { - const date = new Date(selectedDate) - if (!selectedDate) return 0 - if (type === 'year') { - return date.getFullYear() - } - if (type === 'month') { - return date.getMonth() + 1 - } - if (type === 'day') { - return date.getDate() - } - if (type === 'hour') { - return date.getHours() - } - if (type === 'minute') { - return date.getMinutes() - } - if (type === 'seconds') { - return date.getSeconds() - } - return 0 - } + // 如果提供了 filter 函数,则对选项进行过滤 + if (filter?.(columnType, pickerColumn)) { + return filter(columnType, pickerColumn) + } - const columns = () => { - const val = ranges().map((res, columnIndex) => { - return generateColumn( - res.range[0], - res.range[1], - getDateIndex(res.type), - res.type, - columnIndex - ) + // 返回当前列的选项 + return pickerColumn }) - return val || [] + + // 返回生成的列数据,如果为空则返回空数组 + return columns || [] } + // 当 selectedDate、startDate 或 endDate 变化时,重新生成 Picker 列数据 useEffect(() => { - setPickerOptions(columns()) + setPickerOptions(generatePickerColumns()) }, [selectedDate, startDate, endDate]) return ( @@ -414,7 +347,7 @@ export const DatePicker: FunctionComponent< options: PickerOption[], value: (number | string)[], index: number - ) => handlePickerChange(options, value, index)} + ) => handlePickerValueChange(options, value, index)} threeDimensional={threeDimensional} /> )} diff --git a/src/packages/datepicker/utils.ts b/src/packages/datepicker/utils.ts new file mode 100644 index 0000000000..27d0887647 --- /dev/null +++ b/src/packages/datepicker/utils.ts @@ -0,0 +1,247 @@ +import { padZero } from '@/utils/pad-zero' +import { PickerOption } from '../picker/types' + +/** + * 获取指定年份和月份的最后一天 + * @param year - 年份 + * @param month - 月份(1 到 12) + * @returns 返回该月份的最后一天 + */ +export function getLastDayOfMonth(year: number, month: number): number { + return new Date(year, month, 0).getDate() +} + +/** + * 根据类型和日期值,计算并返回日期边界值(年、月、日、时、分、秒) + * @param type 边界类型:'min' 表示最小值,'max' 表示最大值 + * @param value 传入的日期值 + * @param startDate 传入的开始时间 + * @param endDate 传入的结束时间 + * @returns 返回包含边界值的对象 + */ +export const calculateDateBoundary = ( + type: 'min' | 'max', + value: Date, + startDate: Date, + endDate: Date +) => { + // 根据类型选择边界日期:'min' 使用 startDate,'max' 使用 endDate + const boundary = type === 'min' ? startDate : endDate + + // 获取边界日期的年份 + const year = boundary.getFullYear() + + // 初始化月份、日期、小时和分钟 + const isMax = type === 'max' // 是否为 'max' 类型 + let month = isMax ? 12 : 1 // 'max' 时月份为 12,否则为 1 + let date = isMax + ? getLastDayOfMonth(value.getFullYear(), value.getMonth() + 1) + : 1 // 'max' 时日期为当前月份的最后一天,否则为 1 + let hour = isMax ? 23 : 0 // 'max' 时小时为 23,否则为 0 + let minute = isMax ? 59 : 0 // 'max' 时分钟为 59,否则为 0 + + // 如果传入日期的年份与边界日期的年份相同 + if (value.getFullYear() === year) { + month = boundary.getMonth() + 1 // 使用边界日期的月份 + + // 如果传入日期的月份与边界日期的月份相同 + if (value.getMonth() + 1 === month) { + date = boundary.getDate() // 使用边界日期的日期 + + // 如果传入日期的日期与边界日期的日期相同 + if (value.getDate() === date) { + hour = boundary.getHours() // 使用边界日期的小时 + + // 如果传入日期的小时与边界日期的小时相同 + if (value.getHours() === hour) { + minute = boundary.getMinutes() // 使用边界日期的分钟 + } + } + } + } + + // 返回边界值的对象 + return { + [`${type}Year`]: year, // 返回年份 + [`${type}Month`]: month, // 返回月份 + [`${type}Date`]: date, // 返回日期 + [`${type}Hour`]: hour, // 返回小时 + [`${type}Minute`]: minute, // 返回分钟 + [`${type}Seconds`]: minute, // 返回秒数(与分钟相同) + } +} + +/** + * 根据选中的日期和类型,生成日期选择器的范围配置 + * @returns {Array} 返回日期选择器的范围配置数组 + */ +export const generateDatePickerRanges = ( + type: string, + selectedDate: number, + startDate: Date, + endDate: Date +) => { + // 将选中的日期转换为 Date 对象 + const selected = new Date(selectedDate) + if (!selected) return [] // 如果选中的日期无效,返回空数组 + + // 获取最大和最小边界值 + const { maxYear, maxDate, maxMonth, maxHour, maxMinute, maxSeconds } = + calculateDateBoundary('max', selected, startDate, endDate) + const { minYear, minDate, minMonth, minHour, minMinute, minSeconds } = + calculateDateBoundary('min', selected, startDate, endDate) + + // 定义完整的日期范围配置 + const fullRanges = [ + { type: 'year', range: [minYear, maxYear] }, // 年份范围 + { type: 'month', range: [minMonth, maxMonth] }, // 月份范围 + { type: 'day', range: [minDate, maxDate] }, // 日期范围 + { type: 'hour', range: [minHour, maxHour] }, // 小时范围 + { type: 'minute', range: [minMinute, maxMinute] }, // 分钟范围 + { type: 'seconds', range: [minSeconds, maxSeconds] }, // 秒数范围 + ] + + // 根据类型返回对应的范围配置 + switch (type.toLocaleLowerCase()) { + case 'date': + return fullRanges.slice(0, 3) // 返回年、月、日 + case 'datetime': + return fullRanges.slice(0, 5) // 返回年、月、日、时、分 + case 'time': + return fullRanges.slice(3, 6) // 返回时、分、秒 + case 'year-month': + return fullRanges.slice(0, 2) // 返回年、月 + case 'hour-minutes': + return fullRanges.slice(3, 5) // 返回时、分 + case 'month-day': + return fullRanges.slice(1, 3) // 返回月、日 + case 'datehour': + return fullRanges.slice(0, 4) // 返回年、月、日、时 + default: + return fullRanges // 返回完整范围 + } +} + +/** + * 根据类型获取日期对象中对应的值 + * @param type 需要获取的日期部分(如 'year', 'month', 'day' 等) + * @param selectedDate 选中的日期时间戳 + * @returns 返回日期对象中对应部分的值,如果类型无效或日期无效,返回 0 + */ +export const getDatePartValue = ( + type: string, + selectedDate: number +): number => { + // 将时间戳转换为 Date 对象 + const date = new Date(selectedDate) + + // 如果选中的日期无效,返回 0 + if (!selectedDate) return 0 + + // 根据类型返回对应的日期部分值 + switch (type) { + case 'year': + return date.getFullYear() // 返回年份 + case 'month': + return date.getMonth() + 1 // 返回月份(注意:getMonth() 返回 0-11,需要加 1) + case 'day': + return date.getDate() // 返回日期 + case 'hour': + return date.getHours() // 返回小时 + case 'minute': + return date.getMinutes() // 返回分钟 + case 'seconds': + return date.getSeconds() // 返回秒数 + default: + return 0 // 如果类型无效,返回 0 + } +} + +/** + * 生成 Picker 的列数据,并触发回调函数返回选中索引 + * @param min 最小值 + * @param max 最大值 + * @param currentValue 当前选中的值 + * @param type 列的类型(如 'year', 'month', 'minute' 等) + * @param minuteStep 分钟步长(仅当类型为 'minute' 时生效) + * @param callback 回调函数,用于返回选中索引 + * @returns 返回生成的 Picker 列数据 + */ +export const generatePickerColumnWithCallback = ( + min: number, + max: number, + currentValue: number | string, + type: string, + minuteStep: number, + callback: (selectedIndex: number, options: PickerOption[]) => void, + showChinese: boolean, + zhCNType: { [key: string]: string }, + formatter?: (type: string, option: PickerOption) => PickerOption +): PickerOption[] => { + let currentMin = min // 当前最小值 + const options: PickerOption[] = [] // 存储生成的选项 + let selectedIndex = 0 // 当前选中值的索引 + + // 遍历从最小值到最大值的范围 + while (currentMin <= max) { + // 将当前值格式化为 PickerOption 并添加到数组中 + options.push( + formatPickerOption(type, currentMin, showChinese, zhCNType, formatter) + ) + + // 根据类型决定步长:如果是分钟,使用 minuteStep,否则步长为 1 + if (type === 'minute') { + currentMin += minuteStep + } else { + currentMin++ + } + + // 如果当前值小于等于选中的值,更新选中索引 + if (currentMin <= Number(currentValue)) { + selectedIndex++ + } + } + + // 触发回调函数,返回选中索引 + callback(selectedIndex, options) + + // 返回生成的选项数组 + return options +} + +/** + * 格式化 Picker 选项 + * @param type 选项类型(如 'year', 'month', 'minute' 等) + * @param value 选项的值 + * @param showChinese 是否显示中文文本 + * @param zhCNType 中文文本映射对象 + * @param formatter 自定义格式化函数 + * @returns 返回格式化后的 Picker 选项 + */ +export const formatPickerOption = ( + type: string, + value: string | number, + showChinese: boolean, + zhCNType: { [key: string]: string }, + formatter?: (type: string, option: PickerOption) => PickerOption +): PickerOption => { + // 如果提供了自定义格式化函数,则使用该函数格式化选项 + if (formatter) { + return formatter(type, { + text: padZero(value, 2), // 补零后的文本 + value: padZero(value, 2), // 补零后的值 + }) + } + + // 补零后的值 + const paddedValue = padZero(value, 2) + + // 如果需要显示中文,添加对应的中文文本 + const chineseText = showChinese ? zhCNType[type] : '' + + // 返回格式化后的选项 + return { + text: paddedValue + chineseText, // 文本 = 补零后的值 + 中文文本 + value: paddedValue, // 值 = 补零后的值 + } +} From 3ccf9bc19d108180980b2df6aefc7477d07ba5f7 Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Thu, 20 Feb 2025 13:52:45 +0800 Subject: [PATCH 02/10] =?UTF-8?q?fix:=20datepicker=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/packages/datepicker/datepicker.tsx | 126 ++++--------------------- src/packages/datepicker/utils.ts | 118 +++++++++++++++++++++++ 2 files changed, 135 insertions(+), 109 deletions(-) diff --git a/src/packages/datepicker/datepicker.tsx b/src/packages/datepicker/datepicker.tsx index 8fffbbf4f4..f626a2482a 100644 --- a/src/packages/datepicker/datepicker.tsx +++ b/src/packages/datepicker/datepicker.tsx @@ -6,12 +6,12 @@ import { usePropsValue } from '@/hooks/use-props-value' // 导入自定义钩子 import { BasicComponent, ComponentDefaults } from '@/utils/typings' // 导入基础组件类型和默认值 import { isDate } from '@/utils/is-date' // 导入日期验证工具函数 import { + formatValue, generateDatePickerRanges, generatePickerColumnWithCallback, getDatePartValue, - getLastDayOfMonth, + handlePickerValueChange, } from './utils' -import { PickerValue } from '../pickerview/types' export interface DatePickerProps extends BasicComponent { value?: Date // 当前选中的日期 @@ -125,21 +125,11 @@ export const DatePicker: FunctionComponent< const [pickerValue, setPickerValue] = useState<(string | number)[]>([]) const [pickerOptions, setPickerOptions] = useState([]) - // 格式化日期值,确保其在 startDate 和 endDate 之间 - const formatValue = (value: Date | null) => { - if (!value || (value && !isDate(value))) { - value = startDate // 如果值无效,使用 startDate - } - return Math.min( - Math.max(value.getTime(), startDate.getTime()), - endDate.getTime() - ) // 确保日期在范围内 - } - // 使用 usePropsValue 管理选中的日期值 const [selectedDate, setSelectedDate] = usePropsValue({ - value: props.value && formatValue(props.value), // 当前值 - defaultValue: props.defaultValue && formatValue(props.defaultValue), // 默认值 + value: props.value && formatValue(props.value, startDate, endDate), // 当前值 + defaultValue: + props.defaultValue && formatValue(props.defaultValue, startDate, endDate), // 默认值 finalValue: 0, // 最终值 }) @@ -151,19 +141,18 @@ export const DatePicker: FunctionComponent< * @param index 当前列的索引 */ const handleDateComparison = ( - currentDate: number, newDate: Date | null, selectedOptions: PickerOption[], index: number ) => { // 比较当前日期和新日期的时间戳是否相同 - const isEqual = new Date(currentDate)?.getTime() === newDate?.getTime() + const isEqual = new Date(selectedDate)?.getTime() === newDate?.getTime() // 如果新日期有效且与当前日期不同 if (newDate && isDate(newDate)) { if (!isEqual) { // 更新选中的日期 - setSelectedDate(formatValue(newDate)) + setSelectedDate(formatValue(newDate, startDate, endDate)) } // 触发 onChange 回调,传递选中的选项和日期信息 @@ -179,100 +168,19 @@ export const DatePicker: FunctionComponent< } } - /** - * 处理 Picker 值变化的逻辑 - * @param selectedOptions 选中的选项数组 - * @param selectedValue 选中的值数组 - * @param index 当前列的索引 - */ - const handlePickerValueChange = ( + const handleChange = ( selectedOptions: PickerOption[], selectedValue: (number | string)[], index: number ) => { - const rangeType = type.toLocaleLowerCase() // 获取日期选择器的类型并转换为小写 - - // 处理日期相关的类型(如 'date', 'datetime', 'datehour' 等) - if ( - ['date', 'datetime', 'datehour', 'month-day', 'year-month'].includes( - rangeType - ) - ) { - const formattedDate: PickerValue[] = [] - - // 将选中的值转换为数组 - selectedValue.forEach((item) => { - formattedDate.push(item) - }) - - // 如果类型是 'month-day' 且缺少年份,补充当前年份 - if (rangeType === 'month-day' && formattedDate.length < 3) { - formattedDate.unshift( - new Date(defaultValue || startDate || endDate).getFullYear() - ) - } - - // 如果类型是 'year-month' 且缺少日期,补充当前日期 - if (rangeType === 'year-month' && formattedDate.length < 3) { - formattedDate.push( - new Date(defaultValue || startDate || endDate).getDate() - ) - } - - // 解析年、月、日 - const year = Number(formattedDate[0]) - const month = Number(formattedDate[1]) - 1 // 月份从 0 开始 - const day = Math.min( - Number(formattedDate[2]), - getLastDayOfMonth(year, month + 1) // 获取当前月份的最后一天 - ) - - let date: Date | null = null - - // 根据类型创建日期对象 - if ( - rangeType === 'date' || - rangeType === 'month-day' || - rangeType === 'year-month' - ) { - date = new Date(year, month, day) // 仅包含年、月、日 - } else if (rangeType === 'datetime') { - date = new Date( - year, - month, - day, - Number(formattedDate[3]), - Number(formattedDate[4]) - ) // 包含年、月、日、时、分 - } else if (rangeType === 'datehour') { - date = new Date(year, month, day, Number(formattedDate[3])) // 包含年、月、日、时 - } - - // 比较并处理日期变化 - handleDateComparison(selectedDate, date, selectedOptions, index) - } else { - // 处理时间相关的类型(如 'hour-minutes', 'time') - const [hour, minute, seconds] = selectedValue - - // 获取当前日期的年、月、日 - const currentDate = new Date(selectedDate) - const year = currentDate.getFullYear() - const month = currentDate.getMonth() - const day = currentDate.getDate() - - // 创建日期对象 - const date = new Date( - year, - month, - day, - Number(hour), - Number(minute), - rangeType === 'time' ? Number(seconds) : 0 // 如果是 'time' 类型,包含秒数 - ) - - // 比较并处理日期变化 - handleDateComparison(selectedDate, date, selectedOptions, index) - } + handlePickerValueChange( + selectedOptions, + selectedValue, + index, + type, + defaultValue || startDate || endDate, + handleDateComparison + ) } /** @@ -347,7 +255,7 @@ export const DatePicker: FunctionComponent< options: PickerOption[], value: (number | string)[], index: number - ) => handlePickerValueChange(options, value, index)} + ) => handleChange(options, value, index)} threeDimensional={threeDimensional} /> )} diff --git a/src/packages/datepicker/utils.ts b/src/packages/datepicker/utils.ts index 27d0887647..2caf265623 100644 --- a/src/packages/datepicker/utils.ts +++ b/src/packages/datepicker/utils.ts @@ -1,5 +1,7 @@ import { padZero } from '@/utils/pad-zero' +import { isDate } from '@/utils/is-date' import { PickerOption } from '../picker/types' +import { PickerValue } from '../pickerview/types' /** * 获取指定年份和月份的最后一天 @@ -245,3 +247,119 @@ export const formatPickerOption = ( value: paddedValue, // 值 = 补零后的值 } } + +/** + * 格式化日期值,确保其在 startDate 和 endDate 之间 + */ +export const formatValue = ( + value: Date | null, + startDate: Date, + endDate: Date +) => { + if (!value || (value && !isDate(value))) { + value = startDate // 如果值无效,使用 startDate + } + return Math.min( + Math.max(value.getTime(), startDate.getTime()), + endDate.getTime() + ) // 确保日期在范围内 +} + +/** + * 处理 Picker 值变化的逻辑 + * @param selectedOptions 选中的选项数组 + * @param selectedValue 选中的值数组 + * @param index 当前列的索引 + */ +export const handlePickerValueChange = ( + selectedOptions: PickerOption[], + selectedValue: PickerValue[], + index: number, + type: string, + defaultDate: Date, + handleDateComparison: ( + newDate: Date | null, + selectedOptions: PickerOption[], + index: number + ) => void +) => { + const rangeType = type.toLocaleLowerCase() // 获取日期选择器的类型并转换为小写 + + // 处理日期相关的类型(如 'date', 'datetime', 'datehour' 等) + if ( + ['date', 'datetime', 'datehour', 'month-day', 'year-month'].includes( + rangeType + ) + ) { + const formattedDate: PickerValue[] = [] + + // 将选中的值转换为数组 + selectedValue.forEach((item) => { + formattedDate.push(item) + }) + + // 如果类型是 'month-day' 且缺少年份,补充当前年份 + if (rangeType === 'month-day' && formattedDate.length < 3) { + formattedDate.unshift(new Date(defaultDate).getFullYear()) + } + + // 如果类型是 'year-month' 且缺少日期,补充当前日期 + if (rangeType === 'year-month' && formattedDate.length < 3) { + formattedDate.push(new Date(defaultDate).getDate()) + } + + // 解析年、月、日 + const year = Number(formattedDate[0]) + const month = Number(formattedDate[1]) - 1 // 月份从 0 开始 + const day = Math.min( + Number(formattedDate[2]), + getLastDayOfMonth(year, month + 1) // 获取当前月份的最后一天 + ) + + let date: Date | null = null + + // 根据类型创建日期对象 + if ( + rangeType === 'date' || + rangeType === 'month-day' || + rangeType === 'year-month' + ) { + date = new Date(year, month, day) // 仅包含年、月、日 + } else if (rangeType === 'datetime') { + date = new Date( + year, + month, + day, + Number(formattedDate[3]), + Number(formattedDate[4]) + ) // 包含年、月、日、时、分 + } else if (rangeType === 'datehour') { + date = new Date(year, month, day, Number(formattedDate[3])) // 包含年、月、日、时 + } + + // 比较并处理日期变化 + handleDateComparison(date, selectedOptions, index) + } else { + // 处理时间相关的类型(如 'hour-minutes', 'time') + const [hour, minute, seconds] = selectedValue + + // 获取当前日期的年、月、日 + const currentDate = new Date(defaultDate) + const year = currentDate.getFullYear() + const month = currentDate.getMonth() + const day = currentDate.getDate() + + // 创建日期对象 + const date = new Date( + year, + month, + day, + Number(hour), + Number(minute), + rangeType === 'time' ? Number(seconds) : 0 // 如果是 'time' 类型,包含秒数 + ) + + // 比较并处理日期变化 + handleDateComparison(date, selectedOptions, index) + } +} From 9d2681698317dad624a9b49fa73cccb076e598f9 Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Thu, 20 Feb 2025 17:00:27 +0800 Subject: [PATCH 03/10] =?UTF-8?q?fix:=20=E5=8F=96=E6=B6=88=E5=90=8E?= =?UTF-8?q?=E8=BF=98=E5=8E=9F=E5=88=9D=E5=A7=8B=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/packages/datepicker/datepicker.tsx | 57 +++++++++++++++++----- src/packages/datepicker/demos/h5/demo8.tsx | 10 ++-- 2 files changed, 53 insertions(+), 14 deletions(-) diff --git a/src/packages/datepicker/datepicker.tsx b/src/packages/datepicker/datepicker.tsx index f626a2482a..c7940ecb6f 100644 --- a/src/packages/datepicker/datepicker.tsx +++ b/src/packages/datepicker/datepicker.tsx @@ -12,6 +12,7 @@ import { getDatePartValue, handlePickerValueChange, } from './utils' +import { PickerValue } from '../nutui.react.build.taro' export interface DatePickerProps extends BasicComponent { value?: Date // 当前选中的日期 @@ -51,12 +52,12 @@ export interface DatePickerProps extends BasicComponent { onConfirm: ( // Picker 确认时的回调 selectedOptions: PickerOption[], - selectedValue: (string | number)[] + selectedValue: PickerValue[] ) => void onChange?: ( // Picker 值变化时的回调 selectedOptions: PickerOption[], - selectedValue: (string | number)[], + selectedValue: PickerValue[], columnIndex: number ) => void } @@ -133,6 +134,11 @@ export const DatePicker: FunctionComponent< finalValue: 0, // 最终值 }) + const [innerDate, setInnerDate] = useState(selectedDate) + useEffect(() => { + setInnerDate(selectedDate) + }, [selectedDate]) + /** * 比较新旧日期,如果不同则更新选中日期并触发 onChange 回调 * @param currentDate 当前选中的日期时间戳 @@ -146,13 +152,13 @@ export const DatePicker: FunctionComponent< index: number ) => { // 比较当前日期和新日期的时间戳是否相同 - const isEqual = new Date(selectedDate)?.getTime() === newDate?.getTime() + const isEqual = new Date(innerDate)?.getTime() === newDate?.getTime() // 如果新日期有效且与当前日期不同 if (newDate && isDate(newDate)) { if (!isEqual) { // 更新选中的日期 - setSelectedDate(formatValue(newDate, startDate, endDate)) + setInnerDate(formatValue(newDate, startDate, endDate)) } // 触发 onChange 回调,传递选中的选项和日期信息 @@ -168,6 +174,20 @@ export const DatePicker: FunctionComponent< } } + const handleDateComparison1 = ( + newDate: Date | null, + selectedOptions: PickerOption[], + index: number + ) => { + if (newDate && isDate(newDate)) { + const isEqual = new Date(selectedDate)?.getTime() === newDate?.getTime() + if (!isEqual) { + console.log('确认时修改值', formatValue(newDate, startDate, endDate)) + setSelectedDate(formatValue(newDate, startDate, endDate)) + } + } + } + const handleChange = ( selectedOptions: PickerOption[], selectedValue: (number | string)[], @@ -191,7 +211,7 @@ export const DatePicker: FunctionComponent< // 生成日期选择器的范围配置 const dateRanges = generateDatePickerRanges( type, - selectedDate, + innerDate, startDate, endDate ) @@ -200,7 +220,7 @@ export const DatePicker: FunctionComponent< const columns = dateRanges.map((rangeConfig, columnIndex) => { // 获取当前列的类型和选中值 const { type: columnType, range } = rangeConfig - const selectedValue = getDatePartValue(columnType, selectedDate) + const selectedValue = getDatePartValue(columnType, innerDate) // 生成当前列的选项,并设置选中值 const pickerColumn = generatePickerColumnWithCallback( @@ -235,7 +255,24 @@ export const DatePicker: FunctionComponent< // 当 selectedDate、startDate 或 endDate 变化时,重新生成 Picker 列数据 useEffect(() => { setPickerOptions(generatePickerColumns()) - }, [selectedDate, startDate, endDate]) + }, [innerDate, startDate, endDate]) + + const handleCancel = () => { + setInnerDate(selectedDate) + onCancel?.() + } + + const handleConfirm = (options: PickerOption[], value: PickerValue[]) => { + handlePickerValueChange( + options, + value, + 0, + type, + defaultValue || startDate || endDate, + handleDateComparison1 + ) + onConfirm?.(options, value) + } return (
@@ -246,11 +283,9 @@ export const DatePicker: FunctionComponent< visible={visible} options={pickerOptions} onClose={onClose} - onCancel={onCancel} + onCancel={handleCancel} value={pickerValue} - onConfirm={(options: PickerOption[], value: (string | number)[]) => - onConfirm && onConfirm(options, value) - } + onConfirm={handleConfirm} onChange={( options: PickerOption[], value: (number | string)[], diff --git a/src/packages/datepicker/demos/h5/demo8.tsx b/src/packages/datepicker/demos/h5/demo8.tsx index a5cce7f3eb..84f2b853e3 100644 --- a/src/packages/datepicker/demos/h5/demo8.tsx +++ b/src/packages/datepicker/demos/h5/demo8.tsx @@ -7,9 +7,13 @@ const Demo8 = () => { const defaultValue = new Date() const defaultDescription = `${defaultValue.getFullYear()}-${ defaultValue.getMonth() + 1 - }-${defaultValue.getDate()}` + }-${defaultValue.getDate()} 06:00` const [show, setShow] = useState(false) - const [desc, setDesc] = useState(`${defaultDescription} 00`) + const [desc, setDesc] = useState( + `${defaultValue.getFullYear()}年${ + defaultValue.getMonth() + 1 + }月${defaultValue.getDate()}日 06时` + ) const confirm = (values: (string | number)[], options: PickerOption[]) => { setDesc(options.map((option) => option.text).join(' ')) @@ -42,7 +46,7 @@ const Demo8 = () => { return ( <> setShow(true)} /> From 3e208b14512bd7be653029dcfc7ddfeee792fc13 Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Fri, 21 Feb 2025 15:18:11 +0800 Subject: [PATCH 04/10] =?UTF-8?q?fix:=20=E5=8F=97=E6=8E=A7=E7=9A=84?= =?UTF-8?q?=E4=BD=BF=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/packages/datepicker/datepicker.tsx | 247 ++++++++------------- src/packages/datepicker/demos/h5/demo1.tsx | 74 +++--- src/packages/datepicker/demos/h5/demo2.tsx | 6 +- src/packages/datepicker/index.ts | 4 +- src/packages/datepicker/types.ts | 55 +++++ src/packages/datepicker/utils.ts | 5 +- 6 files changed, 207 insertions(+), 184 deletions(-) create mode 100644 src/packages/datepicker/types.ts diff --git a/src/packages/datepicker/datepicker.tsx b/src/packages/datepicker/datepicker.tsx index c7940ecb6f..20519663f1 100644 --- a/src/packages/datepicker/datepicker.tsx +++ b/src/packages/datepicker/datepicker.tsx @@ -1,10 +1,14 @@ -import React, { FunctionComponent, useState, useEffect } from 'react' // 导入 React 和相关钩子 -import Picker from '@/packages/picker' // 导入 Picker 组件 -import { PickerOption, PickerProps } from '@/packages/picker/index' // 导入 Picker 的类型定义 -import { useConfig } from '@/packages/configprovider' // 导入配置钩子 -import { usePropsValue } from '@/hooks/use-props-value' // 导入自定义钩子,用于管理属性值 -import { BasicComponent, ComponentDefaults } from '@/utils/typings' // 导入基础组件类型和默认值 -import { isDate } from '@/utils/is-date' // 导入日期验证工具函数 +import React, { + useState, + useEffect, + ForwardRefRenderFunction, + useImperativeHandle, +} from 'react' +import Picker, { PickerOption } from '@/packages/picker' +import { useConfig } from '@/packages/configprovider' +import { usePropsValue } from '@/hooks/use-props-value' +import { ComponentDefaults } from '@/utils/typings' +import { isDate } from '@/utils/is-date' import { formatValue, generateDatePickerRanges, @@ -12,77 +16,26 @@ import { getDatePartValue, handlePickerValueChange, } from './utils' -import { PickerValue } from '../nutui.react.build.taro' +import { DatePickerActions, DatePickerProps, DatePickerRef } from './types' -export interface DatePickerProps extends BasicComponent { - value?: Date // 当前选中的日期 - defaultValue?: Date // 默认选中的日期 - visible: boolean // 是否显示 Picker - title: string // Picker 的标题 - type: // 日期选择器的类型 - | 'date' - | 'time' - | 'year-month' - | 'month-day' - | 'datehour' - | 'datetime' - | 'hour-minutes' - showChinese: boolean // 是否显示中文文本 - minuteStep: number // 分钟步长 - startDate: Date // 日期范围的开始日期 - endDate: Date // 日期范围的结束日期 - threeDimensional: boolean // 是否启用 3D 效果 - pickerProps: Partial< - Omit< - PickerProps, - | 'defaultValue' - | 'threeDimensional' - | 'title' - | 'value' - | 'onConfirm' - | 'onClose' - | 'onCancel' - | 'onChange' - > - > - formatter: (type: string, option: PickerOption) => PickerOption // 格式化选项的函数 - filter: (type: string, option: PickerOption[]) => PickerOption[] // 过滤选项的函数 - onClose: () => void // Picker 关闭时的回调 - onCancel: () => void // Picker 取消时的回调 - onConfirm: ( - // Picker 确认时的回调 - selectedOptions: PickerOption[], - selectedValue: PickerValue[] - ) => void - onChange?: ( - // Picker 值变化时的回调 - selectedOptions: PickerOption[], - selectedValue: PickerValue[], - columnIndex: number - ) => void -} - -// 获取当前年份 const currentYear = new Date().getFullYear() -// 定义默认属性 const defaultProps = { - ...ComponentDefaults, // 继承基础组件的默认值 - visible: false, // 默认不显示 Picker - title: '', // 默认标题为空 - type: 'date', // 默认类型为日期选择器 - showChinese: false, // 默认不显示中文文本 - threeDimensional: true, // 默认启用 3D 效果 - minuteStep: 1, // 默认分钟步长为 1 - startDate: new Date(currentYear - 10, 0, 1), // 默认开始日期为当前年份的前 10 年 - endDate: new Date(currentYear + 10, 11, 31), // 默认结束日期为当前年份的后 10 年 + ...ComponentDefaults, + visible: false, + title: '', + type: 'date', + showChinese: false, + threeDimensional: true, + minuteStep: 1, + startDate: new Date(currentYear - 10, 0, 1), + endDate: new Date(currentYear + 10, 11, 31), } as DatePickerProps -export const DatePicker: FunctionComponent< - Partial & - Omit, 'onChange' | 'defaultValue'> -> = (props) => { - // 解构传入的属性,并合并默认属性 +const InternalPicker: ForwardRefRenderFunction< + DatePickerRef, + Partial +> = (props, ref) => { const { startDate, endDate, @@ -108,11 +61,9 @@ export const DatePicker: FunctionComponent< ...props, } - // 获取语言配置 const { locale } = useConfig() const lang = locale.datepicker - // 定义中文文本映射 const zhCNType: { [key: string]: string } = { day: lang.day, year: lang.year, @@ -122,75 +73,94 @@ export const DatePicker: FunctionComponent< seconds: lang.seconds, } - // 定义 Picker 的值和选项的状态 const [pickerValue, setPickerValue] = useState<(string | number)[]>([]) const [pickerOptions, setPickerOptions] = useState([]) - // 使用 usePropsValue 管理选中的日期值 const [selectedDate, setSelectedDate] = usePropsValue({ - value: props.value && formatValue(props.value, startDate, endDate), // 当前值 + value: props.value && formatValue(props.value, startDate, endDate), defaultValue: - props.defaultValue && formatValue(props.defaultValue, startDate, endDate), // 默认值 - finalValue: 0, // 最终值 + props.defaultValue && formatValue(props.defaultValue, startDate, endDate), + finalValue: 0, }) const [innerDate, setInnerDate] = useState(selectedDate) - useEffect(() => { - setInnerDate(selectedDate) - }, [selectedDate]) - /** - * 比较新旧日期,如果不同则更新选中日期并触发 onChange 回调 - * @param currentDate 当前选中的日期时间戳 - * @param newDate 新选中的日期对象 - * @param selectedOptions 选中的选项数组 - * @param index 当前列的索引 - */ + const [innerVisible, setInnerVisible] = usePropsValue({ + value: props.visible, + defaultValue: false, + finalValue: false, + }) + + const actions: DatePickerActions = { + open: () => { + setInnerVisible(true) + }, + close: () => { + setInnerVisible(false) + }, + } + + useImperativeHandle(ref, () => actions) + const handleDateComparison = ( newDate: Date | null, selectedOptions: PickerOption[], index: number ) => { - // 比较当前日期和新日期的时间戳是否相同 const isEqual = new Date(innerDate)?.getTime() === newDate?.getTime() - - // 如果新日期有效且与当前日期不同 if (newDate && isDate(newDate)) { if (!isEqual) { - // 更新选中的日期 setInnerDate(formatValue(newDate, startDate, endDate)) } - - // 触发 onChange 回调,传递选中的选项和日期信息 onChange?.( selectedOptions, [ - String(newDate.getFullYear()), // 年份 - String(newDate.getMonth() + 1), // 月份(注意:getMonth() 返回 0-11,需要加 1) - String(newDate.getDate()), // 日期 + String(newDate.getFullYear()), + String(newDate.getMonth() + 1), + String(newDate.getDate()), ], - index // 当前列的索引 + index ) } } - const handleDateComparison1 = ( - newDate: Date | null, - selectedOptions: PickerOption[], - index: number - ) => { + const handleConfirmDateComparison = (newDate: Date | null) => { if (newDate && isDate(newDate)) { const isEqual = new Date(selectedDate)?.getTime() === newDate?.getTime() if (!isEqual) { - console.log('确认时修改值', formatValue(newDate, startDate, endDate)) setSelectedDate(formatValue(newDate, startDate, endDate)) } } } + const handleCancel = () => { + setInnerDate(selectedDate) + onCancel?.() + } + + const handleClose = () => { + setInnerVisible(false) + onClose?.() + } + + const handleConfirm = ( + options: PickerOption[], + value: (string | number)[] + ) => { + handlePickerValueChange( + options, + value, + 0, + type, + defaultValue || startDate || endDate, + handleConfirmDateComparison + ) + onConfirm?.(options, value) + } + const handleChange = ( selectedOptions: PickerOption[], - selectedValue: (number | string)[], + selectedValue: (string | number)[], index: number ) => { handlePickerValueChange( @@ -203,12 +173,7 @@ export const DatePicker: FunctionComponent< ) } - /** - * 生成 Picker 的列数据 - * @returns 返回生成的 Picker 列数据数组 - */ const generatePickerColumns = (): PickerOption[][] => { - // 生成日期选择器的范围配置 const dateRanges = generateDatePickerRanges( type, innerDate, @@ -216,79 +181,58 @@ export const DatePicker: FunctionComponent< endDate ) - // 遍历范围配置,生成每一列的选项 const columns = dateRanges.map((rangeConfig, columnIndex) => { - // 获取当前列的类型和选中值 const { type: columnType, range } = rangeConfig const selectedValue = getDatePartValue(columnType, innerDate) - // 生成当前列的选项,并设置选中值 const pickerColumn = generatePickerColumnWithCallback( - range[0], // 最小值 - range[1], // 最大值 - selectedValue, // 当前选中的值 - columnType, // 列的类型 - minuteStep, // 分钟步长 + range[0], + range[1], + selectedValue, + columnType, + minuteStep, (selectedIndex, options) => { - // 更新 pickerValue 中对应列的值 pickerValue[columnIndex] = options[selectedIndex]?.value setPickerValue([...pickerValue]) }, - showChinese, // 是否显示中文 - zhCNType, // 中文文本映射 - formatter // 自定义格式化函数 + showChinese, + zhCNType, + formatter ) - // 如果提供了 filter 函数,则对选项进行过滤 if (filter?.(columnType, pickerColumn)) { return filter(columnType, pickerColumn) } - // 返回当前列的选项 return pickerColumn }) - // 返回生成的列数据,如果为空则返回空数组 return columns || [] } - // 当 selectedDate、startDate 或 endDate 变化时,重新生成 Picker 列数据 useEffect(() => { - setPickerOptions(generatePickerColumns()) - }, [innerDate, startDate, endDate]) - - const handleCancel = () => { setInnerDate(selectedDate) - onCancel?.() - } + }, [selectedDate]) - const handleConfirm = (options: PickerOption[], value: PickerValue[]) => { - handlePickerValueChange( - options, - value, - 0, - type, - defaultValue || startDate || endDate, - handleDateComparison1 - ) - onConfirm?.(options, value) - } + useEffect(() => { + setPickerOptions(generatePickerColumns()) + }, [innerDate, startDate, endDate]) return (
- {pickerOptions.length > 0 && ( + {pickerOptions.length && innerVisible && ( handleChange(options, value, index)} threeDimensional={threeDimensional} @@ -298,4 +242,7 @@ export const DatePicker: FunctionComponent< ) } -DatePicker.displayName = 'NutDatePicker' +const DatePicker = React.forwardRef>( + InternalPicker +) +export default DatePicker diff --git a/src/packages/datepicker/demos/h5/demo1.tsx b/src/packages/datepicker/demos/h5/demo1.tsx index f227a06824..ff87453056 100644 --- a/src/packages/datepicker/demos/h5/demo1.tsx +++ b/src/packages/datepicker/demos/h5/demo1.tsx @@ -1,25 +1,50 @@ import React, { useState } from 'react' import { DatePicker, Cell, type PickerOption } from '@nutui/nutui-react' +import isEqual from 'react-fast-compare' + +const useDatePicker = (initialDate: Date) => { + const defaultDateObj = { + year: initialDate.getFullYear(), + month: initialDate.getMonth() + 1, + day: initialDate.getDate(), + } + + const defaultDesc = `${defaultDateObj.year}年${defaultDateObj.month}月${defaultDateObj.day}日` + const defaultValue = Object.values(defaultDateObj).join('-') + + return { defaultDesc, defaultValue } +} const Demo1 = () => { - const defaultValue = new Date() - const defaultDescription = `${defaultValue.getFullYear()}-${ - defaultValue.getMonth() + 1 - }-${defaultValue.getDate()}` - const [show, setShow1] = useState(false) - const [desc1, setDesc1] = useState(defaultDescription) - - const [value, setValue] = useState('2023/01/01') + const defaultDate = new Date() + const { defaultDesc: defaultDesc1, defaultValue: defaultValue1 } = + useDatePicker(defaultDate) + const { defaultDesc: defaultDesc2, defaultValue: defaultValue2 } = + useDatePicker(defaultDate) + + const [show1, setShow1] = useState(false) + const [desc1, setDesc1] = useState(defaultDesc1) + + const [value, setValue] = useState(defaultValue2) const [show2, setShow2] = useState(false) - const [desc2, setDesc2] = useState('') - const confirm1 = (values: (string | number)[], options: PickerOption[]) => { - setDesc1(options.map((option) => option.text).join(' ')) - } - const change = (options: PickerOption[], values: (string | number)[]) => { - const v = values.join('/') - setValue(v) - setDesc2(options.map((option) => option.text).join(' ')) - } + const [desc2, setDesc2] = useState(defaultDesc2) + + const handleConfirm = + (setDesc: (desc: string) => void, setValue?: (value: string) => void) => + (options: PickerOption[], values: (string | number)[]) => { + if (setValue) { + if (isEqual(values, ['2026', '02', '21'])) { + setValue('2026/03/22') + setDesc('2026年03月22日') + } else { + setValue(values.join('/')) + setDesc(options.map((option) => option.text).join('')) + } + } else { + setDesc(options.map((option) => option.text).join('')) + } + } + return ( <> { /> setShow1(false)} - onConfirm={(options, values) => { - setShow1(false) - confirm1(values, options) - console.log('onconfirm') - }} + onClose={() => setShow1(false)} + onConfirm={handleConfirm(setDesc1)} /> { showChinese onClose={() => setShow2(false)} threeDimensional={false} - onChange={(options, values) => change(options, values)} + onConfirm={handleConfirm(setDesc2, setValue)} /> ) } + export default Demo1 diff --git a/src/packages/datepicker/demos/h5/demo2.tsx b/src/packages/datepicker/demos/h5/demo2.tsx index bd4350dcde..60a695dc40 100644 --- a/src/packages/datepicker/demos/h5/demo2.tsx +++ b/src/packages/datepicker/demos/h5/demo2.tsx @@ -20,9 +20,9 @@ const Demo2 = () => { /> setShow(false)} diff --git a/src/packages/datepicker/index.ts b/src/packages/datepicker/index.ts index 0c83b853f5..976678232c 100644 --- a/src/packages/datepicker/index.ts +++ b/src/packages/datepicker/index.ts @@ -1,4 +1,4 @@ -import { DatePicker } from './datepicker' +import DatePicker from './datepicker' -export type { DatePickerProps } from './datepicker' +export type { DatePickerProps } from './types' export default DatePicker diff --git a/src/packages/datepicker/types.ts b/src/packages/datepicker/types.ts new file mode 100644 index 0000000000..6e406cc582 --- /dev/null +++ b/src/packages/datepicker/types.ts @@ -0,0 +1,55 @@ +import { BasicComponent } from '@/utils/typings' +import { PickerProps } from '../picker' +import { PickerOption } from '../picker/types' + +export type DatePickerRef = DatePickerActions +export type DatePickerActions = { + open: () => void + close: () => void +} + +export interface DatePickerProps extends BasicComponent { + value?: Date + defaultValue?: Date + visible: boolean + title: string + type: + | 'date' + | 'time' + | 'year-month' + | 'month-day' + | 'datehour' + | 'datetime' + | 'hour-minutes' + showChinese: boolean + minuteStep: number + startDate: Date + endDate: Date + threeDimensional: boolean + pickerProps: Partial< + Omit< + PickerProps, + | 'defaultValue' + | 'threeDimensional' + | 'title' + | 'value' + | 'onConfirm' + | 'onClose' + | 'onCancel' + | 'onChange' + > + > + formatter: (type: string, option: PickerOption) => PickerOption + filter: (type: string, option: PickerOption[]) => PickerOption[] + onClose: () => void + onCancel: () => void + onConfirm: ( + selectedOptions: PickerOption[], + selectedValue: (string | number)[] + ) => void + onChange?: ( + selectedOptions: PickerOption[], + selectedValue: (string | number)[], + columnIndex: number + ) => void +} diff --git a/src/packages/datepicker/utils.ts b/src/packages/datepicker/utils.ts index 2caf265623..512c4d86a3 100644 --- a/src/packages/datepicker/utils.ts +++ b/src/packages/datepicker/utils.ts @@ -1,7 +1,6 @@ import { padZero } from '@/utils/pad-zero' import { isDate } from '@/utils/is-date' import { PickerOption } from '../picker/types' -import { PickerValue } from '../pickerview/types' /** * 获取指定年份和月份的最后一天 @@ -273,7 +272,7 @@ export const formatValue = ( */ export const handlePickerValueChange = ( selectedOptions: PickerOption[], - selectedValue: PickerValue[], + selectedValue: (string | number)[], index: number, type: string, defaultDate: Date, @@ -291,7 +290,7 @@ export const handlePickerValueChange = ( rangeType ) ) { - const formattedDate: PickerValue[] = [] + const formattedDate: (string | number)[] = [] // 将选中的值转换为数组 selectedValue.forEach((item) => { From f0e4d6cedce4f65f08dbb49370c0b610a574efef Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Fri, 21 Feb 2025 16:00:17 +0800 Subject: [PATCH 05/10] =?UTF-8?q?fix:=20=E6=94=AF=E6=8C=81=E6=8C=87?= =?UTF-8?q?=E4=BB=A4=E5=BC=8F=E8=B0=83=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/packages/datepicker/datepicker.tsx | 44 ++++++++++++++------------ src/packages/datepicker/types.ts | 1 + src/packages/form/demos/h5/demo7.tsx | 35 ++++++++++++++++++++ 3 files changed, 60 insertions(+), 20 deletions(-) diff --git a/src/packages/datepicker/datepicker.tsx b/src/packages/datepicker/datepicker.tsx index 20519663f1..4d3d9e00a7 100644 --- a/src/packages/datepicker/datepicker.tsx +++ b/src/packages/datepicker/datepicker.tsx @@ -55,6 +55,7 @@ const InternalPicker: ForwardRefRenderFunction< threeDimensional, className, style, + children, ...rest } = { ...defaultProps, @@ -219,26 +220,29 @@ const InternalPicker: ForwardRefRenderFunction< }, [innerDate, startDate, endDate]) return ( -
- {pickerOptions.length && innerVisible && ( - handleChange(options, value, index)} - threeDimensional={threeDimensional} - /> - )} -
+ <> + {typeof children === 'function' && children(selectedDate)} +
+ {pickerOptions.length && innerVisible && ( + handleChange(options, value, index)} + threeDimensional={threeDimensional} + /> + )} +
+ ) } diff --git a/src/packages/datepicker/types.ts b/src/packages/datepicker/types.ts index 6e406cc582..1ff1bc5517 100644 --- a/src/packages/datepicker/types.ts +++ b/src/packages/datepicker/types.ts @@ -52,4 +52,5 @@ export interface DatePickerProps extends BasicComponent { selectedValue: (string | number)[], columnIndex: number ) => void + children?: any } diff --git a/src/packages/form/demos/h5/demo7.tsx b/src/packages/form/demos/h5/demo7.tsx index f0387f47a7..ddd6ac960d 100644 --- a/src/packages/form/demos/h5/demo7.tsx +++ b/src/packages/form/demos/h5/demo7.tsx @@ -12,6 +12,7 @@ import { Rate, Range, Toast, + DatePicker, } from '@nutui/nutui-react' import { ArrowRight } from '@nutui/icons-react' @@ -116,6 +117,40 @@ const Demo7 = () => { }}
+ { + console.log('sssss', args[0]) + return new Date(args[1].join('/')) + }} + onClick={(event, ref: any) => { + ref.open() + }} + initialValue={new Date()} + > + + {(value: any) => { + return ( + } + align="center" + /> + ) + }} + + Date: Fri, 21 Feb 2025 16:54:41 +0800 Subject: [PATCH 06/10] refactor: datepicker taro --- src/packages/datepicker/datepicker.taro.tsx | 475 ++++++------------- src/packages/datepicker/demos/taro/demo1.tsx | 68 ++- src/packages/datepicker/demos/taro/demo2.tsx | 7 +- src/packages/datepicker/index.taro.ts | 4 +- src/packages/form/demos/taro/demo7.tsx | 35 ++ 5 files changed, 235 insertions(+), 354 deletions(-) diff --git a/src/packages/datepicker/datepicker.taro.tsx b/src/packages/datepicker/datepicker.taro.tsx index 702f1e4e07..56dafee7e7 100644 --- a/src/packages/datepicker/datepicker.taro.tsx +++ b/src/packages/datepicker/datepicker.taro.tsx @@ -1,59 +1,26 @@ -import React, { FunctionComponent, useState, useEffect } from 'react' +import React, { + useState, + useEffect, + ForwardRefRenderFunction, + useImperativeHandle, +} from 'react' import { View } from '@tarojs/components' -import Picker, { PickerOption, PickerProps } from '@/packages/picker/index.taro' +import Picker, { PickerOption } from '@/packages/picker/index.taro' import { useConfig } from '@/packages/configprovider/index.taro' import { usePropsValue } from '@/hooks/use-props-value' -import { BasicComponent, ComponentDefaults } from '@/utils/typings' +import { ComponentDefaults } from '@/utils/typings' import { isDate } from '@/utils/is-date' -import { padZero } from '@/utils/pad-zero' - -export interface DatePickerProps extends BasicComponent { - value?: Date - defaultValue?: Date - visible: boolean - title: string - type: - | 'date' - | 'time' - | 'year-month' - | 'month-day' - | 'datehour' - | 'datetime' - | 'hour-minutes' - showChinese: boolean - minuteStep: number - startDate: Date - endDate: Date - threeDimensional: boolean - pickerProps: Partial< - Omit< - PickerProps, - | 'defaultValue' - | 'threeDimensional' - | 'title' - | 'value' - | 'onConfirm' - | 'onClose' - | 'onCancel' - | 'onChange' - > - > - formatter: (type: string, option: PickerOption) => PickerOption - filter: (type: string, option: PickerOption[]) => PickerOption[] - onClose: () => void - onCancel: () => void - onConfirm: ( - selectedOptions: PickerOption[], - selectedValue: (string | number)[] - ) => void - onChange?: ( - selectedOptions: PickerOption[], - selectedValue: (string | number)[], - columnIndex: number - ) => void -} +import { + formatValue, + generateDatePickerRanges, + generatePickerColumnWithCallback, + getDatePartValue, + handlePickerValueChange, +} from './utils' +import { DatePickerActions, DatePickerProps, DatePickerRef } from './types' const currentYear = new Date().getFullYear() + const defaultProps = { ...ComponentDefaults, visible: false, @@ -66,10 +33,10 @@ const defaultProps = { endDate: new Date(currentYear + 10, 11, 31), } as DatePickerProps -export const DatePicker: FunctionComponent< - Partial & - Omit, 'onChange' | 'defaultValue'> -> = (props) => { +const InternalPicker: ForwardRefRenderFunction< + DatePickerRef, + Partial +> = (props, ref) => { const { startDate, endDate, @@ -89,13 +56,16 @@ export const DatePicker: FunctionComponent< threeDimensional, className, style, + children, ...rest } = { ...defaultProps, ...props, } + const { locale } = useConfig() const lang = locale.datepicker + const zhCNType: { [key: string]: string } = { day: lang.day, year: lang.year, @@ -104,127 +74,45 @@ export const DatePicker: FunctionComponent< minute: lang.min, seconds: lang.seconds, } + const [pickerValue, setPickerValue] = useState<(string | number)[]>([]) const [pickerOptions, setPickerOptions] = useState([]) - const formatValue = (value: Date | null) => { - if (!value || (value && !isDate(value))) { - value = startDate - } - return Math.min( - Math.max(value.getTime(), startDate.getTime()), - endDate.getTime() - ) - } + const [selectedDate, setSelectedDate] = usePropsValue({ - value: props.value && formatValue(props.value), - defaultValue: props.defaultValue && formatValue(props.defaultValue), + value: props.value && formatValue(props.value, startDate, endDate), + defaultValue: + props.defaultValue && formatValue(props.defaultValue, startDate, endDate), finalValue: 0, }) - function getMonthEndDay(year: number, month: number): number { - return new Date(year, month, 0).getDate() - } + const [innerDate, setInnerDate] = useState(selectedDate) - const getBoundary = (type: string, value: Date) => { - const boundary = type === 'min' ? startDate : endDate - const year = boundary.getFullYear() - let month = 1 - let date = 1 - let hour = 0 - let minute = 0 + const [innerVisible, setInnerVisible] = usePropsValue({ + value: props.visible, + defaultValue: false, + finalValue: false, + }) - if (type === 'max') { - month = 12 - date = getMonthEndDay(value.getFullYear(), value.getMonth() + 1) - hour = 23 - minute = 59 - } - const seconds = minute - if (value.getFullYear() === year) { - month = boundary.getMonth() + 1 - if (value.getMonth() + 1 === month) { - date = boundary.getDate() - if (value.getDate() === date) { - hour = boundary.getHours() - if (value.getHours() === hour) { - minute = boundary.getMinutes() - } - } - } - } - return { - [`${type}Year`]: year, - [`${type}Month`]: month, - [`${type}Date`]: date, - [`${type}Hour`]: hour, - [`${type}Minute`]: minute, - [`${type}Seconds`]: seconds, - } + const actions: DatePickerActions = { + open: () => { + setInnerVisible(true) + }, + close: () => { + setInnerVisible(false) + }, } - const ranges = () => { - const selected = new Date(selectedDate) - if (!selected) return [] - const { maxYear, maxDate, maxMonth, maxHour, maxMinute, maxSeconds } = - getBoundary('max', selected) - const { minYear, minDate, minMonth, minHour, minMinute, minSeconds } = - getBoundary('min', selected) - const result = [ - { - type: 'year', - range: [minYear, maxYear], - }, - { - type: 'month', - range: [minMonth, maxMonth], - }, - { - type: 'day', - range: [minDate, maxDate], - }, - { - type: 'hour', - range: [minHour, maxHour], - }, - { - type: 'minute', - range: [minMinute, maxMinute], - }, - { - type: 'seconds', - range: [minSeconds, maxSeconds], - }, - ] - switch (type.toLocaleLowerCase()) { - case 'date': - return result.slice(0, 3) - case 'datetime': - return result.slice(0, 5) - case 'time': - return result.slice(3, 6) - case 'year-month': - return result.slice(0, 2) - case 'hour-minutes': - return result.slice(3, 5) - case 'month-day': - return result.slice(1, 3) - case 'datehour': - return result.slice(0, 4) - default: - return result - } - } + useImperativeHandle(ref, () => actions) - const compareDateChange = ( - currentDate: number, + const handleDateComparison = ( newDate: Date | null, selectedOptions: PickerOption[], index: number ) => { - const isEqual = new Date(currentDate)?.getTime() === newDate?.getTime() + const isEqual = new Date(innerDate)?.getTime() === newDate?.getTime() if (newDate && isDate(newDate)) { if (!isEqual) { - setSelectedDate(formatValue(newDate as Date)) + setInnerDate(formatValue(newDate, startDate, endDate)) } onChange?.( selectedOptions, @@ -237,194 +125,129 @@ export const DatePicker: FunctionComponent< ) } } - const handlePickerChange = ( - selectedOptions: PickerOption[], - selectedValue: (number | string)[], - index: number - ) => { - const rangeType = type.toLocaleLowerCase() - if ( - ['date', 'datetime', 'datehour', 'month-day', 'year-month'].includes( - rangeType - ) - ) { - const formatDate: (number | string)[] = [] - selectedValue.forEach((item) => { - formatDate.push(item) - }) - if (rangeType === 'month-day' && formatDate.length < 3) { - formatDate.unshift( - new Date(defaultValue || startDate || endDate).getFullYear() - ) - } - - if (rangeType === 'year-month' && formatDate.length < 3) { - formatDate.push( - new Date(defaultValue || startDate || endDate).getDate() - ) - } - const year = Number(formatDate[0]) - const month = Number(formatDate[1]) - 1 - const day = Math.min( - Number(formatDate[2]), - getMonthEndDay(Number(formatDate[0]), Number(formatDate[1])) - ) - let date: Date | null = null - if ( - rangeType === 'date' || - rangeType === 'month-day' || - rangeType === 'year-month' - ) { - date = new Date(year, month, day) - } else if (rangeType === 'datetime') { - date = new Date( - year, - month, - day, - Number(formatDate[3]), - Number(formatDate[4]) - ) - } else if (rangeType === 'datehour') { - date = new Date(year, month, day, Number(formatDate[3])) + const handleConfirmDateComparison = (newDate: Date | null) => { + if (newDate && isDate(newDate)) { + const isEqual = new Date(selectedDate)?.getTime() === newDate?.getTime() + if (!isEqual) { + setSelectedDate(formatValue(newDate, startDate, endDate)) } + } + } - compareDateChange(selectedDate, date, selectedOptions, index) - } else { - // 'hour-minutes' 'time' - const [hour, minute, seconds] = selectedValue - const currentDate = new Date(selectedDate) - const year = currentDate.getFullYear() - const month = currentDate.getMonth() - const day = currentDate.getDate() + const handleCancel = () => { + setInnerDate(selectedDate) + onCancel?.() + } - const date = new Date( - year, - month, - day, - Number(hour), - Number(minute), - rangeType === 'time' ? Number(seconds) : 0 - ) - compareDateChange(selectedDate, date, selectedOptions, index) - } + const handleClose = () => { + setInnerVisible(false) + onClose?.() } - const formatOption = (type: string, value: string | number) => { - if (formatter) { - return formatter(type, { - text: padZero(value, 2), - value: padZero(value, 2), - }) - } - const padMin = padZero(value, 2) - const fatter = showChinese ? zhCNType[type] : '' - return { text: padMin + fatter, value: padMin } + const handleConfirm = ( + options: PickerOption[], + value: (string | number)[] + ) => { + handlePickerValueChange( + options, + value, + 0, + type, + defaultValue || startDate || endDate, + handleConfirmDateComparison + ) + onConfirm?.(options, value) } - const generateColumn = ( - min: number, - max: number, - val: number | string, - type: string, - columnIndex: number + const handleChange = ( + selectedOptions: PickerOption[], + selectedValue: (string | number)[], + index: number ) => { - let cmin = min - const arr: Array = [] - let index = 0 - while (cmin <= max) { - arr.push(formatOption(type, cmin)) + handlePickerValueChange( + selectedOptions, + selectedValue, + index, + type, + defaultValue || startDate || endDate, + handleDateComparison + ) + } - if (type === 'minute') { - cmin += minuteStep - } else { - cmin++ - } + const generatePickerColumns = (): PickerOption[][] => { + const dateRanges = generateDatePickerRanges( + type, + innerDate, + startDate, + endDate + ) - if (cmin <= Number(val)) { - index++ - } - } + const columns = dateRanges.map((rangeConfig, columnIndex) => { + const { type: columnType, range } = rangeConfig + const selectedValue = getDatePartValue(columnType, innerDate) + + const pickerColumn = generatePickerColumnWithCallback( + range[0], + range[1], + selectedValue, + columnType, + minuteStep, + (selectedIndex, options) => { + pickerValue[columnIndex] = options[selectedIndex]?.value + setPickerValue([...pickerValue]) + }, + showChinese, + zhCNType, + formatter + ) - pickerValue[columnIndex] = arr[index]?.value - setPickerValue([...pickerValue]) + if (filter?.(columnType, pickerColumn)) { + return filter(columnType, pickerColumn) + } - if (filter?.(type, arr)) { - return filter?.(type, arr) - } - return arr - } + return pickerColumn + }) - const getDateIndex = (type: string) => { - const date = new Date(selectedDate) - if (!selectedDate) return 0 - if (type === 'year') { - return date.getFullYear() - } - if (type === 'month') { - return date.getMonth() + 1 - } - if (type === 'day') { - return date.getDate() - } - if (type === 'hour') { - return date.getHours() - } - if (type === 'minute') { - return date.getMinutes() - } - if (type === 'seconds') { - return date.getSeconds() - } - return 0 + return columns || [] } - const columns = () => { - const val = ranges().map((res, columnIndex) => { - return generateColumn( - res.range[0], - res.range[1], - getDateIndex(res.type), - res.type, - columnIndex - ) - }) - return val || [] - } + useEffect(() => { + setInnerDate(selectedDate) + }, [selectedDate]) useEffect(() => { - setPickerOptions(columns()) - }, [selectedDate, startDate, endDate]) + setPickerOptions(generatePickerColumns()) + }, [innerDate, startDate, endDate]) return ( - - {pickerOptions.length > 0 && ( - - onConfirm && onConfirm(options, value) - } - onChange={( - options: PickerOption[], - value: (number | string)[], - index: number - ) => handlePickerChange(options, value, index)} - threeDimensional={threeDimensional} - /> - )} - + <> + {typeof children === 'function' && children(selectedDate)} + + {pickerOptions.length && innerVisible && ( + handleChange(options, value, index)} + threeDimensional={threeDimensional} + /> + )} + + ) } -DatePicker.displayName = 'NutDatePicker' +const DatePicker = React.forwardRef>( + InternalPicker +) +export default DatePicker diff --git a/src/packages/datepicker/demos/taro/demo1.tsx b/src/packages/datepicker/demos/taro/demo1.tsx index dadd406cb0..a81c77dfc5 100644 --- a/src/packages/datepicker/demos/taro/demo1.tsx +++ b/src/packages/datepicker/demos/taro/demo1.tsx @@ -1,25 +1,50 @@ import React, { useState } from 'react' import { DatePicker, Cell, type PickerOption } from '@nutui/nutui-react-taro' +import isEqual from 'react-fast-compare' + +const useDatePicker = (initialDate: Date) => { + const defaultDateObj = { + year: initialDate.getFullYear(), + month: initialDate.getMonth() + 1, + day: initialDate.getDate(), + } + + const defaultDesc = `${defaultDateObj.year}年${defaultDateObj.month}月${defaultDateObj.day}日` + const defaultValue = Object.values(defaultDateObj).join('-') + + return { defaultDesc, defaultValue } +} const Demo1 = () => { - const defaultValue = new Date() - const defaultDescription = `${defaultValue.getFullYear()}-${ - defaultValue.getMonth() + 1 - }-${defaultValue.getDate()}` + const defaultDate = new Date() + const { defaultDesc: defaultDesc1, defaultValue: defaultValue1 } = + useDatePicker(defaultDate) + const { defaultDesc: defaultDesc2, defaultValue: defaultValue2 } = + useDatePicker(defaultDate) + const [show1, setShow1] = useState(false) - const [desc1, setDesc1] = useState(defaultDescription) + const [desc1, setDesc1] = useState(defaultDesc1) - const [value, setValue] = useState('2023/01/01') + const [value, setValue] = useState(defaultValue2) const [show2, setShow2] = useState(false) - const [desc2, setDesc2] = useState('') - const confirm = (values: (string | number)[], options: PickerOption[]) => { - setDesc1(options.map((option) => option.text).join(' ')) - } - const change = (options: PickerOption[], values: (string | number)[]) => { - const v = values.join('/') - setValue(v) - setDesc2(options.map((option) => option.text).join(' ')) - } + const [desc2, setDesc2] = useState(defaultDesc2) + + const handleConfirm = + (setDesc: (desc: string) => void, setValue?: (value: string) => void) => + (options: PickerOption[], values: (string | number)[]) => { + if (setValue) { + if (isEqual(values, ['2026', '02', '21'])) { + setValue('2026/03/22') + setDesc('2026年03月22日') + } else { + setValue(values.join('/')) + setDesc(options.map((option) => option.text).join('')) + } + } else { + setDesc(options.map((option) => option.text).join('')) + } + } + return ( <> { pickerProps={{ popupProps: { zIndex: 1220 }, }} - defaultValue={new Date(`${defaultDescription}`)} + defaultValue={new Date(defaultValue1)} showChinese - onCancel={() => setShow1(false)} - onConfirm={(options, values) => { - setShow1(false) - confirm(values, options) - console.log('onconfirm') - }} + onClose={() => setShow1(false)} + onConfirm={handleConfirm(setDesc1)} /> { showChinese onClose={() => setShow2(false)} threeDimensional={false} - onChange={(options, values) => change(options, values)} + onConfirm={handleConfirm(setDesc2, setValue)} /> ) } + export default Demo1 diff --git a/src/packages/datepicker/demos/taro/demo2.tsx b/src/packages/datepicker/demos/taro/demo2.tsx index 7d3b42edaf..947d497e95 100644 --- a/src/packages/datepicker/demos/taro/demo2.tsx +++ b/src/packages/datepicker/demos/taro/demo2.tsx @@ -7,6 +7,7 @@ const Demo2 = () => { const [desc, setDesc] = useState( `${defaultValue.getMonth() + 1}-${defaultValue.getDate()}` ) + const confirm = (values: (string | number)[], options: PickerOption[]) => { setDesc(options.map((option) => option.text).join('-')) } @@ -19,9 +20,9 @@ const Demo2 = () => { /> setShow(false)} diff --git a/src/packages/datepicker/index.taro.ts b/src/packages/datepicker/index.taro.ts index d990ce8459..976678232c 100644 --- a/src/packages/datepicker/index.taro.ts +++ b/src/packages/datepicker/index.taro.ts @@ -1,4 +1,4 @@ -import { DatePicker } from './datepicker.taro' +import DatePicker from './datepicker' -export type { DatePickerProps } from './datepicker.taro' +export type { DatePickerProps } from './types' export default DatePicker diff --git a/src/packages/form/demos/taro/demo7.tsx b/src/packages/form/demos/taro/demo7.tsx index bee2dae660..34141a06fd 100644 --- a/src/packages/form/demos/taro/demo7.tsx +++ b/src/packages/form/demos/taro/demo7.tsx @@ -12,6 +12,7 @@ import { Button, Rate, Range, + DatePicker, } from '@nutui/nutui-react-taro' import { ArrowRight } from '@nutui/icons-react-taro' import { View } from '@tarojs/components' @@ -117,6 +118,40 @@ const Demo7 = () => { }} + { + console.log('sssss', args[0]) + return new Date(args[1].join('/')) + }} + onClick={(event, ref: any) => { + ref.open() + }} + initialValue={new Date()} + > + + {(value: any) => { + return ( + } + align="center" + /> + ) + }} + + Date: Mon, 24 Feb 2025 14:52:31 +0800 Subject: [PATCH 07/10] =?UTF-8?q?chore:=20=E7=A7=BB=E9=99=A4=E6=B3=A8?= =?UTF-8?q?=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/packages/datepicker/utils.ts | 136 ++++++++++++------------------- 1 file changed, 52 insertions(+), 84 deletions(-) diff --git a/src/packages/datepicker/utils.ts b/src/packages/datepicker/utils.ts index 512c4d86a3..cc82353bc2 100644 --- a/src/packages/datepicker/utils.ts +++ b/src/packages/datepicker/utils.ts @@ -26,36 +26,27 @@ export const calculateDateBoundary = ( startDate: Date, endDate: Date ) => { - // 根据类型选择边界日期:'min' 使用 startDate,'max' 使用 endDate const boundary = type === 'min' ? startDate : endDate - - // 获取边界日期的年份 const year = boundary.getFullYear() - - // 初始化月份、日期、小时和分钟 - const isMax = type === 'max' // 是否为 'max' 类型 - let month = isMax ? 12 : 1 // 'max' 时月份为 12,否则为 1 + const isMax = type === 'max' + let month = isMax ? 12 : 1 let date = isMax ? getLastDayOfMonth(value.getFullYear(), value.getMonth() + 1) - : 1 // 'max' 时日期为当前月份的最后一天,否则为 1 - let hour = isMax ? 23 : 0 // 'max' 时小时为 23,否则为 0 - let minute = isMax ? 59 : 0 // 'max' 时分钟为 59,否则为 0 + : 1 + let hour = isMax ? 23 : 0 + let minute = isMax ? 59 : 0 - // 如果传入日期的年份与边界日期的年份相同 if (value.getFullYear() === year) { - month = boundary.getMonth() + 1 // 使用边界日期的月份 + month = boundary.getMonth() + 1 - // 如果传入日期的月份与边界日期的月份相同 if (value.getMonth() + 1 === month) { - date = boundary.getDate() // 使用边界日期的日期 + date = boundary.getDate() - // 如果传入日期的日期与边界日期的日期相同 if (value.getDate() === date) { - hour = boundary.getHours() // 使用边界日期的小时 + hour = boundary.getHours() - // 如果传入日期的小时与边界日期的小时相同 if (value.getHours() === hour) { - minute = boundary.getMinutes() // 使用边界日期的分钟 + minute = boundary.getMinutes() } } } @@ -63,11 +54,11 @@ export const calculateDateBoundary = ( // 返回边界值的对象 return { - [`${type}Year`]: year, // 返回年份 - [`${type}Month`]: month, // 返回月份 - [`${type}Date`]: date, // 返回日期 - [`${type}Hour`]: hour, // 返回小时 - [`${type}Minute`]: minute, // 返回分钟 + [`${type}Year`]: year, + [`${type}Month`]: month, + [`${type}Date`]: date, + [`${type}Hour`]: hour, + [`${type}Minute`]: minute, [`${type}Seconds`]: minute, // 返回秒数(与分钟相同) } } @@ -82,9 +73,8 @@ export const generateDatePickerRanges = ( startDate: Date, endDate: Date ) => { - // 将选中的日期转换为 Date 对象 const selected = new Date(selectedDate) - if (!selected) return [] // 如果选中的日期无效,返回空数组 + if (!selected) return [] // 获取最大和最小边界值 const { maxYear, maxDate, maxMonth, maxHour, maxMinute, maxSeconds } = @@ -92,34 +82,33 @@ export const generateDatePickerRanges = ( const { minYear, minDate, minMonth, minHour, minMinute, minSeconds } = calculateDateBoundary('min', selected, startDate, endDate) - // 定义完整的日期范围配置 const fullRanges = [ - { type: 'year', range: [minYear, maxYear] }, // 年份范围 - { type: 'month', range: [minMonth, maxMonth] }, // 月份范围 - { type: 'day', range: [minDate, maxDate] }, // 日期范围 - { type: 'hour', range: [minHour, maxHour] }, // 小时范围 - { type: 'minute', range: [minMinute, maxMinute] }, // 分钟范围 - { type: 'seconds', range: [minSeconds, maxSeconds] }, // 秒数范围 + { type: 'year', range: [minYear, maxYear] }, + { type: 'month', range: [minMonth, maxMonth] }, + { type: 'day', range: [minDate, maxDate] }, + { type: 'hour', range: [minHour, maxHour] }, + { type: 'minute', range: [minMinute, maxMinute] }, + { type: 'seconds', range: [minSeconds, maxSeconds] }, ] // 根据类型返回对应的范围配置 switch (type.toLocaleLowerCase()) { case 'date': - return fullRanges.slice(0, 3) // 返回年、月、日 + return fullRanges.slice(0, 3) case 'datetime': - return fullRanges.slice(0, 5) // 返回年、月、日、时、分 + return fullRanges.slice(0, 5) case 'time': - return fullRanges.slice(3, 6) // 返回时、分、秒 + return fullRanges.slice(3, 6) case 'year-month': - return fullRanges.slice(0, 2) // 返回年、月 + return fullRanges.slice(0, 2) case 'hour-minutes': - return fullRanges.slice(3, 5) // 返回时、分 + return fullRanges.slice(3, 5) case 'month-day': - return fullRanges.slice(1, 3) // 返回月、日 + return fullRanges.slice(1, 3) case 'datehour': - return fullRanges.slice(0, 4) // 返回年、月、日、时 + return fullRanges.slice(0, 4) default: - return fullRanges // 返回完整范围 + return fullRanges } } @@ -133,28 +122,25 @@ export const getDatePartValue = ( type: string, selectedDate: number ): number => { - // 将时间戳转换为 Date 对象 const date = new Date(selectedDate) - // 如果选中的日期无效,返回 0 if (!selectedDate) return 0 - // 根据类型返回对应的日期部分值 switch (type) { case 'year': - return date.getFullYear() // 返回年份 + return date.getFullYear() case 'month': - return date.getMonth() + 1 // 返回月份(注意:getMonth() 返回 0-11,需要加 1) + return date.getMonth() + 1 case 'day': - return date.getDate() // 返回日期 + return date.getDate() case 'hour': - return date.getHours() // 返回小时 + return date.getHours() case 'minute': - return date.getMinutes() // 返回分钟 + return date.getMinutes() case 'seconds': - return date.getSeconds() // 返回秒数 + return date.getSeconds() default: - return 0 // 如果类型无效,返回 0 + return 0 } } @@ -179,9 +165,9 @@ export const generatePickerColumnWithCallback = ( zhCNType: { [key: string]: string }, formatter?: (type: string, option: PickerOption) => PickerOption ): PickerOption[] => { - let currentMin = min // 当前最小值 - const options: PickerOption[] = [] // 存储生成的选项 - let selectedIndex = 0 // 当前选中值的索引 + let currentMin = min + const options: PickerOption[] = [] + let selectedIndex = 0 // 遍历从最小值到最大值的范围 while (currentMin <= max) { @@ -203,10 +189,8 @@ export const generatePickerColumnWithCallback = ( } } - // 触发回调函数,返回选中索引 callback(selectedIndex, options) - // 返回生成的选项数组 return options } @@ -226,24 +210,20 @@ export const formatPickerOption = ( zhCNType: { [key: string]: string }, formatter?: (type: string, option: PickerOption) => PickerOption ): PickerOption => { - // 如果提供了自定义格式化函数,则使用该函数格式化选项 if (formatter) { return formatter(type, { - text: padZero(value, 2), // 补零后的文本 - value: padZero(value, 2), // 补零后的值 + text: padZero(value, 2), + value: padZero(value, 2), }) } - // 补零后的值 const paddedValue = padZero(value, 2) - // 如果需要显示中文,添加对应的中文文本 const chineseText = showChinese ? zhCNType[type] : '' - // 返回格式化后的选项 return { - text: paddedValue + chineseText, // 文本 = 补零后的值 + 中文文本 - value: paddedValue, // 值 = 补零后的值 + text: paddedValue + chineseText, + value: paddedValue, } } @@ -256,12 +236,12 @@ export const formatValue = ( endDate: Date ) => { if (!value || (value && !isDate(value))) { - value = startDate // 如果值无效,使用 startDate + value = startDate } return Math.min( Math.max(value.getTime(), startDate.getTime()), endDate.getTime() - ) // 确保日期在范围内 + ) } /** @@ -282,9 +262,8 @@ export const handlePickerValueChange = ( index: number ) => void ) => { - const rangeType = type.toLocaleLowerCase() // 获取日期选择器的类型并转换为小写 + const rangeType = type.toLocaleLowerCase() - // 处理日期相关的类型(如 'date', 'datetime', 'datehour' 等) if ( ['date', 'datetime', 'datehour', 'month-day', 'year-month'].includes( rangeType @@ -292,38 +271,33 @@ export const handlePickerValueChange = ( ) { const formattedDate: (string | number)[] = [] - // 将选中的值转换为数组 selectedValue.forEach((item) => { formattedDate.push(item) }) - // 如果类型是 'month-day' 且缺少年份,补充当前年份 if (rangeType === 'month-day' && formattedDate.length < 3) { formattedDate.unshift(new Date(defaultDate).getFullYear()) } - // 如果类型是 'year-month' 且缺少日期,补充当前日期 if (rangeType === 'year-month' && formattedDate.length < 3) { formattedDate.push(new Date(defaultDate).getDate()) } - // 解析年、月、日 const year = Number(formattedDate[0]) - const month = Number(formattedDate[1]) - 1 // 月份从 0 开始 + const month = Number(formattedDate[1]) - 1 const day = Math.min( Number(formattedDate[2]), - getLastDayOfMonth(year, month + 1) // 获取当前月份的最后一天 + getLastDayOfMonth(year, month + 1) ) let date: Date | null = null - // 根据类型创建日期对象 if ( rangeType === 'date' || rangeType === 'month-day' || rangeType === 'year-month' ) { - date = new Date(year, month, day) // 仅包含年、月、日 + date = new Date(year, month, day) } else if (rangeType === 'datetime') { date = new Date( year, @@ -331,34 +305,28 @@ export const handlePickerValueChange = ( day, Number(formattedDate[3]), Number(formattedDate[4]) - ) // 包含年、月、日、时、分 + ) } else if (rangeType === 'datehour') { - date = new Date(year, month, day, Number(formattedDate[3])) // 包含年、月、日、时 + date = new Date(year, month, day, Number(formattedDate[3])) } - // 比较并处理日期变化 handleDateComparison(date, selectedOptions, index) } else { - // 处理时间相关的类型(如 'hour-minutes', 'time') const [hour, minute, seconds] = selectedValue - - // 获取当前日期的年、月、日 const currentDate = new Date(defaultDate) const year = currentDate.getFullYear() const month = currentDate.getMonth() const day = currentDate.getDate() - // 创建日期对象 const date = new Date( year, month, day, Number(hour), Number(minute), - rangeType === 'time' ? Number(seconds) : 0 // 如果是 'time' 类型,包含秒数 + rangeType === 'time' ? Number(seconds) : 0 ) - // 比较并处理日期变化 handleDateComparison(date, selectedOptions, index) } } From a3d2345012cd457558707a8942d142ccc6b7deb6 Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Mon, 24 Feb 2025 15:15:54 +0800 Subject: [PATCH 08/10] =?UTF-8?q?chore:=20=E6=9B=B4=E6=96=B0=E5=8D=95?= =?UTF-8?q?=E6=B5=8B=E5=BC=95=E5=85=A5=E6=96=B9=E5=BC=8F+picker=E6=8F=90?= =?UTF-8?q?=E5=8F=96pickerProps?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../datepicker/__test__/datepicker.spec.tsx | 2 +- src/packages/datepicker/types.ts | 3 +- src/packages/picker/index.taro.ts | 3 +- src/packages/picker/index.ts | 3 +- src/packages/picker/picker.taro.tsx | 41 ++----------------- src/packages/picker/picker.tsx | 41 ++----------------- src/packages/picker/types.ts | 38 +++++++++++++++++ 7 files changed, 48 insertions(+), 83 deletions(-) diff --git a/src/packages/datepicker/__test__/datepicker.spec.tsx b/src/packages/datepicker/__test__/datepicker.spec.tsx index 2bfbba3dcc..9952adc4b2 100644 --- a/src/packages/datepicker/__test__/datepicker.spec.tsx +++ b/src/packages/datepicker/__test__/datepicker.spec.tsx @@ -1,7 +1,7 @@ import React from 'react' import { render, waitFor, fireEvent } from '@testing-library/react' import '@testing-library/jest-dom' -import { DatePicker } from '../datepicker' +import DatePicker from '../datepicker' const currentYear = new Date().getFullYear() test('Show Chinese', async () => { diff --git a/src/packages/datepicker/types.ts b/src/packages/datepicker/types.ts index 1ff1bc5517..0af55e6625 100644 --- a/src/packages/datepicker/types.ts +++ b/src/packages/datepicker/types.ts @@ -1,6 +1,5 @@ import { BasicComponent } from '@/utils/typings' -import { PickerProps } from '../picker' -import { PickerOption } from '../picker/types' +import { PickerOption, PickerProps } from '../picker/types' export type DatePickerRef = DatePickerActions export type DatePickerActions = { diff --git a/src/packages/picker/index.taro.ts b/src/packages/picker/index.taro.ts index edc33812b3..596e835ed6 100644 --- a/src/packages/picker/index.taro.ts +++ b/src/packages/picker/index.taro.ts @@ -1,5 +1,4 @@ import Picker from './picker.taro' -export type { PickerOption } from './types' -export type { PickerProps } from './picker.taro' +export type { PickerOption, PickerProps } from './types' export default Picker diff --git a/src/packages/picker/index.ts b/src/packages/picker/index.ts index 2f320e8d33..266c22b803 100644 --- a/src/packages/picker/index.ts +++ b/src/packages/picker/index.ts @@ -1,5 +1,4 @@ import Picker from './picker' -export type { PickerOption } from './types' -export type { PickerProps } from './picker' +export type { PickerOption, PickerProps } from './types' export default Picker diff --git a/src/packages/picker/picker.taro.tsx b/src/packages/picker/picker.taro.tsx index 19c1fff276..0946696e95 100644 --- a/src/packages/picker/picker.taro.tsx +++ b/src/packages/picker/picker.taro.tsx @@ -2,60 +2,25 @@ import React, { useState, useEffect, useRef, - RefObject, ForwardRefRenderFunction, useImperativeHandle, } from 'react' import classNames from 'classnames' import Taro from '@tarojs/taro' import { View, PickerView, PickerViewColumn } from '@tarojs/components' -import Popup, { PopupProps } from '@/packages/popup/index.taro' +import Popup from '@/packages/popup/index.taro' import PickerPanel from './pickerpanel.taro' import useRefs from '@/hooks/use-refs' import { useConfig } from '@/packages/configprovider/index.taro' -import { PickerOption } from './types' +import { PickerOption, PickerProps } from './types' import { usePropsValue } from '@/hooks/use-props-value' -import { BasicComponent, ComponentDefaults } from '@/utils/typings' +import { ComponentDefaults } from '@/utils/typings' export type PickerActions = { open: () => void close: () => void } -export interface PickerProps extends Omit { - visible?: boolean | undefined - title?: string - options: (PickerOption | PickerOption[])[] - value?: (number | string)[] - defaultValue?: (number | string)[] - threeDimensional?: boolean - duration: number | string - closeOnOverlayClick: boolean - popupProps: Partial< - Omit - > - onConfirm?: ( - selectedOptions: PickerOption[], - selectedValue: (string | number)[] - ) => void - onCancel?: () => void - onClose?: ( - selectedOptions: PickerOption[], - selectedValue: (string | number)[] - ) => void - afterClose?: ( - selectedOptions: PickerOption[], - selectedValue: (string | number)[], - pickerRef: RefObject - ) => void - onChange?: ( - selectedOptions: PickerOption[], - selectedValue: (string | number)[], - columnIndex: number - ) => void - children?: any -} - const defaultProps = { ...ComponentDefaults, visible: false, diff --git a/src/packages/picker/picker.tsx b/src/packages/picker/picker.tsx index 3ffbb5c2e0..e8faab6f04 100644 --- a/src/packages/picker/picker.tsx +++ b/src/packages/picker/picker.tsx @@ -2,59 +2,24 @@ import React, { useState, useEffect, useRef, - RefObject, ForwardRefRenderFunction, useImperativeHandle, } from 'react' import classNames from 'classnames' -import Popup, { PopupProps } from '@/packages/popup/index' +import Popup from '@/packages/popup/index' import { SafeArea } from '@/packages/safearea/safearea' import PickerPanel from './pickerpanel' import useRefs from '@/hooks/use-refs' import { useConfig } from '@/packages/configprovider' -import { PickerOption } from './types' +import { PickerOption, PickerProps } from './types' import { usePropsValue } from '@/hooks/use-props-value' -import { BasicComponent, ComponentDefaults } from '@/utils/typings' +import { ComponentDefaults } from '@/utils/typings' export type PickerActions = { open: () => void close: () => void } -export interface PickerProps extends Omit { - visible?: boolean | undefined - title?: string - options: (PickerOption | PickerOption[])[] - value?: (number | string)[] - defaultValue?: (number | string)[] - threeDimensional?: boolean - duration: number | string - closeOnOverlayClick: boolean - popupProps: Partial< - Omit - > - onConfirm?: ( - selectedOptions: PickerOption[], - selectedValue: (string | number)[] - ) => void - onCancel?: () => void - onClose?: ( - selectedOptions: PickerOption[], - selectedValue: (string | number)[] - ) => void - afterClose?: ( - selectedOptions: PickerOption[], - selectedValue: (string | number)[], - pickerRef: RefObject - ) => void - onChange?: ( - selectedOptions: PickerOption[], - selectedValue: (string | number)[], - columnIndex: number - ) => void - children?: any -} - const defaultProps = { ...ComponentDefaults, title: '', diff --git a/src/packages/picker/types.ts b/src/packages/picker/types.ts index 5c7e00b846..f59149fe30 100644 --- a/src/packages/picker/types.ts +++ b/src/packages/picker/types.ts @@ -1,3 +1,7 @@ +import { RefObject } from 'react' +import { PopupProps } from '../popup/types' +import { BasicComponent } from '@/utils/typings' + export interface PickerOption { text: string | number value: string | number @@ -5,3 +9,37 @@ export interface PickerOption { children?: PickerOption[] className?: string | number } + +export interface PickerProps extends Omit { + visible?: boolean | undefined + title?: string + options: (PickerOption | PickerOption[])[] + value?: (number | string)[] + defaultValue?: (number | string)[] + threeDimensional?: boolean + duration: number | string + closeOnOverlayClick: boolean + popupProps: Partial< + Omit + > + onConfirm?: ( + selectedOptions: PickerOption[], + selectedValue: (string | number)[] + ) => void + onCancel?: () => void + onClose?: ( + selectedOptions: PickerOption[], + selectedValue: (string | number)[] + ) => void + afterClose?: ( + selectedOptions: PickerOption[], + selectedValue: (string | number)[], + pickerRef: RefObject + ) => void + onChange?: ( + selectedOptions: PickerOption[], + selectedValue: (string | number)[], + columnIndex: number + ) => void + children?: any +} From fb6dfae502b32b67e3eb754d5fba3c7f526e5cd6 Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Mon, 24 Feb 2025 19:20:51 +0800 Subject: [PATCH 09/10] =?UTF-8?q?fix:=20ts=E7=B1=BB=E5=9E=8B=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/packages/datepicker/datepicker.taro.tsx | 3 ++- src/packages/datepicker/index.taro.ts | 2 +- src/packages/datepicker/types.taro.ts | 18 ++++++++++++++++++ src/packages/picker/index.taro.ts | 3 ++- src/packages/picker/picker.taro.tsx | 3 ++- src/packages/picker/types.taro.ts | 8 ++++++++ 6 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 src/packages/datepicker/types.taro.ts create mode 100644 src/packages/picker/types.taro.ts diff --git a/src/packages/datepicker/datepicker.taro.tsx b/src/packages/datepicker/datepicker.taro.tsx index 56dafee7e7..837e904f4a 100644 --- a/src/packages/datepicker/datepicker.taro.tsx +++ b/src/packages/datepicker/datepicker.taro.tsx @@ -17,7 +17,8 @@ import { getDatePartValue, handlePickerValueChange, } from './utils' -import { DatePickerActions, DatePickerProps, DatePickerRef } from './types' +import { DatePickerActions, DatePickerRef } from './types' +import { DatePickerProps } from './types.taro' const currentYear = new Date().getFullYear() diff --git a/src/packages/datepicker/index.taro.ts b/src/packages/datepicker/index.taro.ts index 976678232c..e5aef5354a 100644 --- a/src/packages/datepicker/index.taro.ts +++ b/src/packages/datepicker/index.taro.ts @@ -1,4 +1,4 @@ import DatePicker from './datepicker' -export type { DatePickerProps } from './types' +export type { DatePickerProps } from './types.taro' export default DatePicker diff --git a/src/packages/datepicker/types.taro.ts b/src/packages/datepicker/types.taro.ts new file mode 100644 index 0000000000..d4bfaa00d5 --- /dev/null +++ b/src/packages/datepicker/types.taro.ts @@ -0,0 +1,18 @@ +import { PickerProps } from '@/packages/picker/types.taro' +import { DatePickerProps as DatePickerWebProps } from './types' + +export type DatePickerProps = Omit & { + pickerProps: Partial< + Omit< + PickerProps, + | 'defaultValue' + | 'threeDimensional' + | 'title' + | 'value' + | 'onConfirm' + | 'onClose' + | 'onCancel' + | 'onChange' + > + > +} diff --git a/src/packages/picker/index.taro.ts b/src/packages/picker/index.taro.ts index 596e835ed6..2dc0c9af3c 100644 --- a/src/packages/picker/index.taro.ts +++ b/src/packages/picker/index.taro.ts @@ -1,4 +1,5 @@ import Picker from './picker.taro' -export type { PickerOption, PickerProps } from './types' +export type { PickerOption } from './types' +export type { PickerProps } from './types.taro' export default Picker diff --git a/src/packages/picker/picker.taro.tsx b/src/packages/picker/picker.taro.tsx index 0946696e95..14283dbfe7 100644 --- a/src/packages/picker/picker.taro.tsx +++ b/src/packages/picker/picker.taro.tsx @@ -12,9 +12,10 @@ import Popup from '@/packages/popup/index.taro' import PickerPanel from './pickerpanel.taro' import useRefs from '@/hooks/use-refs' import { useConfig } from '@/packages/configprovider/index.taro' -import { PickerOption, PickerProps } from './types' +import { PickerOption } from './types' import { usePropsValue } from '@/hooks/use-props-value' import { ComponentDefaults } from '@/utils/typings' +import { PickerProps } from './types.taro' export type PickerActions = { open: () => void diff --git a/src/packages/picker/types.taro.ts b/src/packages/picker/types.taro.ts new file mode 100644 index 0000000000..e2b2ea9777 --- /dev/null +++ b/src/packages/picker/types.taro.ts @@ -0,0 +1,8 @@ +import { PopupProps } from '@/packages/popup/types.taro' +import { PickerProps as PickerWebProps } from './types' + +export type PickerProps = Omit & { + popupProps: Partial< + Omit + > +} From 6e041002841f969dc3a18f050ef58f5c87853e3d Mon Sep 17 00:00:00 2001 From: songchenglin3 <353833373@qq.com> Date: Tue, 25 Feb 2025 16:03:33 +0800 Subject: [PATCH 10/10] =?UTF-8?q?fix:=20=E5=BC=B9=E7=AA=97=E5=8A=A8?= =?UTF-8?q?=E6=95=88=E8=BF=98=E5=8E=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/packages/datepicker/datepicker.taro.tsx | 19 ++++++++++--------- src/packages/datepicker/datepicker.tsx | 19 ++++++++++--------- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/packages/datepicker/datepicker.taro.tsx b/src/packages/datepicker/datepicker.taro.tsx index 837e904f4a..afdcf64d92 100644 --- a/src/packages/datepicker/datepicker.taro.tsx +++ b/src/packages/datepicker/datepicker.taro.tsx @@ -166,14 +166,15 @@ const InternalPicker: ForwardRefRenderFunction< selectedValue: (string | number)[], index: number ) => { - handlePickerValueChange( - selectedOptions, - selectedValue, - index, - type, - defaultValue || startDate || endDate, - handleDateComparison - ) + innerVisible && + handlePickerValueChange( + selectedOptions, + selectedValue, + index, + type, + defaultValue || startDate || endDate, + handleDateComparison + ) } const generatePickerColumns = (): PickerOption[][] => { @@ -225,7 +226,7 @@ const InternalPicker: ForwardRefRenderFunction< <> {typeof children === 'function' && children(selectedDate)} - {pickerOptions.length && innerVisible && ( + {pickerOptions.length && ( { - handlePickerValueChange( - selectedOptions, - selectedValue, - index, - type, - defaultValue || startDate || endDate, - handleDateComparison - ) + innerVisible && + handlePickerValueChange( + selectedOptions, + selectedValue, + index, + type, + defaultValue || startDate || endDate, + handleDateComparison + ) } const generatePickerColumns = (): PickerOption[][] => { @@ -223,7 +224,7 @@ const InternalPicker: ForwardRefRenderFunction< <> {typeof children === 'function' && children(selectedDate)}
- {pickerOptions.length && innerVisible && ( + {pickerOptions.length && (