Mantine Schedule 组件库实战指南:用 @mantine/schedule 构建企业级日历与排期界面

发布时间:2026/9/10 8:37:17
Mantine Schedule 组件库实战指南:用 @mantine/schedule 构建企业级日历与排期界面 Mantine Schedule 组件库实战指南用 mantine/schedule 构建企业级日历与排期界面【免费下载链接】mantineA fully featured React components library项目地址: https://gitcode.com/GitHub_Trending/ma/mantinemantine/schedule是 Mantine 生态中面向 React 的完整日历/排期组件包提供日、周、月、年四种视图以及资源视图Resource Views与议程视图Agenda View内置重复事件RFC 5545 RRule、拖拽移动、边缘缩放、时间槽框选、外部拖入等交互能力。本文以该包在 packages/mantine/schedule 中的真实源码实现为依据从安装配置、事件数据结构、各视图 API、交互事件到 i18n 与样式定制系统讲解如何在一个 Mantine 应用中落地一套生产可用的排期界面。安装与依赖关系在 Mantine 项目中安装mantine/schedule需要同时安装它的几个核心依赖包。根据包内 package.json 中的peerDependencies声明它依赖mantine/coremantine/datesmantine/hooksreact^19.2.0与react-dom^19.2.0官方 README 提供了两种包管理器安装方式# With yarn yarn add mantine/schedule mantine/core mantine/hooks mantine/dates # With npm npm install mantine/schedule mantine/core mantine/hooks mantine/dates其中mantine/dates是必须的因为 Schedule 系列组件通过useDatesContext()读取DatesProvider提供的 locale、firstDayOfWeek、weekendDays等全局日期配置见 DayView.tsx 与 WeekView.tsx组件间通过上下文共享同一套日期语义。运行时依赖方面mantine/schedule还直接依赖rrule^2.8.1用于解析与展开 RFC 5545 重复规则。安装后需要确保引入包导出的样式文件如mantine/schedule/styles.css见 package.json 的exports字段并按 Mantine 文档约定在全局配置中启用 MantineProvider。包结构与组件全景从 index.ts 的导出清单可以完整看到包的组成主入口组件Schedule一个可切换四种视图的“一站式”调度器独立视图组件DayView、WeekView、MonthView、YearView资源视图ResourcesDayView、ResourcesWeekView、ResourcesMonthView、ResourcesSchedule辅助视图AgendaView、MobileMonthView底层构件ScheduleEvent、ScheduleBackgroundEvent、ScheduleHeader、CurrentTimeIndicator、MoreEvents、DragContext类型、工具函数与标签i18n定义。仓库结构上components/下每个视图目录都自带同名.module.css、.story.tsx与.test.tsx并配有独立的布局计算工具目录如WeekView/get-week-view-events/便于读者按模块深入阅读实现细节。事件数据模型ScheduleEventData所有视图消费统一的事件数据结构ScheduleEventData它由三种变体联合而成定义在 types.ts单次事件ScheduleSingleEventData不含recurrence字段重复系列ScheduleRecurringSeriesEventData带recurrencerrule 规则单次覆盖ScheduleRecurringOverrideEventData通过recurringEventIdrecurrenceId覆盖系列中的某一次发生。公共字段ScheduleEventBase见 types.ts字段类型说明idstring \| number事件唯一标识用作 key 与识别titlestring事件标题start/endDate \| DateTimeStringValue开始/结束时间字符串格式为YYYY-MM-DD HH:mm:sscolorMantineColortheme.colors键名或任意合法 CSS 颜色variantfilled \| light事件样式变体默认lightdisplaydefault \| background背景事件以整块色块渲染在普通事件之后默认非交互payloadRecordPropertyKey, any业务自定义数据库内部不消费resourceIdstring \| number资源视图中使用的事件归属时间统一约定日期字符串为YYYY-MM-DDDateStringValue日期时间字符串为YYYY-MM-DD HH:mm:ssDateTimeStringValue这是包内所有回调、工具函数与内部计算的通用格式。一个典型的事件数组示例取自 Schedule.story.tsx 的真实用法import { ScheduleEventData } from mantine/schedule; const events: ScheduleEventData[] [ { id: 1, title: Team Meeting, start: new Date(2024, 0, 15, 10, 0), end: new Date(2024, 0, 15, 11, 0), color: blue, payload: {}, }, { id: 2, title: Conference Day 1, start: new Date(2024, 0, 15, 0, 0, 0), end: new Date(2024, 0, 15, 23, 59, 59), color: cyan, payload: {}, }, ];重复事件Recurrence重复系列通过 RFC 5545 规则描述const recurringEvent: ScheduleEventData { id: weekly-standup, title: Weekly Standup, start: 2024-01-15 09:00:00, end: 2024-01-15 09:30:00, color: blue, recurrence: { rrule: FREQWEEKLY;BYDAYMO,WE,FR, exdate: [2024-01-17 09:00:00], // 排除特定日期 dtstart: 2024-01-15 09:00:00, // 显式系列起点可选 }, };运行时会由expandRecurringEvents见 expand-recurring-events.ts在渲染前把系列展开为可见范围内的具体发生实例使用RRule.parseString解析 rrule 字符串兼容带RRULE:前缀与多行 iCalendar 文本的情况getRRuleString通过rule.between()查询与当前视图范围重叠的发生并按expansionLimit默认 2000见DEFAULT_EXPANSION_LIMIT截断防止无限循环生成实例的id形如{seriesId}::{recurrenceId}并附带recurringInstance元数据isRecurringInstance、recurringEventId、recurrenceId、originalStart/originalEnd拖拽/缩放后可用于回写原系列exdate命中的发生被跳过若存在对应的 override 事件则替换为该 override。每个视图组件都在渲染前调用expandRecurringEvents例如 DayView.tsx因此无需手工展开。一站式入口Schedule 组件Schedule是面向“快速搭建”的聚合组件源码见 Schedule.tsx内部用useUncontrolled同时管理date与view两个状态既可以传入date/onDateChange、view/onViewChange做受控使用也可以只传defaultDate默认今天与defaultView默认week做非受控使用。import { Schedule } from mantine/schedule; function CalendarPage() { return ( Schedule defaultDate2024-01-15 defaultViewweek events{events} withAgenda layoutresponsive / ); }核心 props 一览详见 Schedule.tsxProp默认值说明view/defaultViewweek视图级别day、week、month、yeardate/defaultDate今天当前展示日期受控/非受控events—跨所有视图展示的事件数组layoutdefaultresponsive时小屏自动切换到YearView/MobileMonthViewmodedefaultstatic时禁用全部事件交互拖拽、缩放、点击withEventsDragAndDropfalse启用事件拖拽withEventResizefalse启用事件边缘缩放withDragSlotSelectfalse启用拖拽框选时间段withInteractiveBackgroundEventsfalse背景事件可点击recurrenceExpansionLimit2000每条重复系列最多展开的实例数withAgendafalse在 Day/Week/Month 视图头部显示 Agenda 按钮locale、radius、labels—语言、圆角、文案覆盖视图专属配置通过dayViewProps、weekViewProps、monthViewProps、yearViewProps、mobileMonthViewProps透传。Schedule在mode static时会把withEventsDragAndDrop、withEventResize、withInteractiveBackgroundEvents强制置为false见 Schedule.tsx保证只读展示场景下不会有任何交互残留。视图组件逐个击破DayView单日时间轴DayView是“日网格”视图DayView.tsx其时间槽布局由getDayTimeIntervals生成。关键 props 与默认值startTime/endTimeHH:mm:ss时间轴范围默认00:00:00至23:59:59intervalMinutes默认15每个时间槽的分钟数必须能整除一小时如 15、30或为整小时数如 120、240withSubHourGridLines默认true小于 1 小时的间隔是否显示细分网格线withAllDaySlot默认true显示顶部全天槽slotLabelFormat默认HH:mm与headerFormat默认MMMM D, YYYY时间标签与头部日期格式可传 dayjs 格式字符串或回调函数slotHeight默认64px与allDaySlotHeight默认44px行高通过 CSS 变量--day-view-slot-height、--day-view-all-day-slot-height注入highlightBusinessHours默认false与businessHours默认[09:00:00, 17:00:00]高亮工作时间withCurrentTimeIndicator默认仅当天显示与withCurrentTimeBubble默认true当前时间指示线及其气泡getCurrentTime自定义“当前时间”获取函数可用于时区修正startScrollTime初始渲染时滚动到指定时刻getTimeSlotProps为每个时间槽注入额外 props返回的事件处理器会与内部处理器组合而非覆盖。交互回调onTimeSlotClick携带slotStart/slotEnd与原生事件、onAllDaySlotClick、onEventClick、onEventDragStart/onEventDrop/onEventDragEnd、onEventResize、onSlotDragEnd、onExternalEventDrop。事件定位算法位于 get-day-positioned-events.ts对重叠事件进行分组布局为每个事件计算百分比top、height、width、offset见DayEventPositionData从而在时间轴上实现多列并排而不互相遮挡。全天事件最多展示 2 条超出部分通过MoreEvents弹出“N more”列表由getVisibleEvents计算。WeekView周网格WeekView在 DayView 能力基础上增加周维度WeekView.tsx。差异点intervalMinutes默认60周视图单格更大firstDayOfWeek默认1周一与weekendDays默认由DatesProvider决定周起始日与周末标记也可从DatesProvider继承withWeekendDays默认true隐藏周末列withWeekNumber默认true左上角显示周数weekdayFormat默认ddd与weekLabelFormat默认MMM DDhighlightToday默认false高亮今天所在列forceCurrentTimeIndicator默认false跨周浏览时也显示当前时间线businessHours支持按天配置传入以星期为键的记录0为周日将某天设为null表示该天完全不在工作时间内renderWeekLabel完全自定义头部周标签的渲染。布局计算位于 get-week-view-events 目录assign-event-rows负责全天事件的纵向堆叠calculate-regular-event-overlaps负责定时事件的重叠分列calculate-all-day-event-width/calculate-all-day-event-offset处理跨天全天事件的宽度与偏移calculate-event-days与get-hanging-status计算跨周事件的“悬挂”状态hangingstart/end/both/none。点击星期头部会通过onViewChange(day)联动切换到日视图。MonthView月历网格MonthView把事件按“周行 天列”布局目录见 MonthView。布局管线由多个带独立单测的小工具组成get-weeks-in-range计算当月覆盖的周calculate-event-position-in-week与find-available-row为事件分配行号row与起始偏移startOffsetget-renderable-month-event-segments处理跨周事件的分段渲染get-visible-columns计算可展示的列数。关键 props 包括firstDayOfWeek、weekendDays、withWeekendDays、weekdayFormat、getDayProps细粒度定制每天单元格等同时支持onDayClick与事件点击回调。月视图顶部同样可通过withAgenda展开议程列表。YearView年视图YearView将一年拆分为 12 个月的小网格YearView.tsx支持onMonthClick点击月份后切换到月视图在Schedule中该跳转由handleMonthClick统一处理先更新日期再切换视图见 Schedule.tsx。资源视图按资源分列的排期资源视图适合会议室、医生排班、工位等“资源 × 时间”场景由ResourcesDayView、ResourcesWeekView、ResourcesMonthView与ResourcesSchedule资源版一站式入口提供。资源数据模型见 types.tsinterface ScheduleResourceData { id: string | number; // 资源唯一标识 label: React.ReactNode; // 资源显示名 color?: MantineColor; // 资源颜色 payload?: RecordPropertyKey, any; // 自定义数据 } interface ScheduleResourceGroup { label: React.ReactNode; resourceIds: (string | number)[]; // 组内资源 }事件通过resourceId关联到具体资源列。重叠事件的分组计算在 get-overlap-clusters 与get-resources-week-view-events中实现分组以类似 rowspan 的方式跨列合并显示。ResourcesSchedule在顶部提供资源切换能力适合“同一天内快速切换资源”的运营型界面。AgendaView 与 MobileMonthViewAgendaView把一段时间内的事件渲染为扁平列表get-agenda-view-events负责收集与排序可作为 Day/Week/Month 视图的补充信息流通过withAgenda开启MobileMonthView为触屏优化的精简月视图layoutresponsive时在窄屏替代 Day/Week/Month 视图见 Schedule.tsx。交互能力拖拽、缩放与框选Schedule系列的交互由hooks/目录下的专用 hooks 驱动hooks/index.tsuse-drag-drop-handlers事件拖拽主逻辑计算落点calculateDropTarget、维护DragContext被拖事件、预览、落点高亮并处理拖拽中自动滚动配合use-auto-scroll-on-draguse-event-resize事件边缘缩放按intervalMinutes或eventResizeInterval对齐步长use-slot-drag-select按住拖拽框选连续时间段结束时通过onSlotDragEnd(rangeStart, rangeEnd)回调use-drag-state拖拽状态机use-drag-state.test.ts覆盖了状态迁移。启用拖拽移动Schedule events{events} withEventsDragAndDrop canDragEvent{(event) event.payload?.editable ! false} onEventDrop{({ eventId, newStart, newEnd, event }) { // 更新业务数据源例如调用 API 保存新时间 console.log(eventId, newStart, newEnd, event); }} /拖放时间由calculateDropTimeutils/calculate-drop-time计算按时间槽或eventDragInterval对齐并保留鼠标在事件内的偏移量保证拖拽手感。拖拽期间会渲染dragPreview预览块DayView 与 WeekView 分别有dayViewDragPreview、weekViewDragPreview样式名。启用边缘缩放Schedule events{events} withEventResize onEventResize{({ eventId, newStart, newEnd, event }) { // 保存新的开始/结束时间 }} /缩放同样按时间步长吸附DayView/WeekView 中受eventResizeInterval控制默认取intervalMinutes。resize 过程中事件本体即时更新getResizePosition松手后触发onEventResize。外部元素拖入通过onExternalEventDrop(dataTransfer, dropDateTime)可以把日程应用之外的元素如任务列表直接拖入时间轴dataTransfer中可携带自定义数据与内部事件拖拽共用onDragOver高亮与自动滚动逻辑。框选时间段Schedule withDragSlotSelect onSlotDragEnd{(rangeStart, rangeEnd) { // 例如弹出“新建事件”表单并预填时间 setNewEvent({ start: rangeStart, end: rangeEnd }); }} /框选在 DayView 与 WeekView 中可用选中的槽位带drag-selectedmod 高亮。modestatic 只读模式将mode设为static会同时关闭事件拖拽、缩放、点击、时间槽点击与键盘导航各槽位tabIndex变为 -1适合“只读预览/分享页”场景背景事件交互也会被强制关闭。背景事件与自定义渲染背景事件设置display: background的事件会作为整块半透明色块渲染在普通事件之后常用来表示工作时间、假期、维护窗口等。默认不可交互Day/Week/Month 视图可通过withInteractiveBackgroundEvents开启点击并触发onEventClick注意YearView与响应式布局下的MobileMonthView不渲染背景事件见 Schedule.tsx 的注释说明。自定义事件渲染两个层级// 1. 只改事件内容 const renderEventBody (event: ScheduleEventData) ( div strong{event.title}/strong span{event.payload?.location}/span /div ); // 2. 完全接管事件根元素渲染 const renderEvent: RenderEvent (event, props) ( button {...props}>Schedule events{events} labels{{ today: 今天, week: 周, month: 月, allDay: 全天, moreLabel: (n) 还有 ${n} 个, noEvents: 暂无事件, }} /日期与星期名的本地化则走mantine/dates的DatesProviderlocale字段各视图的localeprop 可单独覆盖Schedule的locale也会透传给内部视图。无障碍与键盘导航源码在多处体现了无障碍设计时间槽、星期头、全天槽均为可聚焦的UnstyledButton带aria-label如Time slot 09:00:00 - 10:00:00并提供previousControlProps/nextControlProps/todayControlProps/viewSelectProps透传 ARIA 属性DayView 时间槽支持ArrowUp/ArrowDown在槽位间移动焦点DayView.tsxWeekView 提供完整的焦点网格导航handleWeekViewKeyDown可在星期列、全天槽、时间槽之间用方向键穿梭WeekView.tsxmodestatic时所有交互控件移出 Tab 顺序。样式定制Styles API 与 CSS 变量所有视图组件都遵循 Mantine 的 Styles API 约定factoryStylesApiPropsclassNames/styles/vars/unstyled。每个视图导出StylesNames联合类型例如WeekViewStylesNames覆盖weekView、weekViewHeader、weekViewDay、weekViewAllDaySlots、weekViewSlotLabel、weekViewBackgroundEvent、weekViewDragPreview等全部命名节点WeekView.tsx。尺寸与圆角通过 CSS 变量注入varsResolver如 DayView 的--day-view-radius、--day-view-slot-height、--day-view-all-day-slot-heightWeekView 的--week-view-radius、--week-view-slot-height、--week-view-all-day-slots-heightScheduleEvent 的--event-bg/--event-color/--event-radius。示例WeekView date2024-01-15 events{events} slotHeight{72} allDaySlotHeight{56} radiusmd classNames{{ weekViewDay: classes.myDay }} /包内每个组件都带.module.css与 Storybook story.story.tsx可作为样式覆盖的参照实现。测试与可靠性该包对布局算法与交互状态做了较充分的测试覆盖可作为实现正确性的参照布局算法单测get-week-positioned-events.test.ts、get-day-positioned-events.test.ts、get-month-view-events下的calculate-event-position-in-week.test.ts、find-available-row.test.ts、get-weeks-in-range.test.ts以及get-overlap-clusters.test.ts资源视图重叠分组等交互 hook 测试use-drag-drop-handlers.test.ts、use-drag-state.test.ts、use-event-resize.test.ts、use-horizontal-event-resize.test.ts组件测试每个视图组件均有*.test.tsx如 Schedule.test.tsx。在自行扩展布局算法或交互逻辑时这些测试文件是很好的行为契约参考。结语mantine/schedule以“单一事件模型 多视图聚合”的方式把日/周/月/年视图、资源排期、议程列表、重复事件、拖拽/缩放/框选等排期系统的高频能力收敛进一个包中。无论是直接用Schedule快速搭建还是用DayView/WeekView等独立组件定制专属界面其 源码目录 都提供了清晰的分层视图组件负责组合与交互utils/负责纯布局计算可独立单测hooks/负责拖拽等交互状态。结合 Mantine 的 Styles API 与DatesProvider国际化体系它足以支撑从内部工具到对外产品的各类日历场景。【免费下载链接】mantineA fully featured React components library项目地址: https://gitcode.com/GitHub_Trending/ma/mantine创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考