Skip to content

Commit

Permalink
Merge branch 'dev.admin/date'
Browse files Browse the repository at this point in the history
添加了对 min max 属性的支持
  • Loading branch information
caixw committed Jan 22, 2025
2 parents 9beeed0 + 4c33377 commit b3c85f6
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 51 deletions.
8 changes: 6 additions & 2 deletions admin/src/components/form/date/demo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,16 @@ 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<Week>(0);
const [disabledS, disabled] = boolSelector('disabled');
const [readonlyS, readonly] = boolSelector('readonly');
const [roundedS, rounded] = boolSelector('rounded');
const [weekendS, weekend] = boolSelector('weekend');
const [timeS, time] = boolSelector('time');
const [minmaxS, minmax] = boolSelector('minmax');

return <Demo settings={
<>
Expand All @@ -28,10 +31,11 @@ export default function() {
{readonlyS}
{weekendS}
{roundedS}
{minmaxS}
<input type="number" min="0" max="6" class="w-40" placeholder='每周起始于' value={week as any} onChange={(e) => setWeek(parseInt(e.target.value) as Week)} />
</>
}>
<DatePanel weekend={weekend()} palette={palette()} readonly={readonly()} disabled={disabled()} accessor={ac} weekBase={week()} time={time()} />
<DatePicker weekend={weekend()} palette={palette()} tabindex={0} rounded={rounded()} readonly={readonly()} disabled={disabled()} accessor={ac} weekBase={week()} time={time()} />
<DatePanel min={minmax() ? min : undefined} max={minmax() ? max : undefined} weekend={weekend()} palette={palette()} readonly={readonly()} disabled={disabled()} accessor={ac} weekBase={week()} time={time()} />
<DatePicker min={minmax() ? min : undefined} max={minmax() ? max : undefined} weekend={weekend()} palette={palette()} tabindex={0} rounded={rounded()} readonly={readonly()} disabled={disabled()} accessor={ac} weekBase={week()} time={time()} />
</Demo>;
}
19 changes: 13 additions & 6 deletions admin/src/components/form/date/panel.tsx
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -18,7 +17,15 @@ export interface Props extends FieldBaseProps {
*/
time?: boolean;

// TODO min, max
/**
* 允许的最小日期
*/
min?: Date;

/**
* 允许的最大日期
*/
max?: Date;

/**
* 是否高亮周末的列
Expand Down Expand Up @@ -137,7 +144,7 @@ export function DatePanel(props: Props): JSX.Element {
</thead>

<tbody>
<For each={weekDays(p.dt, props.weekBase!)}>
<For each={weekDays(p.dt, props.weekBase!, props.min, props.max)}>
{(week) => (
<tr>
<For each={week}>
Expand Down
8 changes: 6 additions & 2 deletions admin/src/components/form/date/picker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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;
Expand Down
48 changes: 26 additions & 22 deletions admin/src/components/form/date/utils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<Array<Array<[boolean, number, number]>>>([
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<Array<Array<[boolean, number, number]>>>([
[[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]]
Expand Down
71 changes: 52 additions & 19 deletions admin/src/components/form/date/utils.ts
Original file line number Diff line number Diff line change
@@ -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];
Expand All @@ -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<MonthDays> {
// 当前月的第一天和最后一天
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; // 需要拿到前一个月需要添加的天数
Expand All @@ -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<Array<[boolean, Month, number]>> {
export function getWeekDays(m: Array<MonthDays>, min?: Date, max?: Date): Array<Array<[boolean, Month, number]>> {
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<Array<[boolean, Month, number]>> = [];
for (let i = 0; i < days.length; i += 7) {
weeks.push(days.slice(i, i + 7));
Expand All @@ -103,8 +136,8 @@ export function getWeekDays(m: Array<[boolean, Month, number, number]>): Array<A
* - 1 月份;
* - 2 在当前月份中的日期;
*/
export function weekDays(date: Date, weekStart: Week): Array<Array<[boolean, Month, number]>> {
return getWeekDays(monthDays(date, weekStart));
export function weekDays(date: Date, weekStart: Week, min?: Date, max?: Date): Array<Array<[boolean, Month, number]>> {
return getWeekDays(monthDays(date, weekStart), min, max);
}

export const hoursOptions: Options<number> = [
Expand Down

0 comments on commit b3c85f6

Please sign in to comment.