使用Fullcalendar管理日程事件(增删改查拖放)
准备
本实例将要实现的功能:打开日程安排月视图,默认加载当前月视图的所有事件;点击视图中的任意日期,会弹出新增事件表单,输入事件相关信息后,保存即可;点击视图中的事件,会弹出修改事件表单,可对事件进行修改,也删除事件;我们也可以对视图中的事件进行拖放,拖放完毕也就改变了事件的时间。
注意本文提到的“事件”是指Fullcalendar日程安排事件内容。
本文涉及到的web技术有:
Vue + FullCalendar + Axios + Element-ui + PHP。
本文篇幅较长,建议最好边阅读边实际操作。
我们使用Axios作为Ajax请求模块,具体使用教程可以参考:《 Vue项目中使用Axios封装http请求
》,我们还用了 Element-ui
的Dialog、表单、日期时间拾取器等组件。
我们在上一节文章《 在Vue框架下使用Fullcalendar
》的基础上,新建Event.vue文件:
import FullCalendar from '@fullcalendar/vue' import dayGridPlugin from '@fullcalendar/daygrid' import timeGridPlugin from '@fullcalendar/timegrid' import interactionPlugin, { Draggable } from '@fullcalendar/interaction'; import '@fullcalendar/core/main.css'; import '@fullcalendar/daygrid/main.css'; import '@fullcalendar/timegrid/main.css'; export default { components: { FullCalendar }, data() { return { calendarPlugins: [ dayGridPlugin, timeGridPlugin, interactionPlugin ], header: { left: 'prev,next today', center: 'title', right: 'dayGridMonth,timeGridWeek,timeGridDay' }, evnetTime: { hour: 'numeric', minute: '2-digit', hour12: false }, calendarEvents: [], calendarEventDrop: info => { this.dropEvent(info); }, handleDatesRender: arg => { this.getEventsList(arg.view) }, dialogFormVisible: false, form: { title: null, start: null, end: null }, optTitle: '添加事件', } }, created() { // }, methods: { 获取事件列表 getEventsList(info) { }, handleDateClick(arg) { this.dialogFormVisible = true; this.optTitle = '新增事件'; this.form.title = '', this.form.id = '', this.form.start = arg.date; this.form.end = arg.date; }, handleEventClick(info) { info.el.style.borderColor = 'red'; this.dialogFormVisible = true; this.optTitle = '修改事件'; this.form = { id: info.event.id, title: info.event.title, start: info.event.start, end: info.event.end, }; }, //保存事件 saveEvent() { }, //删除事件 delEvent() { }, //拖动事件 dropEvent(info) { } } }
读取事件
我们希望每次载入Fullcalendar,以及切换日期的时候,会读取视图中的日期范围内的事件列表。Fullcalendar提供了事件源属性 events
,支持json,数组,回调函数等方式获取日程数据,但是如果要对数据进行修改会新增的时候处理起来比较麻烦了。而我们采用Fullcalendar的另一个方法函数 datesRender
,它的意思是当视图中的日期渲染时,回调函数。
我们在 data
中,回调 getEventsList()
handleDatesRender: arg => { this.getEventsList(arg.view) },
而 methods
中的 getEventsList()
代码如下:
getEventsList(info) { let params = { start: info.activeStart, end: info.activeEnd }; this.$get('events.php', params) .then((res) => { this.calendarEvents = res; }); },
大家一看就明白,我们使用了Axios发送get请求,参数就是当前视图中的开始事件和结束时间,获取events.php返回的数据,并将数据赋给 events
。
新增事件
当我们单击视图中的某一天时,触发日期点击事件: @dateClick="handleDateClick"
,我们在 handleDateClick()
中,弹出表单框,定义表单元素默认值。注意参数 arg
是一个内置对象,可以获取当前点击的日期等数据。
handleDateClick(arg) { this.dialogFormVisible = true; this.optTitle = '新增事件'; this.form.title = '', this.form.id = '', this.form.start = arg.date; this.form.end = arg.date; },
在弹出的dialog表单中,有日程事件的名称,起始和结束时间。当新增和编辑的时候我们共用这一个表单,在编辑表单时会显示删除按钮。
当点击表单中的“确定”按钮时,调用 saveEvent
:
saveEvent() { this.$post('events.php?action=save', this.form) .then((res) => { if (res.code === 0) { if (this.form.id === undefined || this.form.id == '') { //新增 this.form.id = res.id; this.calendarEvents.push(this.form); this.$message({ message: '新增成功!', type: 'success' }); } else { //修改 this.calendarEvents.forEach((item, index, arr) => { if (item.id == this.form.id) { arr[index].title = this.form.title arr[index].start = this.form.start arr[index].end = this.form.end } }); this.$message({ message: '修改成功!', type: 'success' }); } this.dialogFormVisible = false; } else { this.$message({ message: res.message, type: 'error' }); } }); },
使用Axios发送了一个post请求,根据返回数据和本地form对象中是否有id参数,如果没有则是新增事件,直接 this.calendarEvents.push(this.form);
,就是往事件数组追加当前表单的数据,然后关闭Dialog,这时对应的日期内就会显示刚刚添加的事件。
修改事件
当点击视图中的某一个日程事件,会弹出Dialog,这时会触发 @eventClick="handleEventClick"
。
handleEventClick(info) { info.el.style.borderColor = 'red'; this.dialogFormVisible = true; this.optTitle = '修改事件'; this.form = { id: info.event.id, title: info.event.title, start: info.event.start, end: info.event.end, }; },
handleEventClick
自带 info
参数,可以根据该参数获取当前要修改事件的标题名称、起始和结束日期等数据。将这些数据赋值给form,并弹出Dialog。和新增事件一样,保存表单的时候也执行了 saveEvent
。因为修改事件时已知了事件的id,这个时候修改好的事件怎么替换原有的事件呢?我们使用 forEach
方法遍历事件数组,比对如果事件id与当前表单id相当时则修改事件名称和日期时间等,详情请看上面修改事件部分。
删除事件
点击修改事件弹出框Dialog时,左下角会出现“删除”按钮,点击该按钮,调用 delEvent()
:
delEvent() { this.$post('events.php?action=del', {id: this.form.id}) .then((res) => { if (res.code === 0) { this.calendarEvents.forEach((item, index, arr) => { if(item.id == this.form.id) { arr.splice(index, 1); } }); this.dialogFormVisible = false; this.$message({ message: '删除成功!', type: 'success' }); } else { this.$message({ message: res.message, type: 'error' }); } }); },
删除当前事件后,同样的我们用 forEach
遍历事件数组,使用 splice
将当前删除的事件从事件数组中剔除。
拖放事件
当按住视图中的某一个日程事件,拖动至另一个日期中,触发 @eventDrop="calendarEventDrop"
:
calendarEventDrop: info => { this.dropEvent(info); },
eventDrop
传递 info
参数给 this.dropEvent(info)
:
dropEvent(info) { this.form = { id: info.event.id, title: info.event.title, start: info.event.start, end: info.event.end }; this.saveEvent(); }
在拖动后,我们将 data
中的 form
对象的值改变,并调用 saveEvent()
保存拖动后的数据。
后端PHP
后端提供了与前端交互的API接口,PHP接收请求,并作出响应,Mysql提供数据存储查询功能。首先创建数据表:
CREATE TABLE `fullcalendar` ( `id` int(11) NOT NULL AUTO_INCREMENT, `title` varchar(128) NOT NULL, `start_time` int(10) NOT NULL DEFAULT '0', `end_time` int(10) NOT NULL DEFAULT '0', `created_at` datetime NOT NULL, `updated_at` datetime DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
events.php接收获取事件列表、新增、修改、删除事件的接口,具体代码如下:
require_once('conn.php'); $action = isset($_GET['action']) ? $_GET['action'] : ''; if ($action == 'save') { //添加|修改 $res['code'] = -1; $data = file_get_contents('php://input'); $post = json_decode($data, true); $title = htmlentities($post['title']); $start = htmlentities($post['start']); $end = htmlentities($post['end']); if (empty($end)) { $end = $start; } if (empty($title)) { $res['message'] = '名称不能为空!'; echo json_encode($res); exit; } $id = isset($post['id']) ? (int)$post['id'] : '0'; if ($id == 0) { //添加 $sql = "INSERT INTO `fullcalendar` (title,start_time,end_time,created_at) VALUES (:title,:start_time,:end_time,:created_at)"; $stmt = $db->prepare($sql); $stmt->execute([ ':title' => $title, ':start_time' => strtotime($start), ':end_time' => strtotime($end), ':created_at' => date('Y-m-d H:i:s') ]); $lastid = $db->lastInsertId(); if ($lastid > 0) { $res['id'] = $lastid; $res['code'] = 0; } } else { //修改 $sql = "UPDATE `fullcalendar` SET title=:title,start_time=:start_time,end_time=:end_time,updated_at=:updated_at WHERE id=:id"; $stmt = $db->prepare($sql); $stmt->execute([ ':title' => $title, ':start_time' => strtotime($start), ':end_time' => strtotime($end), ':updated_at' => date('Y-m-d H:i:s'), ':id' => $id ]); $res['code'] = 0; } echo json_encode($res); } elseif ($action == 'del') { //删除 $res['code'] = -1; $data = file_get_contents('php://input'); $post = json_decode($data, true); $id = isset($post['id']) ? (int)$post['id'] : '0'; if ($id == 0) { $res['message'] = '非法参数'; echo json_encode($res); exit; } $sql = "DELETE FROM `fullcalendar` WHERE id=:id"; $stmt = $db->prepare($sql); $rs = $stmt->execute([ ':id' => $id ]); if ($rs) { $res['code'] = 0; } else { $res['message'] = '删除失败'; } echo json_encode($res); } else { $start = isset($_GET['start']) ? (int)$_GET['start']/1000 : 0; $end = isset($_GET['end']) ? (int)$_GET['end']/1000 : 0; $start = isset($_GET['start']) ? strtotime($_GET['start']) : 0; $end = isset($_GET['end']) ? strtotime($_GET['end']) : 0; $sql = "SELECT id,title,start_time,end_time FROM `fullcalendar` WHERE start_time>=:startTime AND end_timeprepare($sql); $stmt->execute([ ':startTime' => $start, ':endTime' => $end ]); $list = $stmt->fetchAll(PDO::FETCH_ASSOC); foreach ($list as $key => &$val) { $val['start'] = date('Y-m-d H:i:s', $val['start_time']); $val['end'] = date('Y-m-d H:i:s', $val['end_time']); $val['description'] = 'aaa'; } echo json_encode($list); }
完整的实例代码请下载源码查看。