diff --git a/.changeset/shaggy-rules-own.md b/.changeset/shaggy-rules-own.md new file mode 100644 index 000000000..d7949d32b --- /dev/null +++ b/.changeset/shaggy-rules-own.md @@ -0,0 +1,5 @@ +--- +"@hi-ui/locale-context": patch +--- + +chore: 补充日期语言包 diff --git a/.changeset/small-mangos-cry.md b/.changeset/small-mangos-cry.md new file mode 100644 index 000000000..a698ddb6c --- /dev/null +++ b/.changeset/small-mangos-cry.md @@ -0,0 +1,7 @@ +--- +"@hi-ui/hiui": patch +--- + +feat(date-picker): 支持季度选择 +feat(date-picker): add onPanelChange api +chore(locale-context): 补充日期语言包 diff --git a/.changeset/sour-apples-look.md b/.changeset/sour-apples-look.md new file mode 100644 index 000000000..4f39f5a81 --- /dev/null +++ b/.changeset/sour-apples-look.md @@ -0,0 +1,6 @@ +--- +"@hi-ui/date-picker": minor +--- + +feat: 支持季度选择 +feat: add onPanelChange api diff --git a/packages/ui/date-picker/src/DatePicker.tsx b/packages/ui/date-picker/src/DatePicker.tsx index 45a651365..054f10120 100644 --- a/packages/ui/date-picker/src/DatePicker.tsx +++ b/packages/ui/date-picker/src/DatePicker.tsx @@ -64,6 +64,7 @@ export const DatePicker = forwardRef( maxDate: max = null, minDate: min = null, onSelect: propsOnSelectOriginal, + onPanelChange, theme, disabledHours = DEFAULT_DISABLED_FUNCTION, disabledMinutes = DEFAULT_DISABLED_FUNCTION, @@ -229,8 +230,8 @@ export const DatePicker = forwardRef( end: _dates[1]?.toDate(), } returnDateStr = { - start: _dates[0]?.format(realFormat), - end: _dates[1]?.format(realFormat), + start: _dates[0]?.format(realFormat).toLocaleUpperCase(), + end: _dates[1]?.format(realFormat).toLocaleUpperCase(), } compareNumber = 2 } else if (type.includes('week')) { @@ -259,7 +260,7 @@ export const DatePicker = forwardRef( : getWeekString(_dates[0]) } else { returnDate = _dates[0]?.toDate() - returnDateStr = _dates[0]?.format(realFormat) + returnDateStr = _dates[0]?.format(realFormat).toLocaleUpperCase() } // 只有发生了改变,才会去通知外部 @@ -416,6 +417,7 @@ export const DatePicker = forwardRef( value, disabledDate, onSelect, + onPanelChange, prefixCls, showPanel, isInDateRangeTimeMode, diff --git a/packages/ui/date-picker/src/components/calendar.tsx b/packages/ui/date-picker/src/components/calendar.tsx index 729afca3c..28796d89d 100644 --- a/packages/ui/date-picker/src/components/calendar.tsx +++ b/packages/ui/date-picker/src/components/calendar.tsx @@ -135,6 +135,10 @@ const Calendar = ({ _date.add(1, 'months') } + if (type.includes('quarter')) { + _date.quarter(clickVal) + } + _date[view](clickVal) } else { // 点击的上个月的部分,鼠标还在本月的panel上,则视作,鼠标正在本月第一天 @@ -213,7 +217,7 @@ const Calendar = ({ }, 2000) as any } const renderAltCalendar = (cell: CalendarColInfo, isBelongFullOutOfRange: string) => { - if (view.includes('year') || view.includes('month')) return + if (view.includes('year') || view.includes('month') || view.includes('quarter')) return if ( Object.keys(altCalendarPresetData).length > 0 || Object.keys(dateMarkPresetData).length > 0 || diff --git a/packages/ui/date-picker/src/components/header.tsx b/packages/ui/date-picker/src/components/header.tsx index de2514ab7..9583a3307 100644 --- a/packages/ui/date-picker/src/components/header.tsx +++ b/packages/ui/date-picker/src/components/header.tsx @@ -20,7 +20,7 @@ const getHeaderCenterContent = ( if (view === 'year') { return year - 4 + '~' + (year + 7) } - if (view === 'month') { + if (view === 'month' || view === 'quarter') { return year } @@ -71,7 +71,7 @@ const Header = ({ > - {view !== 'year' && view !== 'month' && ( + {view !== 'year' && view !== 'month' && view !== 'quarter' && ( headerChangeDateEvent('months', -1)} @@ -91,7 +91,7 @@ const Header = ({ {
- {view !== 'year' && view !== 'month' && ( + {view !== 'year' && view !== 'month' && view !== 'quarter' && ( headerChangeDateEvent('months', 1)} className={`${prefixCls}__header-icon`} diff --git a/packages/ui/date-picker/src/components/input.tsx b/packages/ui/date-picker/src/components/input.tsx index 35418ab35..d6b5dc635 100644 --- a/packages/ui/date-picker/src/components/input.tsx +++ b/packages/ui/date-picker/src/components/input.tsx @@ -64,6 +64,10 @@ const Input = ({ } } + if (type.includes('quarter') && vals) { + vals = vals?.toLocaleUpperCase() + } + setValue(vals) cacheValues.current = vals diff --git a/packages/ui/date-picker/src/components/panel.tsx b/packages/ui/date-picker/src/components/panel.tsx index 75e075966..6382ad146 100644 --- a/packages/ui/date-picker/src/components/panel.tsx +++ b/packages/ui/date-picker/src/components/panel.tsx @@ -30,6 +30,7 @@ const Panel = (props: PanelProps) => { weekOffset, locale, onSelect, + onPanelChange, hourStep, minuteStep, secondStep, @@ -74,6 +75,12 @@ const Panel = (props: PanelProps) => { ) return } + + if (type === 'quarter') { + onPick([date], false) + return + } + if (type === 'week' && view === 'date') { // week picker // 根据偏移判断当前使用的周格式 @@ -87,6 +94,9 @@ const Panel = (props: PanelProps) => { if (view === 'year') { _view = 'month' } + if (view === 'quarter') { + _view = 'quarter' + } if (view === 'month') { _view = 'date' } @@ -135,8 +145,7 @@ const Panel = (props: PanelProps) => { const _innerDates = genNewDates(calRenderDates, date) if (type.includes('range') && _innerDates[0] >= _innerDates[1]) return setCalRenderDates(_innerDates) - // 左右切换年或月时触发 onSelect 回调 - onSelect(date.toDate(), true) + onPanelChange?.(date.toDate()) } return ( diff --git a/packages/ui/date-picker/src/components/range-panel.tsx b/packages/ui/date-picker/src/components/range-panel.tsx index 417217bb8..180b6bfa5 100644 --- a/packages/ui/date-picker/src/components/range-panel.tsx +++ b/packages/ui/date-picker/src/components/range-panel.tsx @@ -27,6 +27,7 @@ const RangePanel = () => { theme, locale, onSelect, + onPanelChange, hourStep, minuteStep, secondStep, @@ -173,6 +174,9 @@ const RangePanel = () => { if (views[uIndex] === 'year' && !type.includes('year')) { _views[uIndex] = 'month' } + if (views[uIndex] === 'quarter' && !type.includes('quarter')) { + _views[uIndex] = 'quarter' + } setViews(_views) } @@ -217,7 +221,7 @@ const RangePanel = () => { return } setCalRenderDates(_innerDates) - onSelect(date as any, !calendarClickIsEnd.current) + onPanelChange?.(date.toDate()) } const onTimePeriodPick = useCallback( (ts1: string, ts2: string) => { diff --git a/packages/ui/date-picker/src/context.ts b/packages/ui/date-picker/src/context.ts index 2248dcc9c..28045689f 100644 --- a/packages/ui/date-picker/src/context.ts +++ b/packages/ui/date-picker/src/context.ts @@ -60,5 +60,6 @@ export interface DPContextData extends ExtendsType { value: DatePickerValueV3 // 内部现在暂时使用 v3 的数据格式 altCalendar?: CalendarItemV3[] + onPanelChange?: (data: Date) => void } export default DPContext diff --git a/packages/ui/date-picker/src/hooks/useCalenderData.tsx b/packages/ui/date-picker/src/hooks/useCalenderData.tsx index a74f12dee..1a66b5fa6 100644 --- a/packages/ui/date-picker/src/hooks/useCalenderData.tsx +++ b/packages/ui/date-picker/src/hooks/useCalenderData.tsx @@ -40,6 +40,63 @@ const getYearOrMonthRows = ({ formatRange.end = temp } + if (type.includes('quarter')) { + const quarterColData: CalendarColInfo[] = [] + for (let i = 0; i < 4; i++) { + const value = i + 1 + const col = { + type: 'normal', + text: `Q${i + 1}`, + range: false, + value, + } as CalendarColInfo + + if (current.year() === renderDate?.year() && originDate?.quarter() === value) { + col.type = 'selected' + } + + if (current.year() === renderDate?.year() && current.quarter() === value) { + col.type = 'today' + } + + if (disabledDate?.(_date.quarter(value).toDate(), 'quarter')) { + col.type = 'disabled' + } + + const currentYM = (_date as any)[view](value) + + if ( + range?.start && + range?.end && + (currentYM.isBetween(formatRange.start, formatRange.end) || + currentYM.isBetween(formatRange.end, formatRange.start)) + ) { + col.range = true + } + if (formatRange?.start && currentYM.isSame(formatRange.start, view)) { + col.type = 'selected' + col.range = false + col.rangeStart = true + // 当前,存在开始,结束不存在,范围选择现在只选择了一个值,视作,开始结束为同一个 + if (!formatRange.end) { + col.rangeEnd = true + } + } + if (formatRange?.end && currentYM.isSame(formatRange.end, view)) { + col.type = 'selected' + col.range = false + col.rangeEnd = true + // 当前,存在结束,开始不存在,范围选择现在只选择了一个值,视作,开始结束为同一个 + if (!formatRange.start) { + col.rangeEnd = true + } + } + + quarterColData.push(col) + } + return [quarterColData] as CalendarRowInfo[] + } + const monthText = i18n.get('datePicker.month') for (let i = 0; i < 4; i++) { @@ -188,6 +245,7 @@ const getYearOrMonthRows = ({ } } } + return trs } const getTime = (week: number, y: number, m: number) => { @@ -422,9 +480,10 @@ const useDate = ({ range?: CalenderSelectedRange }) => { const [rows, setRows] = useState([]) + useEffect(() => { const _rows = - view.includes('month') || view.includes('year') + view.includes('month') || view.includes('year') || view.includes('quarter') ? getYearOrMonthRows({ originDate, renderDate, @@ -446,6 +505,7 @@ const useDate = ({ renderDate, disabledDate, }) + setRows(_rows) }, [renderDate, view, range, type, disabledDate, i18n, max, min, weekOffset, originDate]) diff --git a/packages/ui/date-picker/src/hooks/useFormat.ts b/packages/ui/date-picker/src/hooks/useFormat.ts index 79b2a787a..3d21e415f 100644 --- a/packages/ui/date-picker/src/hooks/useFormat.ts +++ b/packages/ui/date-picker/src/hooks/useFormat.ts @@ -17,7 +17,7 @@ const useFormat = (config: UseFormatConfig) => { const { type, showTime, format, locale = 'zh-CN' } = config return useMemo(() => { - let realFormat = format || getLocaleTypeFormatMap(locale)[type] + let realFormat = format || getLocaleTypeFormatMap(locale)[type] || '' if (showTime && !/[H|h|m|s]/.test(realFormat)) { realFormat += ' HH:mm:ss' } diff --git a/packages/ui/date-picker/src/hooks/useTimePickerFormat.ts b/packages/ui/date-picker/src/hooks/useTimePickerFormat.ts index 205999a4e..8c36c2989 100644 --- a/packages/ui/date-picker/src/hooks/useTimePickerFormat.ts +++ b/packages/ui/date-picker/src/hooks/useTimePickerFormat.ts @@ -2,7 +2,7 @@ import { useMemo } from 'react' import { TimePickerFormat } from '@hi-ui/time-picker' export const useTimePickerFormat = (original: string) => { - const timeFormat = original.split(' ')[1] ?? '' + const timeFormat = original?.split(' ')[1] ?? '' return useMemo(() => { let result = '' diff --git a/packages/ui/date-picker/src/styles/date-picker.scss b/packages/ui/date-picker/src/styles/date-picker.scss index 720a605ff..ce25dac02 100644 --- a/packages/ui/date-picker/src/styles/date-picker.scss +++ b/packages/ui/date-picker/src/styles/date-picker.scss @@ -378,6 +378,14 @@ $calendar-large-background-border-radius: $calendar-large-indicator-border-radiu } } } + + &--quarter { + .#{$prefix}__cell { + &-text { + width: 52px; + } + } + } } &__cell { diff --git a/packages/ui/date-picker/src/types.ts b/packages/ui/date-picker/src/types.ts index 3d6bdd7b4..337b6a18b 100644 --- a/packages/ui/date-picker/src/types.ts +++ b/packages/ui/date-picker/src/types.ts @@ -5,7 +5,7 @@ import { TimePickerPanelType } from '@hi-ui/time-picker' import { CalendarColInfo } from './hooks/useCalenderData' import { Moment } from 'moment' -export type CalendarViewEnum = 'date' | 'year' | 'month' +export type CalendarViewEnum = 'date' | 'year' | 'month' | 'quarter' export interface DateRange { start: Date | string | number | undefined | null @@ -93,10 +93,12 @@ export type DatePickerTypeEnum = | 'year' | 'month' | 'week' + | 'quarter' | 'weekrange' | 'timeperiod' | 'yearrange' | 'monthrange' + | 'quarterrange' export type DatePickerAltCalendarPresetEnum = 'zh-CN' | 'id-ID' @@ -235,6 +237,10 @@ export interface DatePickerProps extends Omit, 'placehold * 选择后的回调,(date: 选中的日期,dateStr: 选中的日期字符串) => void */ onChange?: (date: Date | Date[] | null, dateStr: string | string[] | null) => void + /** + * 日期面板改变时的回调函数 + */ + onPanelChange?: (data: Date) => void /** * 不同 UI 外观 * @default 'line' diff --git a/packages/ui/date-picker/src/utils/constants.ts b/packages/ui/date-picker/src/utils/constants.ts index f666b9da9..eaf04dd9b 100644 --- a/packages/ui/date-picker/src/utils/constants.ts +++ b/packages/ui/date-picker/src/utils/constants.ts @@ -15,6 +15,8 @@ export const GranularityMap: { weekrange: 'date', month: 'month', monthrange: 'month', + quarter: 'quarter', + quarterrange: 'quarter', timeperiod: 'second', } /** @@ -26,6 +28,7 @@ export const getLocaleTypeFormatMap = (locale: string) => { ? { date: 'YYYY-MM-DD', month: 'YYYY-MM', + quarter: 'YYYY-qQ', year: 'YYYY', time: 'HH:mm:ss', timerange: 'HH:mm:ss', @@ -34,6 +37,7 @@ export const getLocaleTypeFormatMap = (locale: string) => { weekrange: 'YYYY-WW', timeperiod: 'YYYY-MM-DD HH:mm:ss', monthrange: 'YYYY-MM', + quarterrange: 'YYYY-qQ', yearrange: 'YYYY', } : { @@ -47,6 +51,7 @@ export const getLocaleTypeFormatMap = (locale: string) => { weekrange: 'wo/YYYY', timeperiod: 'MM/DD/YYYY HH:mm:ss', monthrange: 'MM/YYYY', + quarterrange: 'qQ/YYYY', yearrange: 'YYYY', } } @@ -54,6 +59,7 @@ export const getLocaleTypeFormatMap = (locale: string) => { export const INPUTTYPES = { date: 'normal', month: 'normal', + quarter: 'normal', year: 'normal', time: 'normal', timerange: 'range', @@ -62,5 +68,6 @@ export const INPUTTYPES = { weekrange: 'range', timeperiod: 'range', monthrange: 'range', + quarterrange: 'range', yearrange: 'range', } diff --git a/packages/ui/date-picker/src/utils/index.tsx b/packages/ui/date-picker/src/utils/index.tsx index 6f498e798..e6d7f1fca 100644 --- a/packages/ui/date-picker/src/utils/index.tsx +++ b/packages/ui/date-picker/src/utils/index.tsx @@ -45,7 +45,7 @@ export const deconstructDate = (original: Date | string | number | null) => { * @param {String} type */ export const getView = (type: DatePickerTypeEnum) => { - return (type.includes('year') || type.includes('month') + return (type.includes('year') || type.includes('month') || type.includes('quarter') ? type.split('range')[0] : 'date') as CalendarViewEnum } @@ -69,6 +69,7 @@ export const genNewDates = (currentDates: moment.Moment[], newDate: moment.Momen */ export const parseRenderDates = (dates: (moment.Moment | null)[], type: DatePickerTypeEnum) => { let [leftDate, rightDate] = cloneDeep(dates) + const getRightDate = () => { if (type === 'yearrange') { if (!rightDate) { @@ -89,7 +90,7 @@ export const parseRenderDates = (dates: (moment.Moment | null)[], type: DatePick return moment(leftDate).add(12 * crossingPanelNumber, 'year') } - if (type === 'monthrange') { + if (type === 'monthrange' || type === 'quarterrange') { return moment(leftDate).add(1, 'year') } if (!rightDate || leftDate?.isSame(rightDate, 'month')) { diff --git a/packages/ui/date-picker/stories/range.stories.tsx b/packages/ui/date-picker/stories/range.stories.tsx index baf8f1ba5..460187822 100644 --- a/packages/ui/date-picker/stories/range.stories.tsx +++ b/packages/ui/date-picker/stories/range.stories.tsx @@ -4,7 +4,7 @@ import DayJs from 'dayjs' /** * @title 范围 - * @desc 以天、周、月、年等粒度展示一个时间的范围 + * @desc 以天、周、月、季度、年等粒度展示一个时间的范围 */ export const Range = () => { const [dynamicSelectedValue, setDynamicSelectedValue] = useState('') @@ -47,6 +47,16 @@ export const Range = () => { }} /> +

季度

+ { + console.log('onChange', date, dateStr) + }} + /> +

月份

{ return ( <> -

年份 / 月份 / 周

+

年份 / 季度 / 月份 / 周

年份

{ console.log('onChange', date, dateStr) }} /> + +

季度

+ { + console.log('onChange', date, dateStr) + }} + /> +

月份