最近由于客户需求需要做一个高度可定制化的日历,于是机缘巧合之下我认识了FullCalendar这个插件,查了相关中文版的使用教程,发现大部分讲解的其实不是很详细,并且无法满足我的需求,没办法只能生啃官方的英文文档了,下面我将我的一些使用经验分享给大家,也是踩了不少坑......话不多说直接进入正题
先看最终效果实现:
嘿嘿是不是看着功能还是挺强大的,下面将讲解具体的一个实现思路
首先我这边是直接把对应的js文件下载下来了通过script标签引入的,版本号是v6.1.13,如果你是使用的是vue、react、angular等框架,不用担心官方文档中也有对应的教程,例如Vue版本教程,即使你们使用的方式与我不一样,也可以继续观看下面的内容,或许能够帮助你理解这个插件的大致作用
总体可分为3个步骤: 插件本地化 >> 插件初始化 >> 动态更新
第一步 插件本地化
由于默认显示的是英文,因此我需要先设置成中文,如果你需要其他语言的支持可查看官方文档
具体代码如下:
// 为 FullCalendar 添加多种语言支持
function setFullCalendarLocales() {
// 每个本地化配置对象定义了该语言环境下插件的文本和日期格式
window.FullCalendar.globalLocales.push({
// 表示语言代码
code: 'zh-cn',
// 包含一周配置的对象
week: {
// 表示一周的第一天是星期一(周一),符合 ISO 8601 标准
dow: 1,
// 表示包含 1 月 4 日的周是该年的第一周
doy: 4,
},
// 定义日历头部按钮的文本
buttonText: {
// 上月按钮的文本
prev: '上月',
// 下月按钮的文本
next: '下月',
// 今天按钮的文本
today: '今天',
// 月视图按钮的文本
month: '月',
// 周视图按钮的文本
week: '周',
// 日视图按钮的文本
day: '日',
// 日程视图按钮的文本
list: '日程',
},
// 周视图的标题文本
weekText: '周',
// 全天事件的文本
allDayText: '全天',
// 一个函数,返回显示额外事件的文本。n 是额外事件的数量
moreLinkText: (n:any) => '另外 ' + n + ' 个',
// 没有事件时的显示文
noEventsText: '没有事件显示',
});
}
第二步 插件初始化
设置成中文后这个时候我们就需要把我们的日历渲染到界面上了
具体代码如下:
// 插件初始化后的实例
let _calendar=null
// 创建日历
function createCalendar() {
// 获取日历容器
const _calendarEl = document.querySelector("#calendar");
_calendar = new window.FullCalendar.Calendar(_calendarEl, {
// 全局配置
// 允许用户编辑事件(拖动和调整大小)
editable: true,
// 不显示全天事件栏
allDaySlot: false,
// 使用本地时区
timeZone: "local",
// 使用简体中文本地化
locale: "zh-cn",
// 初始视图模式为周视图
initialView: "timeGridWeek",
// 不显示头部工具栏
headerToolbar: null,
// 允许监听窗口大小变化 以改变日历的大小
handleWindowResize: true,
views: {
// 针对于周视图的配置 (可覆盖全局配置)
// 对于年视图、月视图、日视图也可以在 views 中单独配置,数据都是一样的,只不过可以用年份展示也可以用月份展示等
timeGridWeek: {
// 时间间隔默认为30分钟
slotDuration: "00:05:00",
// 日期视图左侧时间轴多长间隔显示一条日期文字(默认跟着slotDuration走的,可自定义)
slotLabelInterval: "00:30:00",
// 每天的开始时间
slotMinTime: "05:00:00",
// 每天的结束时间
slotMaxTime: "21:00:00",
// 自定义每个时间段的高度
// slotHeight: 24,
// 是否显示周末
weekends: true,
// 周/日视图中显示今天当前时间点(以红线标记),默认false不显示
nowIndicator: false,
// 隐藏一周中的某一天或某几天 0代表周末
hiddenDays:[0,1],
// 是否在日历中显示周次(一年中的第几周)
weekNumbers: false,
// 重叠数据条数太多时,限制各自里显示的数据条数(多余的以“+2more”格式显示),默认false不限制,支持输入数字设定固定的显示条数
eventLimit: true,
// 当一块区域内容太多以"+2 more"格式显示时,这个more的名称自定义
eventLimitText : "更多",
// 点开"+2 more"弹出的小窗口标题,与eventLimitClick可以结合用
dayPopoverFormat : "YYYY年M月d日",
// 在Event Object中如果没有end参数时使用,如start=7:00pm,则该日程对象时间范围就是7:00~8:00
defaultTimedEventDuration : "01:00:00",
// 自定义日历横向头部标
dayHeaderContent: dayHeaderContentCallback,
// 初始化事件内容回调
eventContent: eventContentCallback,
// 在视图添加到 DOM 后立即调用
eventDidMount: eventDidMountCallback,
// 调整事件时间间隔回调
eventResize: eventChangeCallback,
// 拖动完成事件回调
eventDrop: eventChangeCallback,
},
},
// 事件数据 (初始化时自动调用此函数获取日历对应的数据)
events: initEventDataCallback,
});
// 开始渲染
_calendar.render();
// 如果容器能滚动将每次滚动至开始时间
_calendar.scrollToTime("05:00:00");
},
这里需要强调的是创建中定义的几个回调函数的执行顺序
initEventDataCallback
>>dayHeaderContentCallback
>>eventContentCallback
>>eventDidMountCallback
比较特殊的是eventChangeCallback
,他是在渲染完成后,用户拖动时间块或者下拉时间块改变结束时间的位置才会触发的
具体代码如下:
// 讲后端数据转换为FullCalendar插件所需要的数据,并给到插件
function initEventDataCallback(dateInfo, success, fail) {
// 这里假设 schedulingDataList 是从后端获取的数据
const data = []
// FullCalendar插件所需要的数据
const eventList = []
data.forEach((item) => {
// 利用dayjs这个库获取星期几对应的日期
const startDate = dayjs(dateInfo.start)
.day(~~item.day_no)
.format("YYYY-MM-DD");
const _start = dayjs(`${startDate} ${item.start_time}`)
.second(0)
.format("YYYY-MM-DD HH:mm:ss");
const _end = dayjs(`${startDate} ${item.end_time}`)
.second(0)
.format("YYYY-MM-DD HH:mm:ss");
const event = {
// 事件唯一标识 可通过 _calendar 的 `getEventById` 方法获取事件
id: '',
// 事件标题,途中会经过 `eventContent` 回调来进行处理自定义的时间块显示内容
title: '',
// 事件开始时间 注意格式为 `YYYY-MM-DD HH:mm:ss`
start: _start,
// 事件结束时间 注意格式为 `YYYY-MM-DD HH:mm:ss`
end: _end,
// 是否允许拖动
editable: false,
// 是否允许拖动开始时间 覆盖全局的 eventStartEditable 选项
startEditable: false,
// 用来同时指定 backgroundColor 和 borderColor
color: "#d3d3d3",
// 是否允许与其他时间块重叠
overlap: true,
// 是否允许调整时间块的大小,也就是是否能够拖动边缘来改变时间间隔,覆盖全局的 eventResourceEditable 选项
durationEditable: false,
// 添加自定义类名样式,也可以使用 `eventClassNames` 回调实现
className: "calendar-event",
classNames: ["calendar-event"],
// 自定义属性 可从 `_calendar.getEventById('xxx').extendedProps` 中获取
userName: item.user_name,
};
eventList.push(event)
});
// 成功回调,会将这些数据渲染到日历上
success(eventList);
},
// 自定义日历横向头部标回调函数
function dayHeaderContentCallback(arg) {
const _weekDaysHeaders = [
"日",
"一",
"二",
"三",
"四",
"五",
"六",
];
return `周${_weekDaysHeaders[arg.dow]}`;
},
// 事件内容回调函数,在这里面可以自定义时间块的显示内容,除了传递html外,还可以传递dom元素
function eventContentCallback(arg, createElement) {
return {
html: `<div id="timeText-${arg.event.id}">${arg.timeText}</div>`,
};
},
// 点击时间文本的回调函数
function eventTimeTextClickCallback(event) {
console.log(event);
},
// 事件渲染完成后的回调函数,此时 `eventContentCallback` 中的自定义元素已经渲染到页面上了
function eventDidMountCallback(e) {
const { event, el } = e;
// 将每个时间块的dom元素存储到事件的 `extendedProps` 中,`extendedProps` 是专门用于存放你定义的自定义属性的
event.setExtendedProp("el", el);
// 拿到时间块的id(唯一标识)
const _id = event.id;
// 获取并绑定 `eventContentCallback` 回调中所自定义元素的点击事件
const _timeTextEl = document.getElementById(`timeText-${_id}`);
// 如果存在时间元素
if (_timeTextEl) {
if (!_timeTextEl?.fn) {
_timeTextEl.fn = eventTimeTextClickCallback.bind(this, event);
}
_timeTextEl.removeEventListener("click", _timeTextEl.fn);
_timeTextEl.addEventListener("click", _timeTextEl.fn);
}
},
// 拖拽和调整事件时间间隔大小回调函数
function eventChangeCallback(e) {
console.log('e:',e);
// 是否回退操作
const isRevert = false
// 例如在拖动或者下拉完毕后,发现时间块与其他时间块重叠,可以回退操作
if (isRevert) {
e.revert();
return;
}
// 可以设置动态设置 _calendar 初始化时的全局配置,例如这里修改了 `dayHeaderContent` 的回调函数,这个时候会立即调用回调函数
_calendar.setOption('dayHeaderContent', ()=> console.log('e:',e));
},

ok这个时候理论上界面上就应该会显示日历了
**第三步 动态更新**
我们通常都会去对日历进行交互操作,这个时候就需要把日历所对应的数据也进行同步,下面是我用到的一些方法,可能不是很完整你们可做参考
// 添加新的时间块
const newEvent = {
id: '',
title: '',
start: '2024-11-02 05:00:00',
end: '2024-11-02 22:00:00',
className: "xxx",
// ... 还可以添加其他属性
};
_calendar.addEvent(newEvent);
// 添加多个新的时间块
_calendar.addEventSource([newEvent,newEvent])
// 根据时间块的唯一标识id获取对象配置
const _event = _calendar.getEventById('');
// 设置开始时间和结束时间
_event.setDates('2024-11-02 05:00:00', '2024-11-02 22:00:00');
// 设置时间块对象的属性值,例如 color 等
_event.setProp("backgroundColor", "#43CF7C");
// 设置自定义属性的值
_event.setExtendedProp("xxx", 'xxx');
// 删除对应的时间块
_event.remove()
// 渲染日历
_calendar.render();
// 设置 _calendar 的配置
_calendar.setOption('dayHeaderContent', ()=> console.log('e:',e));
// 如果日历允许滚动,将会滚动到指定时间的位置
_calendar.scrollToTime('2024-11-02 05:00:00');
// 获取当前所有时间块的数据
_calendar.getEvents()
// 当前视图下对应今天的开始时间
_calendar.view.activeStart
// 当前视图下对应今天的结束时间
_calendar.view.activeEnd
// 销毁日历实例
_calendar.destroy()
ok终于讲完了,通过这些方式,已经能够定制属于你的个性化日历了,希望能够帮助到你!