我使用 jQuery FullCalendar 作为我的日历,在我的网站中用于可用性议程。
fullcalendar 中是否有任何功能/方法/选项可以按天处理我的重复事件?例如,星期一只到早上 7:00 到上午 9:00,星期二 - 下午 4:00 到晚上 9:00,类似这样?
我使用 jQuery FullCalendar 作为我的日历,在我的网站中用于可用性议程。
fullcalendar 中是否有任何功能/方法/选项可以按天处理我的重复事件?例如,星期一只到早上 7:00 到上午 9:00,星期二 - 下午 4:00 到晚上 9:00,类似这样?
为了添加此处列出的简单替代方案,Fullcalendar 现在(在某种程度上)支持每周重复发生的事件。因此,如果您只需要类似的内容:[Every Monday and Thursday from 10:00am to 02:00pm]
,您可以使用以下内容:
events: [{
title:"My repeating event",
start: '10:00', // a start time (10am in this example)
end: '14:00', // an end time (2pm in this example)
dow: [ 1, 4 ] // Repeat monday and thursday
}],
这记录在后台事件中,但它也适用于常规事件。
将其保存到数据库并不难。
如果您不希望它们无限重复,则需要添加一些开始日期和结束日期。
所以,在数据库中:
eventId timeStart timeEnd dow dateStart dateEnd
1 10:00 12:00 [1,4] 2015/03/01 2015/04/01 // Month of March
1 10:00 12:00 [1,4] 2015/05/01 2015/06/01 // Month of May
1 10:00 12:00 [1,4] 2016/01/01 2017/01/01 // Year of 2017
将此作为 JSON 传递给客户端:
{ id:1, start:"10:00", end:"12:00", dow:[1,4],
ranges[{start:"2015/03/01", end:"2015/04/01"},
{start:"2015/05/01", end:"2015/06/01"},
{start:"2016/01/01", end:"2017/01/01"},]
}
在客户端,使用 fullcalendar 的eventRender仅在其中一个时间范围内呈现事件。像这样的东西应该工作:
eventRender: function(event){
return (event.ranges.filter(function(range){ // test event against all the ranges
return (event.start.isBefore(range.end) &&
event.end.isAfter(range.start));
}).length)>0; //if it isn't in one of the ranges, don't render it (by returning false)
},
假设您的事件的结构如下:
var repeatingEvents = [{
title:"My repeating event",
id: 1,
start: '10:00',
end: '14:00',
dow: [ 1, 4 ],
ranges: [{ //repeating events are only displayed if they are within at least one of the following ranges.
start: moment().startOf('week'), //next two weeks
end: moment().endOf('week').add(7,'d'),
},{
start: moment('2015-02-01','YYYY-MM-DD'), //all of february
end: moment('2015-02-01','YYYY-MM-DD').endOf('month'),
},/*...other ranges*/],
},/*...other repeating events*/];
如果你想要一夜之间重复的事件(比如这里),就过去看看24:00
结束时间。例如:
{
start: '10:00', //starts at 10 on monday
end: '27:00', //24+3 is handled correctly.
dow: [1]
}
看看这个网站... http://fajitanachos.com/Fullcalendar-and-recurring-events/
它提供了很多关于经常性事件的好网站。FullCalendar 确实支持与 id 相关的重复事件。您可以在服务器端或客户端处理事件,但首选是服务器端。我会给你一些想法,但它不是包罗万象的。据我所知,重复发生的事件很难维持。
如果您想在客户端处理它们,则必须遍历重复事件的频率和哪几天的逻辑。您可能需要使用 eventRender 回调,然后使用选项回调呈现每个循环事件。这样做的问题是您仍然必须在数据库中保存重复频率和频率选项的逻辑运算符......
(column1:frequency=(int)8, column2:type=enum(a'b'c), a=daily, b=weekly, c=monthly 等等)。
...然后,无论何时您编辑该事件,它都会编辑所有事件。如果您只需要删除一个事件,您将在逻辑中遇到一系列问题,并且很容易变成一个巨大的混乱。
第二种选择是做所有这些服务器端。创建两个表,一个包含父事件,第二个包含所有重复事件。在父表中,您将存储一般信息,例如唯一 id、颜色、背景颜色、标题、allDay、isRecurring、频率、类型等。在子表中,您将使用父表中的唯一 id 来关联每次重复(请记住,如果您想删除/编辑单个事件,子表行也需要有自己的唯一 ID 以及标记它所在的表的列)。添加周期性事件时,您需要添加一个枚举字段,用于标记它是否是周期性事件 AKA...
column:recurring=enum('0','1')---真/假
...然后您需要将每个重复添加到具有特定信息(例如开始和结束等)的子表中。当您查询事件时,您可以从父项查询,然后如果事件重复发生,则将这些事件关联到第二个查询,或者您可以在一个查询中对 table1.id=table2.parentID 使用 INNER JOIN。
如您所见,重复事件可以非常快速地变得非常详细,找出您需要的逻辑,我希望这至少可以帮助您或某人开始。干杯。
无需在此处建立父子关系是为 jquery 中的重复事件提供简单解决方案的代码 Full calender 在您的 php 文件中使用以下这些函数,您可以进一步使用这些函数来调用所有事件。
function render_fccalendar_events() {
$_POST['start'] = strtotime('2013-05-01');
$_POST['end'] = strtotime('2013-05-31');
$start = date('Y-m-d',$_POST['start']);
$end = date('Y-m-d', $_POST['end']);
$readonly = (isset($_POST['readonly'])) ? true : false;
$events = fcdb_query_events($start, $end);
render_json(process_events($events, $start, $end, $readonly));
}
function process_events($events, $start, $end, $readonly) {
if ($events) {
$output = array();
foreach ($events as $event) {
$event->view_start = $start;
$event->view_end = $end;
$event = process_event($event, $readonly, true);
if (is_array($event)) {
foreach ($event as $repeat) {
array_push($output, $repeat);
}
} else {
array_push($output, $event);
}
}
return $output;
}
}
function process_event($input, $readonly = false, $queue = false) {
$output = array();
if ($repeats = generate_repeating_event($input)) {
foreach ($repeats as $repeat) {
array_push($output, generate_event($repeat));
}
} else {
array_push($output, generate_event($input));
}
if ($queue) {
return $output;
}
render_json($output);
}
function generate_event($input) {
$output = array(
'id' => $input->id,
'title' => $input->name,
'start' => $input->start_date,
'end' => $input->end_date,
'allDay' => ($input->allDay) ? true : false,
//'className' => "cat{$repeats}",
'editable' => true,
'repeat_i' => $input->repeat_int,
'repeat_f' => $input->repeat_freq,
'repeat_e' => $input->repeat_end
);
return $output;
}
function generate_repeating_event($event) {
$repeat_desk = json_decode($event->repeat_desk);
if ($event->repeat == "daily") {
$event->repeat_int =0;
$event->repeat_freq = $repeat_desk->every_day;
}
if ($event->repeat == "monthly") {
$event->repeat_int =2;
$event->repeat_freq = $repeat_desk->every_month;
}
if ($event->repeat == "weekly") {
$event->repeat_int =1;
$event->repeat_freq = $repeat_desk->every_weak;
}
if ($event->repeat == "year") {
$event->repeat_int =3;
$event->repeat_freq = $repeat_desk->every_year;
}
if ($event->occurrence == "after-no-of-occurrences") {
if($event->repeat_int == 0){
$ext = "days";
}
if($event->repeat_int == 1){
$ext = "weeks";
}
if($event->repeat_int == 2){
$ext = "months";
}
if($event->repeat_int == 3){
$ext = "years";
}
$event->repeat_end = date('Y-m-d',strtotime("+" . $event->repeat_int . " ".$ext));
} else if ($event->occurrence == "no-end-date") {
$event->repeat_end = "2023-04-13";
} else if ($event->occurrence == "end-by-end-date") {
$event->repeat_end = $event->end_date;
}
if ($event->repeat_freq) {
$event_start = strtotime($event->start_date);
$event_end = strtotime($event->end_date);
$repeat_end = strtotime($event->repeat_end) + 86400;
$view_start = strtotime($event->view_start);
$view_end = strtotime($event->view_end);
$repeats = array();
while ($event_start < $repeat_end) {
if ($event_start >= $view_start && $event_start <= $view_end) {
$event = clone $event; // clone event details and override dates
$event->start_date = date(AEC_DB_DATETIME_FORMAT, $event_start);
$event->end_date = date(AEC_DB_DATETIME_FORMAT, $event_end);
array_push($repeats, $event);
}
$event_start = get_next_date($event_start, $event->repeat_freq, $event->repeat_int);
$event_end = get_next_date($event_end, $event->repeat_freq, $event->repeat_int);
}
return $repeats;
}
return false;
}
function get_next_date($date, $freq, $int) {
if ($int == 0)
return strtotime("+" . $freq . " days", $date);
if ($int == 1)
return strtotime("+" . $freq . " weeks", $date);
if ($int == 2)
return get_next_month($date, $freq);
if ($int == 3)
return get_next_year($date, $freq);
}
function get_next_month($date, $n = 1) {
$newDate = strtotime("+{$n} months", $date);
// adjustment for events that repeat on the 29th, 30th and 31st of a month
if (date('j', $date) !== (date('j', $newDate))) {
$newDate = strtotime("+" . $n + 1 . " months", $date);
}
return $newDate;
}
function get_next_year($date, $n = 1) {
$newDate = strtotime("+{$n} years", $date);
// adjustment for events that repeat on february 29th
if (date('j', $date) !== (date('j', $newDate))) {
$newDate = strtotime("+" . $n + 3 . " years", $date);
}
return $newDate;
}
function render_json($output) {
header("Content-Type: application/json");
echo json_encode(cleanse_output($output));
exit;
}
function cleanse_output($output) {
if (is_array($output)) {
array_walk_recursive($output, create_function('&$val', '$val = trim(stripslashes($val));'));
} else {
$output = stripslashes($output);
}
return $output;
}
function fcdb_query_events($start, $end) {
global $wpdb;
$limit = ($limit) ? " LIMIT {$limit}" : "";
$result = $wpdb->get_results("SELECT id, name,start_date,end_date,repeat_desk,`repeat`,occurrence,occurrence_desk
FROM " .
$wpdb->prefix . "lgc_events
WHERE (
(start_date >= '{$start}' AND start_date < '{$end}')
OR (end_date >= '{$start}' AND end_date < '{$end}')
OR (start_date <= '{$start}' AND end_date >= '{$end}')
OR (start_date < '{$end}' AND (`repeat`!= ''))
)
ORDER BY start_date{$limit};");
return return_result($result);
}
function return_result($result) {
if ($result === false) {
global $wpdb;
$this->log($wpdb->print_error());
return false;
}
return $result;
}
在上面的代码中,我使用了 repeat_desk,在其中存储了重复频率的 json 代码
和 jquery 调用你的文件
events: {
url: '<?php echo $lgc_plugindir; ?>includes/imagerotator.php',
data: {
action: 'get_events'
},
type: 'POST'
}
我将此用于wordpress,您可以根据您的要求使用此代码
对于具有比 FullCalendar 可以处理的内置事件更复杂的重复事件的人(请参阅 slicedtoad 的答案),您可以使用rSchedule。
例如,仅周一至上午 7:00 至上午 9:00,周二 - 下午 4:00 至晚上 9:00
import { Schedule } from '@rschedule/rschedule';
import { StandardDateAdapter } from '@rschedule/standard-date-adapter';
const mondayDate = new Date(2019, 6, 15);
const tuesdayDate = new Date(2019, 6, 16);
const schedule = new Schedule({
// add specific dates
dates: [
new StandardDateAdapter(mondayDate, {duration: 1000 * 60 * 60 * 2})
],
// add recurrence rules
rrules: [{
start: tuesdayDate,
duration: 1000 * 60 * 60 * 5, // duration is expressed in milliseconds
frequency: 'WEEKLY'
}],
});
const firstFiveEvents = schedule
.occurrences({ take: 5 })
.toArray()
.map(adapter =>
({title: 'My event title', start: adapter.date, end: adapter.end})
);
// You can then pass `firstFiveEvents` to fullcalendar for rendering
rSchedule 还支持moment
/luxon
以及时区。有关更多信息,您可以查看rSchedule 文档。
目前我正在做有 FullCalendar 的项目,我必须做经常性事件。所以这就是我为什么可以做到这一点。希望这段代码对某人有所帮助:)
我在数据库中有下一个表:
CREATE TABLE IF NOT EXISTS `app_ext_calendar_events` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`users_id` int(11) NOT NULL,
`name` varchar(255) NOT NULL,
`description` text,
`start_date` int(11) NOT NULL,
`end_date` int(11) NOT NULL,
`event_type` varchar(16) NOT NULL,
`is_public` tinyint(1) DEFAULT NULL,
`bg_color` varchar(16) DEFAULT NULL,
`repeat_type` varchar(16) DEFAULT NULL,
`repeat_interval` int(11) DEFAULT NULL,
`repeat_days` varchar(16) DEFAULT NULL,
`repeat_end` int(11) DEFAULT NULL,
`repeat_limit` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_users_id` (`users_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=18 ;
我开发了下一个 php 类来获取带有重复事件的事件:
<?php
class calendar
{
static public function get_events($date_from, $date_to,$calendar_type)
{
global $app_user;
$list = array();
$events_query = db_query("select * from app_ext_calendar_events where FROM_UNIXTIME(start_date,'%Y-%m-%d')>='" . $date_from . "' and FROM_UNIXTIME(end_date,'%Y-%m-%d')<='" . $date_to . "' and event_type='" . $calendar_type . "' and users_id='" . db_input($app_user['id']) . "'");
while($events = db_fetch_array($events_query))
{
$list[] = $events;
}
if(count($repeat_events_list = calendar::get_repeat_events($date_to,$calendar_type)))
{
$list = array_merge($list,$repeat_events_list);
}
return $list;
}
public static function weeks_dif($start, $end)
{
$year_start = date('Y',$start);
$year_end = date('Y',$end);
$week_start = date('W',$start);
$week_end = date('W',$end);
$dif_years = $year_end - $year_start;
$dif_weeks = $week_end - $week_start;
if($dif_years==0 and $dif_weeks==0)
{
return 0;
}
elseif($dif_years==0 and $dif_weeks>0)
{
return $dif_weeks;
}
elseif($dif_years==1)
{
return (42-$week_start)+$week_end;
}
elseif($dif_years>1)
{
return (42-$week_start)+$week_end+(($dif_years-2)*42);
}
}
public static function months_dif($start, $end)
{
// Assume YYYY-mm-dd - as is common MYSQL format
$splitStart = explode('-', date('Y-n',$start));
$splitEnd = explode('-', date('Y-n',$end));
if (is_array($splitStart) && is_array($splitEnd))
{
$startYear = $splitStart[0];
$startMonth = $splitStart[1];
$endYear = $splitEnd[0];
$endMonth = $splitEnd[1];
$difYears = $endYear - $startYear;
$difMonth = $endMonth - $startMonth;
if (0 == $difYears && 0 == $difMonth)
{ // month and year are same
return 0;
}
else if (0 == $difYears && $difMonth > 0)
{ // same year, dif months
return $difMonth;
}
else if (1 == $difYears)
{
$startToEnd = 13 - $startMonth; // months remaining in start year(13 to include final month
return ($startToEnd + $endMonth); // above + end month date
}
else if ($difYears > 1)
{
$startToEnd = 13 - $startMonth; // months remaining in start year
$yearsRemaing = $difYears - 2; // minus the years of the start and the end year
$remainingMonths = 12 * $yearsRemaing; // tally up remaining months
$totalMonths = $startToEnd + $remainingMonths + $endMonth; // Monthsleft + full years in between + months of last year
return $totalMonths;
}
}
else
{
return false;
}
}
public static function get_repeat_events($date_to,$calendar_type)
{
global $app_user;
//convert date to timestamp
$date_to_timestamp = get_date_timestamp($date_to);
$list = array();
//get all events that already started (start_date<=date_to)
$events_query = db_query("select * from app_ext_calendar_events where length(repeat_type)>0 and FROM_UNIXTIME(start_date,'%Y-%m-%d')<='" . $date_to . "' and event_type='" . $calendar_type . "' and users_id='" . db_input($app_user['id']) . "'");
while($events = db_fetch_array($events_query))
{
$start_date = $events['start_date'];
//set repeat end
$repeat_end = false;
if($events['repeat_end']>0)
{
$repeat_end = $events['repeat_end'];
}
//get repeat events by type
switch($events['repeat_type'])
{
case 'daily':
//check repeat events day bay day
for($date = $start_date; $date<=$date_to_timestamp; $date+=86400)
{
if($date>$start_date)
{
$dif = round(abs($date-$start_date)/86400);
if($dif>0)
{
$event_obj = $events;
$event_obj['start_date'] = strtotime('+' . $dif . ' day',$event_obj['start_date']);
$event_obj['end_date'] = strtotime('+' . $dif . ' day',$event_obj['end_date']);
if(calendar::check_repeat_event_dif($dif,$event_obj,$repeat_end))
{
$list[] = $event_obj;
}
}
}
}
break;
case 'weekly':
//check repeat events day bay day
for($date = $start_date; $date<=$date_to_timestamp; $date+=86400)
{
if($date>$start_date)
{
//find days dif
$dif = round(abs($date-$start_date)/86400);
//find week dif
$week_dif = calendar::weeks_dif($start_date, $date);
if($dif>0 and (in_array(date('N',$date),explode(',',$events['repeat_days']))))
{
$event_obj = $events;
$event_obj['start_date'] = strtotime('+' . $dif . ' day',$event_obj['start_date']);
$event_obj['end_date'] = strtotime('+' . $dif . ' day',$event_obj['end_date']);
if(calendar::check_repeat_event_dif($week_dif,$event_obj,$repeat_end))
{
$list[] = $event_obj;
}
}
}
}
break;
case 'monthly':
/**
*in calendar we display 3 month in one view
*so we have to check difference for each month
*/
//check 1
$date_to_timestamp2 = strtotime('-2 month',$date_to_timestamp);
$dif = calendar::months_dif($start_date, $date_to_timestamp2);
if($dif>0)
{
$event_obj = $events;
$event_obj['start_date'] = strtotime('+' . $dif . ' month',$event_obj['start_date']);
$event_obj['end_date'] = strtotime('+' . $dif . ' month',$event_obj['end_date']);
if(calendar::check_repeat_event_dif($dif,$event_obj,$repeat_end))
{
$list[] = $event_obj;
}
}
//check 2
$date_to_timestamp1 = strtotime('-1 month',$date_to_timestamp);
$dif = calendar::months_dif($start_date, $date_to_timestamp1);
if($dif>0)
{
$event_obj = $events;
$event_obj['start_date'] = strtotime('+' . $dif . ' month',$event_obj['start_date']);
$event_obj['end_date'] = strtotime('+' . $dif . ' month',$event_obj['end_date']);
if(calendar::check_repeat_event_dif($dif,$event_obj,$repeat_end))
{
$list[] = $event_obj;
}
}
//check 3
$dif = calendar::months_dif($start_date, $date_to_timestamp);
if($dif>0)
{
$event_obj = $events;
$event_obj['start_date'] = strtotime('+' . $dif . ' month',$event_obj['start_date']);
$event_obj['end_date'] = strtotime('+' . $dif . ' month',$event_obj['end_date']);
if(calendar::check_repeat_event_dif($dif,$event_obj,$repeat_end))
{
$list[] = $event_obj;
}
}
break;
case 'yearly':
$dif = date('Y',$date_to_timestamp)-date('Y',$start_date);
if($dif>0)
{
$events['start_date'] = strtotime('+' . $dif . ' year',$events['start_date']);
$events['end_date'] = strtotime('+' . $dif . ' year',$events['end_date']);
if(calendar::check_repeat_event_dif($dif,$events,$repeat_end))
{
$list[] = $events;
}
}
break;
}
}
return $list;
}
static public function check_repeat_event_dif($dif,$events,$repeat_end)
{
$check = true;
if($dif>0)
{
//check interval
if($dif/$events['repeat_interval']!=floor($dif/$events['repeat_interval'])) $check=false;
//check repeat limit
if($events['repeat_limit']>0)
if(floor($dif/$events['repeat_interval'])>$events['repeat_limit']) $check=false;
}
else
{
$check=false;
}
//check repeat end date
if($repeat_end>0)
{
if($repeat_end<$events['start_date'])
{
$check=false;
}
}
return $check;
}
}
函数 get_events 以我的方式获取所有事件 + 重复事件有 4 种重复事件类型:每日、每周、每月、每年 + 有重复间隔、重复日期结束和重复限制。
函数 get_repeat_events 计算每种事件类型的差异,如果存在,则包括重复事件。
注意:函数 db_query() 可以替换为 myslq_query 或其他
准备事件到 FullCalendar 我正在使用下一个代码
$list = array();
foreach(calendar::get_events($_GET['start'],$_GET['end'],'personal') as $events)
{
$start = date('Y-m-d H:i',$events['start_date']);
$end = date('Y-m-d H:i',$events['end_date']);
$list[] = array('id' => $events['id'],
'title' => addslashes($events['name']),
'description' => str_replace(array("\n\r","\n","\r"),'<br>',$events['description']),
'start' => str_replace(' 00:00','',$start),
'end' => str_replace(' 00:00','',$end),
'color'=> $events['bg_color'],
'allDay'=>(strstr($start,'00:00') and strstr($end,'00:00')),
'url' => url_for('ext/calendar/personal_form','id=' . $events['id'])
);
}
echo json_encode($list);
这似乎在 eventRender 中工作得很好:function(event, element){}
EXAMPLE JSON:
var json = [{title: "All Day Event",
start: "2015-12-22T00:00",
end: "2015-12-22T23:55",
dow: [2,4],
recurstart: moment("2015-12-22").startOf("week"),
recurend: moment("2015-12-22").endOf("week").add(1,'w')},{
title: "Long Event",
start: "2015-12-21T00:00",
end: "2015-12-24T23:55",
recurstart: moment("2015-12-21").startOf("month"),
recurend: moment("2015-12-24").endOf("month"),
}];
eventRender: function(event, element){
var theDate = moment(event.start).format("YYYY-MM-DD");
var startDate = event.recurstart;
var endDate = event.recurend;
if (startDate < theDate && theDate < endDate) {
console.log(theDate);
}
else {
return event.length>0;
}
}, /* End eventRender */
1) 在 JSON 中设置开始/结束日期和时间。
2)在JSON中创建两个自定义recur Start和recur Ends。
3) 使用 moment.js 创建重复持续时间:http: //momentjs.com/docs/#/durations/。
4) Recur Start 使用 (start:) 日期来确定一周的开始。
5) Recur End 使用 (end:) 日期来确定一周的结束 + 增加 1 周。
6) 添加 1、2、3 周可以创建重复限制。
7) 添加名为 (recurlimit:"") 的 JSON 的另一部分可以管理重复限制。
8) 在 eventRender 中使用变量 - 设置我的示例使用的日期 (theDate),即 moment(event.start)。正确设置格式很重要,这样 start/end/recurstart 等都匹配格式,即 (YYYY-MM-DD) http://momentjs.com/docs/#/displaying/format/。
9) 自定义 recurstart 的变量
10) 自定义递归变量
11) 使用 IF 语句查看 (theDate) 介于 (recurstart) 和 (recurend) 之间的天气 - 记录结果
12) 使用 ELSE 语句返回长度>0 以隐藏不属于该参数的其他事件。
13) 非重复事件必须有 moment("match start date").startOf("month") & moment("match start date").endOf("month") 否则它们将不可见。
要处理复杂的重复规则,我们应该使用 iCal 的 rrule 实现库。我们有JS 库 (rrule)。FullCalendar 现在也支持rrule 插件。
您可以使用下面的代码显示一天、每天、每周、每两周和每月事件,并在鼠标悬停事件上使用工具提示。
/*This is js*/
document.addEventListener('DOMContentLoaded', function() {
var calendarEl = document.getElementById('calendar');
var calendar = new FullCalendar.Calendar(calendarEl, {
//NOTE MUST HAVE REFERENCE rrule javascript and plugin 'rrule', not rrPlugin like docs
//if using scripts, not import/build method
defaultDate: "2021-08-25",
plugins: ['interaction', 'dayGrid', 'timeGrid', 'rrule'],
timeZone: 'UTC',
defaultView: 'dayGridMonth',
header: {
left: 'prev,next today',
center: 'title',
right: 'dayGridMonth,timeGridWeek,timeGridDay'
},
editable: true,
eventClick: function(arg) {
console.log(arg);
if (confirm('Are you sure you want to delete this event?')) {
arg.event.remove()
}
},
eventMouseEnter: function(info) {
var tis=info.el;
var popup=info.event.extendedProps.popup;
var tooltip = '<div class="tooltipevent" style="top:'+($(tis).offset().top-5)+'px;left:'+($(tis).offset().left+($(tis).width())/2)+'px"><div>' + popup.title + '</div><div>' + popup.description + '</div></div>';
var $tooltip = $(tooltip).appendTo('body');
},
eventMouseLeave: function(info) {
$(info.el).css('z-index', 8);
$('.tooltipevent').remove();
},
events: [
{
title: 'One Day',
popup: {
title: 'One Day',
description: 'This is the description',
},
backgroundColor: '#c1391c',
rrule: {
dtstart: '2021-08-01T10:30:00',
until: '2021-08-01T19:30:00',
},
},
{
title: 'Daily',
popup: {
title: 'Daily',
description: 'This is Daily the description',
},
backgroundColor: '#bcc11c',
rrule: {
freq: 'daily',
dtstart: '2021-08-02T10:30:00',
until: '2021-08-05T19:30:00',
},
},
{
title: 'Weekly Event',
popup: {
title: 'Daily',
description: 'This is Daily the description',
},
backgroundColor: '#1cc1ab',
rrule: {
freq: 'weekly',
dtstart: '2021-08-06T10:30:00',
until: '2021-08-20T19:30:00',
},
},
{
title: 'Two Weekly Event',
popup: {
title: 'Daily',
description: 'This is Daily the description',
},
backgroundColor: '#1c60c1',
rrule: {
interval: 2,
freq: 'weekly',
dtstart: '2021-08-07T10:30:00',
until: '2021-08-30T19:30:00',
},
},
{
title: 'Four Weekl Event',
popup: {
title: 'Daily',
description: 'This is Daily the description',
},
backgroundColor: '#c11cbc',
rrule: {
interval: 4,
freq: 'weekly',
dtstart: '2021-08-01T10:30:00',
until: '2021-12-30T19:30:00',
},
},
]
});
calendar.render();
});
html, body {
margin: 0;
padding: 0;
font-family: Arial, Helvetica Neue, Helvetica, sans-serif;
font-size: 14px;
}
#calendar {
max-width: 900px;
margin: 40px auto;
}
.tooltipevent{
width:200px;/*
height:100px;*/
background:#ccc;
position:absolute;
z-index:10001;
transform:translate3d(-50%,-100%,0);
font-size: 0.8rem;
box-shadow: 1px 1px 3px 0px #888888;
line-height: 1rem;
}
.tooltipevent div{
padding:10px;
}
.tooltipevent div:first-child{
font-weight:bold;
color:White;
background-color:#888888;
border:solid 1px black;
}
.tooltipevent div:last-child{
background-color:whitesmoke;
position:relative;
}
.tooltipevent div:last-child::after, .tooltipevent div:last-child::before{
width:0;
height:0;
border:solid 5px transparent;/*
box-shadow: 1px 1px 2px 0px #888888;*/
border-bottom:0;
border-top-color:whitesmoke;
position: absolute;
display: block;
content: "";
bottom:-4px;
left:50%;
transform:translateX(-50%);
}
.tooltipevent div:last-child::before{
border-top-color:#888888;
bottom:-5px;
}
<!-- Required js and css with url -->
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/rrule@2.6.3/dist/es5/rrule.min.js"></script>
<script type="text/javascript" src="https://unpkg.com/@fullcalendar/core@4.3.1/main.min.js"></script>
<script type="text/javascript" src="https://unpkg.com/@fullcalendar/daygrid@4.3.0/main.min.js"></script>
<script type="text/javascript" src="https://unpkg.com/@fullcalendar/timegrid@4.3.0/main.min.js"></script>
<script type="text/javascript" src="https://unpkg.com/@fullcalendar/interaction@4.3.0/main.min.js"></script>
<script type="text/javascript" src="https://unpkg.com/@fullcalendar/rrule@4.3.0/main.min.js"></script>
<script type="text/javascript" src="https://unpkg.com/tooltip.js/dist/umd/tooltip.min.js"></script>
<script type="text/javascript" src="https://unpkg.com/popper.js/dist/umd/popper.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script>
<link rel="stylesheet" type="text/css" href="https://unpkg.com/@fullcalendar/core@4.3.1/main.min.css">
<link rel="stylesheet" type="text/css" href="https://unpkg.com/@fullcalendar/daygrid@4.3.0/main.min.css">
<link rel="stylesheet" type="text/css" href="https://unpkg.com/@fullcalendar/timegrid@4.3.0/main.min.css">
<div id='calendar'></div>
现在有一个插件可以做你想做的事:http: //momentjs.com/docs/#/plugins/recur/