背景
有一些应用系统或应用功能,如日程管理、任务管理需要使用到日历组件。虽然Element Plus也提供了日历组件,但功能比较简单,用来做数据展现勉强可用。但如果需要进行复杂的数据展示,以及互动操作如通过点击添加事件,则需要做大量的二次开发。
FullCalendar是一款备受欢迎的开源日历组件,以其强大的功能而著称。其基础功能不仅免费且开源,为开发者提供了极大的便利,仅有少量高级功能需要收费。然而,尽管该组件功能卓越,其文档却相对简洁,导致在集成过程中需要开发者自行摸索与探索,这无疑增加了不少学习和验证的时间成本。
为此,本专栏通过日程管理系统的真实案例,手把手带你了解该组件的属性和功能,通过需求导向的方式,详细阐述FullCalendar组件的集成思路和实用解决方案。
在介绍过程中,我们将重点关注集成要点和注意事项,力求帮助开发者在集成过程中少走弯路,提供有效的避坑指南,从而提升开发效率,更好地利用这款优秀的日历组件。
官网:https://fullcalendar.io/
环境Vue3+Element Plus+FullCalendar 6.1.11。
使用
拖放日历组件外元素至日志组件内(二)
解决方案
回过头细看官方说明,原来导入的是FullCalendar自身的Draggable组件,但是写法看上去又很奇怪,是JQuery模式的操作,如下:
document.addEventListener('DOMContentLoaded', function() {
var Calendar = FullCalendar.Calendar;
var Draggable = FullCalendar.Draggable;
var containerEl = document.getElementById('external-events');
var calendarEl = document.getElementById('calendar');
var checkbox = document.getElementById('drop-remove');
// initialize the external events
// -----------------------------------------------------------------
new Draggable(containerEl, {
itemSelector: '.fc-event'
});
// initialize the calendar
// -----------------------------------------------------------------
var calendar = new Calendar(calendarEl, {
headerToolbar: {
left: 'prev,next today',
center: 'title',
right: 'dayGridMonth,timeGridWeek,timeGridDay'
},
editable: true,
droppable: true, // this allows things to be dropped onto the calendar
drop: function(info) {
console.log(info)
// is the "remove after drop" checkbox checked?
if (checkbox.checked) {
// if so, remove the element from the "Draggable Events" list
info.draggedEl.parentNode.removeChild(info.draggedEl);
}
}
});
calendar.render();
});
尝试将其转化为vue的写法。
引入draggable组件:
import { Draggable } from '@fullcalendar/interaction'
在任务菜单外,使用div包裹,指定id为external-events,任务列表通过el-row附加v-for,指定class为dragElement,如下:
<div id="external-events">
<el-row
v-for="element in taskList"
:key="element.id"
class="dragElement"
>
<el-tag
closable
@close="remove(element.id)"
@click="modify(element.id)"
:title="element.name"
>
{{ element.name }}</el-tag
>
</el-row>
</div>
</el-card>
初始化加载数据的操作里,调用Draggable,这时候,需要通过 document.getElementById(‘external-events’)获取外层的div元素,并且通过itemSelector: '.dragElement’来获取可拖拽的元素,如下:
// 初始化
init() {
this.loadData()
},
loadData() {
this.$api.personaltask.task.listWithStatus('PENDING').then((res) => {
if (res.data) {
this.taskList = res.data
let containerEl = document.getElementById('external-events')
new Draggable(containerEl, {
itemSelector: '.dragElement',
eventData: function (eventEl) {
return {
title: eventEl.innerText,
duration: '00:30'
}
}
})
}
})
}
完成上述操作后,可以实现任务从外部拖放到日历组件内部了,当然,仅限于前端。
接下来,就是调用后端服务,将被拖动的任务,根据放到日历组件中的位置,设置其起止时间,并变更其状态为“未开始”。
drop回调事件中,能拿到几个重要属性如下:
allDay:是否全天事件;
date:拖放结束放置的位置所在的时间
draggedEl:被拖放的html元素
我们可以通过date获取开始时间,然后结合allDay来计算结束时间,如果是全天,开始时间+1天为结束时间;如果是非全天,开始时间+半小时(我们自行设置的默认持续时长)。
还有最关键的一点,是如何获取到被拖拽的任务的标识id,通过打印输出的方式,发现draggedEl元素里仅传递了任务标题,并没有传递id。
尝试了很多方式,最终是使用date-id这种定义属性,来放入任务标识:
<div id="external-events">
<el-row
v-for="element in taskList"
:key="element.id"
class="dragElement"
:data-id="element.id"
>
<el-tag
closable
@close="remove(element.id)"
@click="modify(element.id)"
:title="element.name"
>
{{ element.name }}</el-tag
>
</el-row>
</div>
然后在drop的回调方法中,通过arg.draggedEl.dataset.id的方式取出来,整个回调方法如下:
drop(arg) {
// 获取是否全天
const allDay = arg.allDay
// 获取开始时间
const start = arg.date
// 获取任务标识
const id = arg.draggedEl.dataset.id
let endTime = new Date(start)
if (allDay) {
//若为全天,结束时间为开始时间加1天
endTime = this.$dateFormatter.formatUTCTime(new Date(start.getTime() + 24 * 60 * 60 * 1000))
} else {
// 非全天,结束时间在开始时间基础上加半小时
endTime = this.$dateFormatter.formatUTCTime(new Date(start.getTime() + 30 * 60 * 1000))
}
const startTime = this.$dateFormatter.formatUTCTime(new Date(start.getTime()))
// 调用安排工作接口
this.$api.personaltask.task.assign(id, startTime, endTime).then(() => {
this.refresh()
})
}
最终,实现了将外部元素拖放到日历组件内部的功能。
应用系统
名称:遇见
地址:https://meet.popsoft.tech
说明:基于一二三应用开发平台和FullCalendar日历组件实现的面向个人的时间管理、任务管理系统,1分钟注册,完整功能,欢迎使用~