循环事件
循环事件是日历应用程序中的一个实用功能,允许用户设置按指定间隔重复的事件。从 7.1 版本开始,Scheduler 采用了 RFC-5545 标准格式来支持循环事件。
本指南将介绍如何在 Scheduler 中使用循环事件,以及如何将其保存到数据库中。
您可以在这里查看旧版循环事件格式的说明。
默认情况下,Scheduler 并未启用循环事件。要添加此功能,您需要在页面上激活一个特殊扩展 -- recurring 插件:
scheduler.plugins({
recurring: true
});
启用循环事件后,lightbox 界面将会多出一个额外的部分,如下图所示:

配置选项
该库为循环事件提供了以下配置选项:
- repeat_date - 控制 'recurring' lightbox 中"结束日期"字段所使用的日期格式。
scheduler.config.repeat_date = "%m/%d/%Y";
...
scheduler.init('scheduler_here', new Date(2019, 7, 5), "month");
'Recurring' lightbox
启用 recurring 扩展后,lightbox 会增加一个名为"Repeat event"的部分。'recurring' lightbox 的默认配置如下:
[
{name:"description", height:130, map_to:"text", type:"textarea" , focus:true},
{name:"recurring", height:115, type:"recurring", map_to:"rec_type",
button:"recurring"},
{name:"time", height:72, type:"time", map_to:"auto"}
];
您可以自由添加其他部分,但请确保"recurring"和"time"这两个部分必须保留。同时,"time"部分应始终放在"recurring"部分之后。
格式说明
循环事件在数据库中以单条记录的形式保存,该记录包含所有标准事件字段以及一些额外属性:
- start_date - (datetime) 表示系列的起始日期
- end_date - (datetime) 表示系列的结束日期
- rrule - (string) 定义循环规则
- duration - (number) 每次循环实例的持续时间
- recurring_event_id - (string|number) 父系列的 ID,仅在修改或删除实例时设置
- original_start - (datetime) 被编辑实例的原始日期,仅在修改或删除实例时设置
- deleted - (boolean) 标记为已删除的实例,仅在删 除实例时设置
rrule 属性遵循 RFC-5545 中定义的 iCalendar 格式,用于指定频率、间隔及其他循环细节。
与 iCalendar 格式的区别
我们的格式与 iCalendar 格式有两点主要不同:
STDATE 和 DTEND 的单独存储
iCalendar 通常将循环系列的起止日期作为 STDATE 和 DTEND 属性包含在 RRULE 字符串中,而我们的格式则将 start_date 和 end_date 作为单独字段存储。这样可以更方便地按日期处理和查询循环事件,而无需解析 RRULE 字符串。
以下是一个每周一重复、从 2024 年 6 月 1 日至 2024 年 12 月 1 日的循环事件系列示例:
{
"id": 1,
"text": "Weekly Team Meeting",
"start_date": "2024-06-03 09:00:00",
"duration": 3600,
"end_date": "2024-12-02 10:00:00",
"rrule": "FREQ=WEEKLY;INTERVAL=1;BYDAY=MO",
"recurring_event_id": null,
"original_start": null
}
异常情况的处理
异常情况(即被修改或删除的实例)会作为单独的事件记录存储,并与其父系列关联。这些异常记录包含三个额外属性:recurring_event_id、original_start 和 deleted。它们用于标识哪些实例已被更改或移除,以及它们与主系列的关系。
与标准 iCalendar 格式不同,异常(被修改或删除的实例)不会存储在 RRULE 的 EXDATE 属性中。
以下是一个包含一个被修改和一个被删除实例的循环系列示例:
[
{
"id": 1,
"text": "Weekly Team Meeting",
"start_date": "2024-06-03 09:00:00",
"duration": 3600,
"end_date": "2024-12-02 10:00:00",
"rrule": "FREQ=WEEKLY;INTERVAL=1;BYDAY=MO",
"recurring_event_id": null,
"original_start": null
},
{
"id": 2,
"text": "Special Team Meeting",
"start_date": "2024-06-10 09:00:00",
"end_date": "2024-06-10 11:00:00",
"rrule": null,
"recurring_event_id": 1,
"original_start": "2024-06-10 09:00:00"
},
{
"id": 3,
"text": "Deleted Team Meeting",
"start_date": "2024-06-17 09:00:00",
"end_date": "2024-06-17 10:00:00",
"rrule": null,
"recurring_event_id": 1,
"original_start": "2024-06-17 09:00:00",
"deleted": true
}
]
原定于 2024-06-10 09:00:00 的事件已被 Special Team Meeting 记录所替代,而 2024-06-17 09:00:00 的事件则被省略。
请注意,已修改或删除实例的 rrule 属性会被忽略。
同时,被删除实例的 text、start_date 和 end_date 字段不会影响 Scheduler 的行为。
编辑/删除系列中的某一实例
您可以删除或编辑循环系列中的某一具体实例。
重要提示
- 对循环事件的每一次更改都会在数据库中生成一条新记录。
- 单个实例通过 recurring_event_id 属性与主系列关联。
- 当某一实例被编辑时,original_start 字段保存的是该实例最初计划的日期,而不是新日期。例如,如果原定于 2024 年 7 月 27 日 15:00 的实例被移动到 2024 年 7 月 30 日 15:00,original_start 仍为 2024 年 7 月 27 日 15:00。
服务端逻辑
除了额外字段外,服务端控制器还应实现以下逻辑:
- 当添加已删除的实例时,服务端响应必须包含 "deleted" 状态。
- 已删除实例通过非空的 deleted 属性识别。
- 当系列被修改时,所有与该系列关联的已修改和已删除实例都应被移除。
- 系列通过非空 rrule 且空 recurring_event_id 识别。
- 已修改实例为所有 recurring_event_id 与系列 id 匹配的记录。
- 如果删除了 recurring_event_id 非空的事件,应通过设置 deleted="true" 进行标记,而不是直接移除。
完整代码示例请参见这里
自定义 lightbox 的 recurring 区块控件
从 4.2 版本开始,dhtmlxScheduler 允许您为 lightbox 的 'recurring' 部分自定义 HTML 表单。
可以自定义哪些内容?
- 更改表单的结构。
- 移除不需要的元素(如"每年"重复选项及其输入框)。
- 为输入项设置默认值(例如默认选中"无结束日期"并隐藏循环结束设置区块)。
使用示例
以下示例移除了"每月"和"每年"重复选项,并默认选中"无结束日期"选项(隐藏循环结束区块)。
- 在页面上定义自定义表单结构(可从 'schedulersourceslocalerecurring' 目录复制默认模板开始):
<div class="dhx_form_repeat" id="my_recurring_form"> /*!*/
<form>
<div>
<select name="repeat">
<option value="day">Daily</option>
<option value="week">Weekly</option>
</select>
</div>
<div>
<div id="dhx_repeat_day">
<input type="hidden" name="day_type" value="d"/>
<input type="hidden" name="day_count" value="1" />
</div>
<div id="dhx_repeat_week">
Repeat every week next days:
<label><input type="checkbox" name="week_day" value="1" />Monday</label>
<label><input type="checkbox" name="week_day" value="2" />Tuesday</label>
<label><input type="checkbox" name="week_day" value="3" />Wednesday</label>
<label><input type="checkbox" name="week_day" value="4" />Thursday</label>
<label><input type="checkbox" name="week_day" value="5" />Friday</label>
<label><input type="checkbox" name="week_day" value="6" />Saturday</label>
<label><input type="checkbox" name="week_day" value="0" />Sunday</label>
<input type="hidden" name="week_count" value="1" />
</div>
</div>
<input type="hidden" value="no" name="end"/>
</form>
</div>
- 将 'recurring' 部分的 'form' 参数设置为自定义表单的 ID:
scheduler.config.lightbox.sections = [
{name:"description", height:130, map_to:"text", type:"textarea" , focus:true},
{name:"recurring", type:"recurring", map_to:"rec_type", button:"recurring",
form:"my_recurring_form"},/*!*/
{name:"time", height:72, type:"time", map_to:"auto"}
];

主要部分
不同语言下,lightbox 中循环块的默认 HTML 结构位于 'schedulersourceslocalerecurring' 目录。
例如,英文语言包使用的是 'schedulersourceslocalerecurringrepeat_template_en.htm' 文件。
lightbox 中的循环块通常包含 3 组控件:
- 选择循环类型的控件。这些输入项的 name 都为 'repeat',可选值为:'daily'、'weekly'、'monthly'、'yearly'。 表单中至少应包含一个带有 value 的 'repeat' 输入项。你可以使用单选按钮、下拉选择框, 或者通过隐藏输入项设置默认类型。
以下是在表单中选择循环类型的几种有效示例:
- 单选按钮:
<label><input type="radio" name="repeat" value="day" />每日</label>
<label><input type="radio" name="repeat" value="week"/>每周</label>
<label><input type="radio" name="repeat" value="month" />每月</label>
<label><input type="radio" name="repeat" value="year" />每年</label>
- 下拉选择框(不包含"每月"和"每年"选项):
<select name="repeat">
<option value="day">每日</option>
<option value="week">每周</option>
</select>
- 隐藏输入项(此配置将仅创建"每日"系列):
<input type="hidden" name="repeat" value="day" />
- 根据所选循环类型设置循环详情的区域。例如,"每日"循环类型的块如下所示:
<div class="dhx_repeat_center">
<div id="dhx_repeat_day">
<label>
<input class="dhx_repeat_radio" type="radio"
name="day_type" value="d"/>每
</label>
<input class="dhx_repeat_text" type="text"
name="day_count" value="1" />天
<label>
<input class="dhx_repeat_radio" type="radio"
name="day_type" checked value="w"/>每个工作日
</label>
</div>
...
</div>
请注意,与特定循环类型相关的标记可以包裹在一个 id 格式为 "dhx_repeat_<repeat type>" 的 div 中,例如 "dhx_repeat_day"。 只有在选中对应循环类型时,这个块才会显示。
- 指定循环结束时间的控件。控制此项的输入 name 为 'end'。
可选值包括 'no'、'date_of_end' 和 'occurences_count'。
与 'repeat' 控件类似,表单中必须至少包含一个此类型的输入项。
<div class="dhx_repeat_right">
<label>
<input type="radio" name="end" value="no" checked/>无结束日期
</label>
<label>
<input type="radio" name="end" value="date_of_end" />在此之后</label>
<input type="text" name="date_of_end" />
<label>
<input type="radio" name="end" value="occurences_count" />在此之后</label>
<input type="text" name="occurences_count" value="1" />次
</div>
对于 'date_of_end' 模式,日期应填写在名为 'date_of_end' 的输入框中。类似地,'occurences_count' 模式下,发生次数应填写在名为 'occurences_count' 的输入框中。
你可以移除任意循环类型,或通过隐藏输入项提前设定:
<input type="hidden" name="end" value="date_of_end" />
<input type="hidden" name="date_of_end" value="01.01.2024" />
修改循环块的注意事项
在自定义 lightbox 的循环块之前,请注意以下几点:
- 所有输入项的 'name' 属性是固定的;不同 name 的输入项将会被忽略。
- 所有输入项的 'value' 属性也是固定的,除非该输入项用于用户直接输入。
- 当你提供新的表单时,dhtmlxScheduler 不会直接使用它,而是会将你的 HTML 结构复制到 lightbox 的模板中。 这意味着,附加在你表单 DOM 元素上的任何事件处理器或自定义属性都不会在 lightbox 中生效。 如需添加事件处理器,你需要将其作为内联 HTML 属性包含,或在表单显示到 lightbox 后再进行绑定。
请注意,dhtmlxScheduler 并不会直接使用你的原始 HTML 表单,而是会在 lightbox 模板中创建它的副本。
例如:
- 这行代码会被复制到 lightbox:
<input onclick="handler()"/>
- 但这样不会被复制:
addEventListener(node, "click", function(){...})
循环事件的旧格式
在 7.1 版本之前,Scheduler 使用的是自定义的循环事件格式。关于此格式的详细信息可参见 这里。