diff --git a/admin/src/components/form/date/demo.tsx b/admin/src/components/form/date/demo.tsx index 1d02f91..8ec926d 100644 --- a/admin/src/components/form/date/demo.tsx +++ b/admin/src/components/form/date/demo.tsx @@ -12,6 +12,8 @@ import { Week } from './utils'; export default function() { const ac = FieldAccessor('dp', '2024-01-02T15:34', true); + const min = new Date('2023-12-02T15:34'); + const max = new Date('2025-12-02T15:34'); const [paletteS, palette] = paletteSelector('primary'); const [week, setWeek] = createSignal(0); const [disabledS, disabled] = boolSelector('disabled'); @@ -19,6 +21,7 @@ export default function() { const [roundedS, rounded] = boolSelector('rounded'); const [weekendS, weekend] = boolSelector('weekend'); const [timeS, time] = boolSelector('time'); + const [minmaxS, minmax] = boolSelector('minmax'); return @@ -28,10 +31,11 @@ export default function() { {readonlyS} {weekendS} {roundedS} + {minmaxS} setWeek(parseInt(e.target.value) as Week)} /> }> - - + + ; } diff --git a/admin/src/components/form/date/panel.tsx b/admin/src/components/form/date/panel.tsx index a4de09b..8fd18a6 100644 --- a/admin/src/components/form/date/panel.tsx +++ b/admin/src/components/form/date/panel.tsx @@ -1,15 +1,14 @@ -// SPDX-FileCopyrightText: 2024 caixw +// SPDX-FileCopyrightText: 2024-2025 caixw // // SPDX-License-Identifier: MIT -import { createMemo, For, mergeProps, Show } from 'solid-js'; +import { createMemo, For, JSX, mergeProps, Show } from 'solid-js'; -import { useApp } from '@/components/context'; import { Button } from '@/components/button'; +import { useApp } from '@/components/context'; import { FieldBaseProps } from '@/components/form'; import { Accessor, FieldAccessor } from '@/components/form/access'; import { Choice } from '@/components/form/choice'; -import { JSX } from 'solid-js'; import { hoursOptions, minutesOptions, Week, weekDay, weekDays, weeks } from './utils'; export interface Props extends FieldBaseProps { @@ -18,7 +17,15 @@ export interface Props extends FieldBaseProps { */ time?: boolean; - // TODO min, max + /** + * 允许的最小日期 + */ + min?: Date; + + /** + * 允许的最大日期 + */ + max?: Date; /** * 是否高亮周末的列 @@ -137,7 +144,7 @@ export function DatePanel(props: Props): JSX.Element { - + {(week) => ( diff --git a/admin/src/components/form/date/picker.tsx b/admin/src/components/form/date/picker.tsx index db590d2..74ea2bb 100644 --- a/admin/src/components/form/date/picker.tsx +++ b/admin/src/components/form/date/picker.tsx @@ -14,7 +14,11 @@ export interface Props extends PanelProps { rounded?: boolean; - // TODO min, max, range + min?: Date; + + max?: Date; + + // TODO range } function togglePop(anchor: Element, pop: HTMLElement): boolean { @@ -33,7 +37,7 @@ export function DatePicker(props: Props): JSX.Element { const ctx = useApp(); props = mergeProps(presetProps, props); - const [panelProps, _] = splitProps(props, ['time', 'weekBase', 'accessor', 'weekend', 'disabled', 'readonly', 'palette', 'class', 'classList']); + const [panelProps, _] = splitProps(props, ['time', 'weekBase', 'accessor', 'weekend', 'disabled', 'readonly', 'palette', 'class', 'classList', 'min', 'max']); const ac = props.accessor; let panelRef: HTMLElement; diff --git a/admin/src/components/form/date/utils.spec.ts b/admin/src/components/form/date/utils.spec.ts index f90252a..d52ecb4 100644 --- a/admin/src/components/form/date/utils.spec.ts +++ b/admin/src/components/form/date/utils.spec.ts @@ -42,50 +42,54 @@ test('sub weekDay', () => { test('monthDays', () => { expect(monthDays(new Date(2024, 6, 1), 0)).toEqual([ - [false, 5, 30, 30], - [true, 6, 1, 31], - [false, 7, 1, 3] + { isCurrent: false, month: 5, start: 30, end: 30, year: 2024 }, + { isCurrent: true, month: 6, start: 1, end: 31, year: 2024 }, + { isCurrent: false, month: 7, start: 1, end: 3, year: 2024 } ]); expect(monthDays(new Date(2024, 6, 1), 1)).toEqual([ - [false, 0, 0, 0], - [true, 6, 1, 31], - [false, 7, 1, 4] + { isCurrent: false, month: 0, start: 0, end: 0, year: 0 }, + { isCurrent: true, month: 6, start: 1, end: 31, year: 2024 }, + { isCurrent: false, month: 7, start: 1, end: 4, year: 2024 } ]); expect(monthDays(new Date(2024, 6, 1), 2)).toEqual([ - [false, 5, 25, 30], - [true, 6, 1, 31], - [false, 7, 1, 5] + { isCurrent: false, month: 5, start: 25, end: 30, year: 2024 }, + { isCurrent: true, month: 6, start: 1, end: 31, year: 2024 }, + { isCurrent: false, month: 7, start: 1, end: 5, year: 2024 } ]); expect(monthDays(new Date(2024, 6, 1), 3)).toEqual([ - [false, 5, 26, 30], - [true, 6, 1, 31], - [false, 7, 1, 6] + { isCurrent: false, month: 5, start: 26, end: 30, year: 2024 }, + { isCurrent: true, month: 6, start: 1, end: 31, year: 2024 }, + { isCurrent: false, month: 7, start: 1, end: 6, year: 2024 } ]); expect(monthDays(new Date(2024, 6, 1), 4)).toEqual([ - [false, 5, 27, 30], - [true, 6, 1, 31], - [false, 0, 0, 0] + { isCurrent: false, month: 5, start: 27, end: 30, year: 2024 }, + { isCurrent: true, month: 6, start: 1, end: 31, year: 2024 }, + { isCurrent: false, month: 0, start: 0, end: 0, year: 0 } ]); expect(monthDays(new Date(2024, 6, 1), 5)).toEqual([ - [false, 5, 28, 30], - [true, 6, 1, 31], - [false, 7, 1, 1] + { isCurrent: false, month: 5, start: 28, end: 30, year: 2024 }, + { isCurrent: true, month: 6, start: 1, end: 31, year: 2024 }, + { isCurrent: false, month: 7, start: 1, end: 1, year: 2024 } ]); expect(monthDays(new Date(2024, 6, 1), 6)).toEqual([ - [false, 5, 29, 30], - [true, 6, 1, 31], - [false, 7, 1, 2] + { isCurrent:false, month:5, start:29, end:30, year: 2024 }, + { isCurrent:true, month:6, start:1, end:31, year: 2024 }, + { isCurrent:false, month:7, start:1, end:2, year: 2024 } ]); }); test('getWeekDays', () => { - expect(getWeekDays([[false, 5, 29, 30], [true, 6, 1, 17], [false, 7, 1, 2]])).toEqual>>([ + expect(getWeekDays([ + { isCurrent: false, month: 5, start: 29, end: 30, year: 2024 }, + { isCurrent: true, month: 6, start: 1, end: 17, year: 2024 }, + { isCurrent: false, month: 7, start: 1, end: 2, year: 2024 }]) + ).toEqual>>([ [[false, 5, 29], [false, 5, 30], [true, 6, 1], [true, 6, 2], [true, 6, 3], [true, 6, 4], [true, 6, 5]], [[true, 6, 6], [true, 6, 7], [true, 6, 8], [true, 6, 9], [true, 6, 10], [true, 6, 11], [true, 6, 12]], [[true, 6, 13], [true, 6, 14], [true, 6, 15], [true, 6, 16], [true, 6, 17], [false, 7, 1], [false, 7, 2]] diff --git a/admin/src/components/form/date/utils.ts b/admin/src/components/form/date/utils.ts index 6c83cf2..3251eb3 100644 --- a/admin/src/components/form/date/utils.ts +++ b/admin/src/components/form/date/utils.ts @@ -1,9 +1,12 @@ -// SPDX-FileCopyrightText: 2024 caixw +// SPDX-FileCopyrightText: 2024-2025 caixw // // SPDX-License-Identifier: MIT import { Options } from '@/components/form/types'; +/** + * 月份,0 表示一月。 + */ export const months = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] as const; export type Month = typeof months[number]; @@ -30,24 +33,31 @@ export function weekDay(base: Week, delta?: number): Week { return v >=weeks.length ? (v - weeks.length) as Week : v as Week; } +/** + * {@link monthDays} 的返回值类型 + */ +interface MonthDays { + isCurrent: boolean;// 表示是否为当前月份 + year: number; // 年份 + month: Month; // 月份 + start: number; // 该月在当前面板上的起始日期 + end: number; // 该月在当前面板上的结束日期 +} + /** * 计算指定月份的天数范围 * * @param weekStart 每周的起始; * @param date 需要计算的月份; - * @returns 返回的元组列表,每个元组包含以下四个字段: - * - 0 boolean 表示是否为当前月份; - * - 1 Month 月份; - * - 2 该月在当前面板上的起始日期; - * - 3 该月在当前面板上的结束日期; + * @returns 返回的 {@link MonthDays} 列表; */ -export function monthDays(date: Date, weekStart: Week): Array<[boolean, Month, number, number]> { +export function monthDays(date: Date, weekStart: Week): Array { // 当前月的第一天和最后一天 const firstDay = new Date(date.getFullYear(), date.getMonth(), 1); const lastDay = new Date(date.getFullYear(), date.getMonth()+1, 0); // 处理前一个月份的数据 - let prev: [boolean, Month, number, number] = [false, 0, 0, 0]; + const prev: MonthDays = { isCurrent: false, month: 0, start: 0, end: 0, year: 0 }; const firstWeekDay = firstDay.getDay() as Week; if (weekStart !== firstWeekDay) { let days = firstWeekDay - weekStart-1; // 需要拿到前一个月需要添加的天数 @@ -56,36 +66,59 @@ export function monthDays(date: Date, weekStart: Week): Array<[boolean, Month, n } const lastDay = new Date(date.getFullYear(), date.getMonth(), 0); const last = lastDay.getDate(); - prev = [false, lastDay.getMonth() as Month, last - days, last]; + prev.month = lastDay.getMonth() as Month; + prev.start = last - days; + prev.end = last; + prev.year = date.getFullYear(); } // 处理后一个月份的数据 - let next: [boolean, Month, number, number] = [false, 0, 0, 0]; + const next: MonthDays = { isCurrent: false, month: 0, start: 0, end: 0, year: 0 }; const lastWeekDay = lastDay.getDay() as Week; if (weekDay(weekStart,-1) !== lastWeekDay) { let days = weekStart - 1 - lastWeekDay; if (days <= 0) { days = weeks.length + days; } - next = [false, (new Date(date.getFullYear(), date.getMonth()+1,1)).getMonth() as Month, 1, days]; + next.month = (new Date(date.getFullYear(), date.getMonth() + 1, 1)).getMonth() as Month; + next.start = 1; + next.end = days; + next.year = date.getFullYear(); } - return [prev, [true,lastDay.getMonth() as Month, 1, lastDay.getDate()], next]; + const curr: MonthDays = { + isCurrent: true, + month: lastDay.getMonth() as Month, + start: 1, + end: lastDay.getDate(), + year: lastDay.getFullYear(), + }; + return [prev, curr, next]; } /** * 将由 {@link monthDays} 的结果转换为以 7 天为一组的天数据 */ -export function getWeekDays(m: Array<[boolean, Month, number, number]>): Array> { +export function getWeekDays(m: Array, min?: Date, max?: Date): Array> { const days: Array<[boolean, Month, number]> = []; for (const mm of m) { - if (mm[2] === 0 && mm[2] === mm[3]) { continue; } - - for (let i = mm[2]; i <= mm[3]; i++) { - days.push([mm[0], mm[1], i]); + if (mm.start === 0 && mm.start === mm.end) { continue; } + + for (let i = mm.start; i <= mm.end; i++) { + let enabled = mm.isCurrent; + const now = new Date(mm.year, mm.month, i); + if (min && min > now) { + enabled = false + } + if (max && max < now) { + enabled = false + } + + days.push([enabled, mm.month, i]); } } + // 将天以 7 为单位进行分割并存入 weeks。 const weeks: Array> = []; for (let i = 0; i < days.length; i += 7) { weeks.push(days.slice(i, i + 7)); @@ -103,8 +136,8 @@ export function getWeekDays(m: Array<[boolean, Month, number, number]>): Array> { - return getWeekDays(monthDays(date, weekStart)); +export function weekDays(date: Date, weekStart: Week, min?: Date, max?: Date): Array> { + return getWeekDays(monthDays(date, weekStart), min, max); } export const hoursOptions: Options = [