From 3cc9a25026f545e4ea90422593ec94a21f706f4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A0=91=E6=9E=97?= <3335405397@qq.com> Date: Sat, 29 Nov 2025 19:12:08 +0800 Subject: [PATCH 1/6] =?UTF-8?q?feat(calendar):=20calendar=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E5=85=AC=E7=94=A8=E9=80=BB=E8=BE=91=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E6=9C=80=E5=B0=8F=E5=B9=B4=E4=BB=BD=E5=B8=B8=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- js/calendar/consts.ts | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 js/calendar/consts.ts diff --git a/js/calendar/consts.ts b/js/calendar/consts.ts new file mode 100644 index 0000000000..299f4fd4d9 --- /dev/null +++ b/js/calendar/consts.ts @@ -0,0 +1,6 @@ +// 最小年份 +export const MIN_YEAR = 1970; + +export default { + MIN_YEAR, +}; From c9d98aa57abf55d258dc30f0944055248f9a9a2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A0=91=E6=9E=97?= <3335405397@qq.com> Date: Sat, 29 Nov 2025 19:12:48 +0800 Subject: [PATCH 2/6] =?UTF-8?q?feat(calendar):=20calendar=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E5=85=AC=E7=94=A8=E9=80=BB=E8=BE=91=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E6=97=A5=E5=8E=86=E6=98=BE=E7=A4=BA=E8=8C=83=E5=9B=B4=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- js/calendar/types.ts | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 js/calendar/types.ts diff --git a/js/calendar/types.ts b/js/calendar/types.ts new file mode 100644 index 0000000000..68653a8c26 --- /dev/null +++ b/js/calendar/types.ts @@ -0,0 +1,9 @@ +export type CalendarValue = string | Date; + +/** + * 日历的显示范围 + */ +export interface CalendarRange { + from: CalendarValue; + to: CalendarValue; +} From 373ad06bc76f29d91ba32bc14125f294a8509b79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A0=91=E6=9E=97?= <3335405397@qq.com> Date: Sat, 29 Nov 2025 19:13:27 +0800 Subject: [PATCH 3/6] =?UTF-8?q?feat(calendar):=20calendar=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E5=85=AC=E7=94=A8=E9=80=BB=E8=BE=91=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E6=97=A5=E5=8E=86=E8=8C=83=E5=9B=B4=E7=9B=B8=E5=85=B3=E5=B7=A5?= =?UTF-8?q?=E5=85=B7=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- js/calendar/utils.ts | 73 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 js/calendar/utils.ts diff --git a/js/calendar/utils.ts b/js/calendar/utils.ts new file mode 100644 index 0000000000..7ad9500bfa --- /dev/null +++ b/js/calendar/utils.ts @@ -0,0 +1,73 @@ +import dayjs from 'dayjs'; + +import type { CalendarRange, CalendarValue } from './types'; +import { MIN_YEAR } from './consts'; + +/** + * 根据当前时间创建一个默认日期 + * @returns 当前日期的dayjs对象 + */ +export const createDefaultCurDate = (): dayjs.Dayjs => dayjs(dayjs().format('YYYY-MM-DD')); + +/** + * 处理`range`参数输入值并生成日历范围 + * @param range 用于设置日历的年月份显示范围,[范围开始,范围结束] + * @returns 处理完成的日历范围 + */ +export const rangeFromTo = ( + range?: Array +): { from: CalendarValue; to: CalendarValue } => { + // 检查范围边界 + const parseRangeBoundary = ( + value: CalendarRange['from'] | CalendarRange['to'] | null | undefined + ) => { + if (value === undefined || value === null) { + return null; + } + const parsed = dayjs(value); + if (!parsed.isValid()) { + return null; + } + return { + parsed, // dayjs 对象 + original: value as CalendarRange['from'] | CalendarRange['to'], // 原始值 + }; + }; + + if (!range || range.length < 2) { + return null; + } + const [v1, v2] = range; + const start = parseRangeBoundary(v1); + const end = parseRangeBoundary(v2); + + if (!start && !end) { + return null; + } + + // 未指定边界上/下限时使用默认值 + const fallback = ( + edge: 'from' | 'to' + ): { parsed: dayjs.Dayjs; original: string } => { + let fallbackParsed = dayjs(MIN_YEAR); + if (edge === 'to') { + fallbackParsed = createDefaultCurDate(); + } + return { + parsed: fallbackParsed, + original: fallbackParsed.format('YYYY-MM-DD'), + }; + }; + + let fromBoundary = start ?? fallback('from'); + let toBoundary = end ?? fallback('to'); + + if (fromBoundary.parsed.isAfter(toBoundary.parsed)) { + [fromBoundary, toBoundary] = [toBoundary, fromBoundary]; // 当前一项日期大于后一项时交换两值以确保边界逻辑正确 + } + + return { + from: fromBoundary.original, + to: toBoundary.original, + }; +}; From e698acb43384b6ea042b76b348af32dced00a7f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A0=91=E6=9E=97?= <3335405397@qq.com> Date: Sat, 29 Nov 2025 20:41:02 +0800 Subject: [PATCH 4/6] =?UTF-8?q?fix(calendar):=20=E6=9B=B4=E5=90=8DrangeFro?= =?UTF-8?q?mTo=E5=87=BD=E6=95=B0=E4=B8=BAhandleRange=E4=BB=A5=E8=A7=A3?= =?UTF-8?q?=E5=86=B3=E4=B8=8E=E4=B8=BB=E4=BB=93=E5=BA=93=E5=87=BD=E6=95=B0?= =?UTF-8?q?=E9=87=8D=E5=90=8D=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- js/calendar/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/calendar/utils.ts b/js/calendar/utils.ts index 7ad9500bfa..14d2dc8a60 100644 --- a/js/calendar/utils.ts +++ b/js/calendar/utils.ts @@ -14,7 +14,7 @@ export const createDefaultCurDate = (): dayjs.Dayjs => dayjs(dayjs().format('YYY * @param range 用于设置日历的年月份显示范围,[范围开始,范围结束] * @returns 处理完成的日历范围 */ -export const rangeFromTo = ( +export const handleRange = ( range?: Array ): { from: CalendarValue; to: CalendarValue } => { // 检查范围边界 From 9ee523cfa2a23ee8519436cef3b332554b7bcc00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A0=91=E6=9E=97?= <3335405397@qq.com> Date: Sat, 29 Nov 2025 23:53:42 +0800 Subject: [PATCH 5/6] =?UTF-8?q?fix(calendar):=20handleRange=E5=87=BD?= =?UTF-8?q?=E6=95=B0=E8=BF=94=E5=9B=9E=E7=B1=BB=E5=9E=8B=E6=BC=8F=E4=BA=86?= =?UTF-8?q?null?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- js/calendar/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/calendar/utils.ts b/js/calendar/utils.ts index 14d2dc8a60..c81aa21cbc 100644 --- a/js/calendar/utils.ts +++ b/js/calendar/utils.ts @@ -16,7 +16,7 @@ export const createDefaultCurDate = (): dayjs.Dayjs => dayjs(dayjs().format('YYY */ export const handleRange = ( range?: Array -): { from: CalendarValue; to: CalendarValue } => { +): { from: CalendarValue; to: CalendarValue } | null => { // 检查范围边界 const parseRangeBoundary = ( value: CalendarRange['from'] | CalendarRange['to'] | null | undefined From f36a812984ffb19fc930e42a6f54cb31f3e8a1b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A0=91=E6=9E=97?= <3335405397@qq.com> Date: Sun, 30 Nov 2025 00:30:36 +0800 Subject: [PATCH 6/6] =?UTF-8?q?test(calendar):=20=E6=96=B0=E5=A2=9EcreateD?= =?UTF-8?q?efaultCurDate=E5=92=8ChandleRange=E7=9A=84=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E6=A1=88=E4=BE=8B=E5=B9=B6=E6=8F=90=E5=8D=87=E8=A6=86=E7=9B=96?= =?UTF-8?q?=E7=8E=87=E8=87=B3100%?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/unit/calendar/utils.test.js | 103 +++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 test/unit/calendar/utils.test.js diff --git a/test/unit/calendar/utils.test.js b/test/unit/calendar/utils.test.js new file mode 100644 index 0000000000..dcfc0d265b --- /dev/null +++ b/test/unit/calendar/utils.test.js @@ -0,0 +1,103 @@ +import { describe, it, expect } from "vitest"; +import dayjs from "dayjs"; +import { createDefaultCurDate, handleRange } from "../../../js/calendar/utils"; + +describe("utils", () => { + describe("createDefaultCurDate", () => { + it("返回当前日期的dayjs对象且日期格式为YYYY-MM-DD", () => { + const result = createDefaultCurDate(); + expect(result).toBeDefined(); + expect(result.isValid()).toBe(true); + const formatted = result.format("YYYY-MM-DD"); + const expected = dayjs().format("YYYY-MM-DD"); + expect(formatted).toBe(expected); + }); + }); + + describe("handleRange", () => { + // 场景1: 传入非列表值/传入列表值少于两项/传入列表值的两项均非日期格式 + it("传入 undefined 返回 null", () => { + const result = handleRange(undefined); + expect(result).toBeNull(); + }); + + it("传入 null 返回 null", () => { + const result = handleRange(null); + expect(result).toBeNull(); + }); + + it("传入非列表值返回 null", () => { + const result = handleRange("not-a-array"); + expect(result).toBeNull(); + }); + + it("传入长度小于2的列表返回 null", () => { + expect(handleRange([])).toBeNull(); + expect(handleRange(["2024-01-15"])).toBeNull(); + }); + + it("传入长度为2且两项均非日期格式的列表返回 null", () => { + expect(handleRange([null, undefined])).toBeNull(); + expect(handleRange(["abc", "not-a-date"])).toBeNull(); + }); + + // 场景2: 传入列表值的两项中有一项符合日期格式,另一项不符合 + it("传入第一项为非日期格式且第二项为日期的列表,第一项使用默认值处理", () => { + const result = handleRange(["abc", "2024-01-15"]); + expect(result).not.toBeNull(); + expect(result.from).toBe("1970-01-01"); + expect(result.to).toBe("2024-01-15"); + }); + + it("传入第一项为日期且第二项为非日期格式的列表,第二项使用默认值处理", () => { + const result = handleRange(["2024-01-15", "not-a-date"]); + const today = dayjs().format("YYYY-MM-DD"); + expect(result).not.toBeNull(); + expect(result.from).toBe("2024-01-15"); + expect(result.to).toBe(today); + }); + + // 场景3: 传入列表值的两项均符合日期格式 + it("传入两项均为日期列表且第一项日期早于第二项日期,原样返回", () => { + const result = handleRange(["2024-01-15", "2024-12-31"]); + expect(result).not.toBeNull(); + expect(result.from).toBe("2024-01-15"); + expect(result.to).toBe("2024-12-31"); + }); + + it("传入两项均为日期列表且第一项日期不早于第二项日期,交换两项后返回", () => { + const result = handleRange(["2024-12-31", "2024-01-15"]); + expect(result).not.toBeNull(); + expect(result.from).toBe("2024-01-15"); + expect(result.to).toBe("2024-12-31"); + }); + + it("处理传入两项为Date格式的列表", () => { + const date1 = new Date("2024-01-15"); + const date2 = new Date("2024-12-31"); + const result = handleRange([date1, date2]); + expect(result).not.toBeNull(); + expect(result.from).toBeInstanceOf(Date); + expect(result.to).toBeInstanceOf(Date); + expect(result.from.getTime()).toBe(new Date("2024-01-15").getTime()); + expect(result.to.getTime()).toBe(new Date("2024-12-31").getTime()); + }); + + it("处理传入一项字符串格式日期另一项为Date格式的列表", () => { + const date1 = new Date("2024-01-15"); + const result = handleRange([date1, "2024-12-31"]); + expect(result).not.toBeNull(); + // Date object is returned as-is, string is returned as string + expect(result.from).toBeInstanceOf(Date); + expect(result.to).toBe("2024-12-31"); + expect(result.from.getTime()).toBe(new Date("2024-01-15").getTime()); + }); + + it("传入两项均为日期且处于同一天的列表,原样返回", () => { + const result = handleRange(["2024-01-15", "2024-01-15"]); + expect(result).not.toBeNull(); + expect(result.from).toBe("2024-01-15"); + expect(result.to).toBe("2024-01-15"); + }); + }); +});