拖动引发的结束时间清空问题
需求背景
在本系列的前面文章里,我们实现了拖放事件来实现调用后端服务变更任务的起止时间功能,例如某个会议原本起止时间是8:00-9:00,可以通过拖放操作将其变更为9:30-10:30。对于只有开始时间,无结束时间的任务,也可以正常拖放和更新起止时间。
在实际使用过程中,会产生将事件在全天区域与非全天区域之间拖动的需求,业务场景如下:
通常,我们会制定一个粗略一些的周计划,将一些任务作为本周要完成的事项,但往往不会严格限定要在哪一天的哪个时间段来做。这时候我们就可以使用FullCalendar组件的全天区域的功能来达到目的。将这些任务,暂存和显示在顶部的全天区域内,在当天早晨进行任务安排的时候,再将其拖放到具体的时间段。
此外,还有一种场景,因为变动,原本已经安排了起止时间的任务无法按计划执行,且没有明确的新的计划,需要放回到“全天”这个池子里以便将来另行安排。
以上两个业务场景,涉及到周视图和日视图中的两个操作,将顶部的全天区域事件拖放到下面正常区域,以及将下面区域中的事件,拖放到顶部全天区域。
经测试,将事件在全天区域与非全天区域之间拖动,会导致结束时间被清空,这是FullCalendar组件内置的处理逻辑。同时,区域内部拖动则表现正常,如周一的全天事件,拖放到周二,或周四的8点-9点的会议拖放到周五,结束时间不会清空。
解决方案
如何解决呢?
需要改造的是拖放结束事件回调,既然FullCalendar组件会将结束时间置为空,那我们就自行来设置一个结束时间。
添加个输出,确认下回调事件的参数数据:
// 拖动结束
eventDrop(arg) {
console.log('1111111', arg.event.start, arg.event.end, arg.event.allDay)
this.changeTime(arg)
}
在两个区域内拖放,打印输出如下:
梳理逻辑如下:
通过allDay属性,可以确定拖放方向,若allDay为false,从全天到非全天,反之为从非全天到全天。
从全天到非全天,结束时间=开始时间+任务的计划时长。如计划时长也为空,计划时长默认设置为半小时。
从非全天到全天,结束时间=开始时间+1天。
上面逻辑看上去不复杂,但是,混合以下两个因素就麻烦了。
一是任务的结束时间可能本来就是空的。
二是拖动可能未发生跨区域的情况。
例如,一个周一的8点开始的会议,未设置结束时间,拖放到了周二8点,这时候拿到的allDay属性为false,按照上面梳理逻辑,会自动计算结束时间后赋值,这么做,系统的自动化处理一定程度相当于“改变”了用户原本的设置。
为了规避这点,调整实现逻辑,当计划时长为空时,不设置其默认值,将结束时间依然留空。
实现方案
最终实现逻辑代码如下:
// 拖动结束
eventDrop(arg) {
const allDay = arg.event.allDay
const plannedDuration = arg.event.extendedProps.plannedDuration
const start = arg.event.start
let end = arg.event.end
console.log('before', end)
if (allDay) {
// 拖动结束位于全天事件区域
if (end == null) {
// 拖动结束时间为空,则设置为开始时间+1天
end = new Date(start.getTime() + 24 * 60 * 60 * 1000)
}
} else {
// 拖动结束位于非全天事件区域
if (end == null && plannedDuration != null) {
// 拖动结束时间为空且计划时长不为空,则设置为开始时间+计划时长
end = new Date(start.getTime() + plannedDuration * 60 * 60 * 1000)
}
}
console.log('after', end)
arg.event.setEnd(end)
this.changeTime(arg)
}
以上增加了判断结束时间是否为空的逻辑,是过滤掉区域内移动的情况。
上面处理过程中需要用到任务的计划时长属性,这不是一个FullCalendar组件的事件对象自身属性,在加载数据环节,需要将该属性放到事件对象的扩展属性extendedProps中。
测试各种场景下的移动,区域内移动、区域间移动、结束时间有值、结束时间无值,均能正常处理。
并且当任务设置了计划时长的时候,从全天区域拖放到具体的开始时间,系统会自动将结束时间设置为开始时长和计划时长的和,更符合用户的期望。
可选方案
在摸索解决的过程中,发现了FullCalendar自带的一个属性allDayMaintainDuration。
其作用是确定一个事件的持续时间全天与非全天两个区域间拖动应该如何变化,该值默认为false,未开启状态。看说明这个参数跟我们期望实现的需求密切相关,于是动手验证了下。
当设置为false时(这是默认值),事件的持续时间将根据它被拖放到的部分进行重置。如果事件被拖放到全天区域,它的持续时间将重置为defaultAllDayEventDuration(可能是一天)。如果事件被拖放到非全天区域,它的持续时间将重置为defaultTimedEventDuration(可能是一个小时)。
官方说明是这样,但所谓的重置,实际只是前端显示看上去时一整体或一小时,仍会将事件对象中的结束时间属性清空掉,在进行调用后端服务持久化时导致结束时间丢失。
当设置为true时,事件在被拖放到全天区域或从全天区域拖出后,其持续时间将大致保持不变。这里所说的“大致”是因为如果一个事件的持续时间具有小时级的精度,它将被向下舍入到最近的整天。这意味着,如果一个事件原本跨越了数个小时但不是一整天,当它被拖放到全天区域时,它的持续时间将被调整为整个日历日。
测试了下效果,非全天事件拖放到全天区域,结束时间会自动变更为当天结束,不会置空;全天事件拖放到非全天区域,起止时间自动变成0点到24天,结束时间不会置空,但是一下占满当天所有时段,用户体验是比较差的,需要通过缩放操作调整起止时间。
该方式最大的优点就是简便,不需要进行复杂的二次开发,开启一个参数配置即可;最大的缺点就是用户体验较差,明显不如我们前面进行的二次扩展,结合了任务计划时长进行了自动化处理工作。
应用系统
名称:遇见
地址:https://meet.popsoft.tech
说明:基于一二三应用开发平台和FullCalendar日历组件实现的面向个人的时间管理、任务管理系统,1分钟注册,完整功能,欢迎使用~