背景
有一些应用系统或应用功能,如日程管理、任务管理需要使用到日历组件。虽然Element Plus也提供了日历组件,但功能比较简单,用来做数据展现勉强可用。但如果需要进行复杂的数据展示,以及互动操作如通过点击添加事件,则需要做大量的二次开发。
FullCalendar是一款备受欢迎的开源日历组件,以其强大的功能而著称。其基础功能不仅免费且开源,为开发者提供了极大的便利,仅有少量高级功能需要收费。然而,尽管该组件功能卓越,其文档却相对简洁,导致在集成过程中需要开发者自行摸索与探索,这无疑增加了不少学习和验证的时间成本。
为此,本专栏通过日程管理系统的真实案例,手把手带你了解该组件的属性和功能,通过需求导向的方式,详细阐述FullCalendar组件的集成思路和实用解决方案。
在介绍过程中,我们将重点关注集成要点和注意事项,力求帮助开发者在集成过程中少走弯路,提供有效的避坑指南,从而提升开发效率,更好地利用这款优秀的日历组件。
官网:https://fullcalendar.io/
环境Vue3+Element Plus+FullCalendar 6.1.11。
使用
增加右键菜单
功能需求
在前面的基础上,我们进一步增加业务功能,使其用起来更方便。
具体来说,就是为事件(对应任务)基础上增加右键菜单,能便捷的进行操作,如复制任务、删除任务、添加工时等。
先翻找官方文档,日历组件并没有预置右键菜单扩展,也没有相关说明,需要自行摸索实现。
实现思路——使用插槽
使用eventContent插槽,自行用div包起来,然后在div上添加@contextmenu事件,如下所示:
<div> <FullCalendar :options="calendarOptions" ref="fullCalendar"><template #eventContent="arg"><div @contextmenu="contextmenu"> {{ arg.event.title }} </div></template></FullCalendar></div>contextmenu(e) {e.preventDefault()console.log(e)}
上述方式可以显示事件的名称,但右键菜单的处理无效,浏览器的右键菜单还是会弹出来,推测组件又做了啥内部处理和封装导致的,contextmenu根本不会被调用。
在外侧放了一个div标签挂载了contextmenu做对比验证,发现能正常运行,基本确定是日历组件内部处理导致的。
<div><div @contextmenu="contextmenu"> 测试 </div><FullCalendar :options="calendarOptions" ref="fullCalendar"><template #eventContent="arg"><div @contextmenu="contextmenu"> {{ arg.event.title }} </div></template></FullCalendar></div>
因此,此路走不通。
此外,即使右键菜单能生效,该方案还存在一个问题,就是需要自己覆写日历组件的内容展现部分,意味着官方原先一些预置的属性和功能,如是否显示事件的起止时间、颜色和背景的控制等,将失去作用,因此,强烈不建议采用此思路。
实现思路——使用回调
组件预置了一个事件加载完成后的回调事件,我们可以在这个环节添加一个右键监听,然后阻止浏览器默认右键菜单,并调用自己的右键菜单。
// 事件加载完成
eventDidMount(arg) {//添加右键菜单arg.el.addEventListener('contextmenu', (e) => {//阻止浏览器的默认右键菜单e.preventDefault()this.showEventContextMenu(e, arg.event.id)})
}
调用右键菜单如下:
// 显示事件右键菜单
showEventContextMenu(mouseEvent, eventId) {// 保存当前事件标识this.contextMenuEventId = eventId// 显示右键菜单this.$nextTick(() => {this.eventContextMenu.left = mouseEvent.clientX - 10const menuHeight = this.$refs.eventContextMenu.$el.clientHeightconst areaHeight = document.documentElement.clientHeightif (mouseEvent.clientY + menuHeight > areaHeight) {// 当鼠标点击的y坐标加上菜单高度超出区域高度时this.eventContextMenu.top = mouseEvent.clientY - menuHeight + 25} else {this.eventContextMenu.top = mouseEvent.clientY - 25}// 显示菜单this.eventContextMenu.visible = true})
},
相应的右键菜单使用element-plus的菜单控件,如下:
<el-menuv-show="eventContextMenu.visible"ref="eventContextMenu":style="{width: '120px',left: eventContextMenu.left + 'px',top: eventContextMenu.top + 'px',position: 'fixed',cursor: 'pointer','z-index': 9999}"popper-append-to-body@mouseleave="eventContextMenu.visible = false"@select="eventContextMenuSelect"><el-menu-item index="addLog"><el-icon><CloseBold /></el-icon><span>记录用时</span></el-menu-item><el-menu-item index="copy"><el-icon><CopyDocument /></el-icon><span>复制任务</span></el-menu-item><el-menu-item index="remove"><el-icon><FolderRemove /></el-icon><span>删除任务</span></el-menu-item></el-menu>
这个过程中需要一些属性来保存和传递数据,如下:
// 当前右键菜单事件标识
contextMenuEventId: '',
// 事件右键菜单属性
eventContextMenu: {// 是否可见visible: false,// 左边距left: 0,// 上边距top: 0
}
效果如下: