又好久没有写博客了,这段时间用到了日期控件fullcalendar,今天来做个总结,方便以后使用:
<script type="text/javascript" src='calendar/js/jquery.min.js'></script> <script type="text/javascript" src='calendar/js/jquery-ui.min.js'></script> <script type="text/javascript" src='calendar/js/fullcalendar.min.js'></script>
在添加新的日程时候用到了fancybox的弹出框,还使用到了ajaxForm提交表单,因此引入了这两个js
<script type="text/javascript" src='calendar/js/jquery.fancybox.js'></script> <script type="text/javascript" src="calendar/js/jquery.form.min.js"></script>
如果用到其他的功能,那么在加入其他的js文件即可。
<link rel="stylesheet" type="text/css" href="calendar/css/jquery-ui.css"> <link rel="stylesheet" type="text/css" href="calendar/css/fullcalendar.css"> <link rel="stylesheet" type="text/css" href="calendar/css/fancybox.css">
主要的就这三个,如果有其他要求可以自己根据需求添加。
在jsp页面上要有一个具体的位置来显示calendar控件
<div class="col-md-9">
<div id='calendar'></div>
</div>
用JQUERY选择上面的id为calendar的DIV进行fullcalendar初始化:
$(function() { $('#calendar').fullCalendar( { //options }); });
header : { left : 'prev,next today', center : 'title', right : 'month,agendaWeek,agendaDay' }
header定义日历顶部的标题和按钮,如果header设置为false,那么将不显示日历上方的标题和按钮,还可以设置的两个参数为:precYear和nextYear。
editable为boolean类型,决定在日历中的事件是否可以更改,例如从一个日期拖拽到另一个日期,或者修改事件时间长短。
editable为true,那么事件可以同时进行日历内部拖拽和设置事件时间长短,如果设置为false,那么两者都不可以实现。
如果editable为false,并且要可以设置事件拖拽,那么可以将eventStartEditable设置为true;如果editable为false,并且要可以设置事件时间长短,那么可以将eventDurationEditable设置为true。
editable: true,
theme: true,
theme决定是否使用JQUERY UI的Theme ,boolean类型,如果设置为true,在使用JQUERY UI的theme的同时还需要引入需要的其他的CSS文件。
dragOpacity: { agenda: .5, '':.6 },
dragOpacity表示事件拖动时候的不透明度,agenda表示对于agenda视图,’’表示其他视图。
eventDrop是事件从一个日期时间拖拽到另一个日期时间后的回调函数。
function (event,delta,revertFunc,jsEvent,ui,view) {}
event:包含拖拽的事件信息(名称、时间等)的事件对象;
delta:拖拽对象,包含事件移动的时间信息,可以有dayDelta表示事件拖拽移动的天数,minuteDelta表示移动的分钟数;
revertFunc:是一个函数,表示如果ajax请求失败,那么就把事件返回到拖拽之前的位置;
allDay 如果是月视图,或者是agenda视图的全天日程,此值为true,否则为false;
/****** drag the event from a time and drop it to a different time ******/ eventDrop: function(event,dayDelta,minuteDelta,allDay,revertFunc) { var url = "。。。。。"; $.post(url,{id:event.id,days:dayDelta,minutes:minuteDelta}, function(msg){ if(msg != 1){ alert(msg); revertFunc(); } }); },
使用AJAX将参数传递到后台,如果事件拖拽成功,那么久将成功后的事件的信息存储到数据库,如果失败返回给前台提示信息,并且将拖拽的事件返回到原来的位置,此处所说的拖拽是指在控件内部的拖拽,例如将一个事件从8月7号拖拽到8月10号。
action实现:
private Integer id; private int days; private int minutes; /*get/set*/ /** * @return "1" returns if the deploy succeed or "failed" * @throws IOException */ public String drag() throws IOException { response.setContentType("text/plain"); PrintWriter out = response.getWriter(); boolean success = agendaServ.drag(id, days, minutes); if(success){ out.print("1"); } else { out.print("failed"); } out.flush(); out.close(); return null; }
service实现:
/** * change the date of agenda by drag the extends to different date * @return */ public boolean drag(Integer id, int days, int minutes) { long diff = days * 86400000; Agenda agenda = agendaDAO.findById(id); if(!agenda.isAllDay()){ diff += minutes * 60000; } // add the changed time to start and end Date start = agenda.getStart(); start.setTime(start.getTime() + diff); agenda.setStart(start); Date end = agenda.getEnd(); if(end != null){ end.setTime(end.getTime() + diff); agenda.setEnd(end); } // update return agendaDAO.update(agenda); }
在Service层中需要判断事件是否是全天事件,如果不是全天事件,那么需要将传递过来的天数和分钟数全部转换成毫秒数,原来的时间加上移动的时间才是移动之后的时间,不管是开始时间还是结束时间都需要处理,处理完成之后使用持久化层修改该事件即可。
eventResize为设置事件时间长短的回调函数;
eventResize函数的参数和eventDrop函数的参数一样,需要知道更改的天数和分钟数,通过AJAX请求修改事件,具体实现如下:
/****** change the event duration ******/ eventResize: function(event,dayDelta,minuteDelta,revertFunc) { var url = "/wldproject/agenda/resize"; $.post(url,{id:event.id,days:dayDelta,minus:minuteDelta},function(msg){ if(msg != 1){ alert(msg); revertFunc(); } }); },
action实现:
private Integer id; private int days; private int minutes; /*get/set*/ public String resize() throws IOException { response.setContentType("text/plain"); PrintWriter out = response.getWriter(); boolean success = agendaServ.resize(id, days, minutes); if(success){ out.print("1"); } else { out.print("failed"); } out.flush(); out.close(); return null; }
service实现:
/** * change the date of agenda by resize the extends * @return */ public boolean resize(Integer id, int days, int minutes) { long diff = days * 86400000; Agenda agenda = agendaDAO.findById(id); if(agenda.isAllDay()){ diff += minutes * 60000; } // add the changed time to end date without start Date end = agenda.getEnd(); if(end != null){ end.setTime(end.getTime() + diff); } else { Date start = agenda.getStart(); end = new Date(start.getTime() + diff); } agenda.setEnd(end); // update return agendaDAO.update(agenda); }
允许用户点击选中一天或者拖拽选中多天,boolean类型,设置为true时才能选中。
选中一天或者多天时,具有select和unselect回调函数。
selectable设置为true时,表示可以选中一天或者多天,select为此时的回调函数;
/****** select sevral days ******/ selectable: true, select: function(startDate, endDate, allDay, jsEvent, view ){ var start = $.fullCalendar.formatDate(startDate,'yyyy-MM-dd'); var end = $.fullCalendar.formatDate(endDate,'yyyy-MM-dd'); if(start == end){ end=""; } var params = { "action" : "add", "fromdate" : start, "todate" : end, "allDay" : allDay, }; var url = '。。。。。?' + $.param(params); $.fancybox({ 'type':'ajax', 'href':url, }); },
startDate:开始时间;
endDate: 结束时间;
allDay: 布尔类型,是否是全天事件;
$.fullCalendar.formatDate(startDate,'yyyy-MM-dd')格式化事件的开始时间和结束时间。
选中一天或者多天之后进行添加事件,使用fancybox的方式弹出添加页面,弹出时需要将开始时间、结束时间等参数传递到弹出的页面上,弹出页面效果如下:
提交表单,关闭弹出的窗口,刷新父窗口页面即可,此处使用ajaxForm异步提交表单:
//提交表单 $("#add_form").ajaxForm({ beforeSubmit: showRequest, //表单验证 success: showResponse //成功返回 });
function showRequest(){ var events = $("#event").val(); if(events == ""){ alert("请输入日程内容!"); $("#event").focus(); return false; } if(confirm("请确定?")){ } else { return false; } } function showResponse(responseText, statusText, xhr, $form){ if(statusText=="success") { if(responseText == "1") { $.fancybox.close(); //重新获取所有事件数据 $("#calendar").fullCalendar("refetchEvents"); } else { alert(responseText); } } else { alert(statusText); } }
private String action; private Integer id; private String islong; private String title; private String allday; private String user; private String supporter; private String level; private String start; private String s_hour; private String s_minute; private String end; private String e_hour; private String e_minute; /*get/set*/ private String add() throws IOException { boolean success = false; Agenda agenda = new Agenda(); agenda.setTitle(title); agenda.setStartTime(start, allday, s_hour, s_minute); if(islong.equals("1")){ agenda.setEndTime(end, allday, e_hour, e_minute); } agenda.setColor(level); agenda.setBoolday(allday); agenda.setUser(user); agenda.setSupporter(supporter); if(action.equals("edit")){ agenda.setId(id); success = agendaServ.update(agenda); } else if(action.equals("add")){ success = agendaServ.add(agenda); } if(success){ return "1"; } return "add failed!!!"; }
之所以是修改和新增在一个方法中是因为弹出窗口是一个页面,根据传递过去的参数action的值判断是新增还是修改,弹出窗口页面如下:
<%@ page language="java" pageEncoding="utf-8"%> <%@ page import="java.net.URLDecoder"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <% String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + "/"; // 新建还是进行修改,若未修改,获取ID String header = "新建事件"; String id = ""; String action = request.getParameter("action"); if(action.equalsIgnoreCase("edit")){ header = "编辑事件"; id = request.getParameter("id"); } // 事件title String title = request.getParameter("title"); if(title == null){ title = ""; } else { title = URLDecoder.decode(title,"UTF-8"); } // 事件开始时间 String fromdate = request.getParameter("fromdate"); // 事件结束时间----判定是否多天事件 String todate = request.getParameter("todate"); String endStyle = "display:none"; String longDayStyle = ""; String longValue = "0"; if(todate != null && !todate.equals("")){ endStyle = ""; longDayStyle = "checked"; longValue = "1"; } // 全天事件判定 String timeStyle = "display:none"; String allDayStyle = "checked";//default all day event String allDay = request.getParameter("allDay"); String dayValue = "1"; if(allDay != null && allDay.equals("false")){// not all day event timeStyle = "display:"; allDayStyle = ""; dayValue = "0"; } %> <!DOCTYPE html> <html> <head><base href=<%=basePath%>></head> <body> <div class="container" style="width:450px"> <h3 class="fancy-head"><%=header%></h3> <form id="add_form" class="form-horizontal" action="0000" method="post"> <!-- 日程等级格式 --> <div class="form-group has-warning"> <label class="col-sm-3 control-label">日程等级:</label> <div class="col-sm-8"> <select class="form-control" name="level"> <option value="black">默认等级</option> <c:forEach var="level" items="${ levelArr }"> <option value="${ level.color }">${ level.name }</option> </c:forEach> </select> </div> </div> <!-- 日程内容格式 --> <div class="form-group has-warning"> <label for="event" class="col-sm-3 control-label">日程内容:</label> <div class="col-sm-8"> <input type="text" class="form-control" name="title" id="event" value="<%=title%>" placeholder="记录你将要做的一件事..."> </div> </div> <!-- 计划人员 --> <div class="form-group has-warning"> <label for="user" class="col-sm-3 control-label">计划人员:</label> <div class="col-sm-8"> <select class="form-control" name="user"> <c:forEach var="user" items="${ userArr }"> <option>${user.name}</option> </c:forEach> <option>其他</option> </select> </div> </div> <!-- 提交人员 --> <div class="form-group has-warning"> <label for="supporter" class="col-sm-3 control-label">提交人员:</label> <div class="col-sm-8"> <select class="form-control" name="supporter"> <c:forEach var="user" items="${ userArr }"> <option>${ user.name }</option> </c:forEach> <option>其他</option> </select> </div> </div> <!-- 开始时间格式 --> <div class="form-group has-warning"> <label for="start" class="col-sm-3 control-label">开始时间:</label> <div class="col-sm-8"> <input type="text" class="date datepicker" name="start" value="<%=fromdate%>"> <span id="sel_start" style="<%=timeStyle%>"> <select name="s_hour"> <c:forEach var='hour' begin='00' end='23'> <option>${hour}</option> </c:forEach> </select> <label>时</label> <select name="s_minute"> <option>00</option> <c:forEach var='minute' begin='10' end='50' step='10'> <option>${minute}</option> </c:forEach> </select> <label>分</label> </span> </div> </div> <!-- 结束时间格式 --> <div class="form-group has-warning" id="p_endtime" style="<%=endStyle%>"> <label for="end" class="col-sm-3 control-label">结束时间:</label> <div class="col-sm-8"> <input type="text" class="date datepicker" name="end" value="<%=todate%>"> <span id="sel_end" style="<%=timeStyle%>"> <select id="e_hour" name="e_hour"> <c:forEach var='hour' begin='00' end='23'> <option>${hour}</option> </c:forEach> </select> <label for="e_hour">时</label> <select id="e_minute" name="e_minute"> <option>00</option> <c:forEach var='minute' begin='10' end='50' step='10'> <option>${minute}</option> </c:forEach> </select> <label for="e_minute">分</label> </span> </div> </div> <!-- 日程类型格式 --> <div class="form-group has-warning"> <label for="type" class="col-sm-3 control-label">日程类型:</label> <div class="col-sm-8"> <div class="checkbox"> <label><input type="checkbox" id="isallday" <%=allDayStyle%>>全天日程</label> <label><input type="checkbox" id="isend" <%=longDayStyle%>>多天日程</label> </div> </div> </div> <!-- 按钮组格式 --> <div class="form-group btn-action"> <div class="col-sm-3"> <% String okstr = "确定"; if(action.equalsIgnoreCase("edit")){ okstr = "修改"; %> <input type="button" class="btn btn-danger" id="del_event" value="删除"> <%}%> </div> <div class="col-sm-8"> <input type="submit" class="btn btn-success" value="<%=okstr%>"> <input type="button" class="btn btn-default" value="取消" onClick="$.fancybox.close()"> </div> </div> <!-- hidden fields used to submit --> <div class="form-group"> <input type="hidden" id="eid" name="id" value="<%=id%>"> <input type="hidden" name="action" value="<%=action%>"> <input type="hidden" id="allday" name="allday" value="<%=dayValue%>"> <input type="hidden" id="islong" name="islong" value="<%=longValue%>"> </div> </form> </div> </body> <script type="text/javascript"> $(function(){ /**********时间控件 time control************/ $(".datepicker").datepicker({ //regional: "zh-CN", dateFormat:"yy-mm-dd", timeFormat: 'hh:mm', stepHour: 1, stepMinute: 10, }); $("#isallday").click(function(){ if($("#sel_start").css("display")=="none"){ // all day event $("#sel_start,#sel_end").show(); $("#allday").val("0"); } else{ // not an all day event $("#sel_start,#sel_end").hide(); $("#allday").val("1"); } }); $("#isend").click(function(){ if($("#p_endtime").css("display")=="none"){ $("#islong").val("1"); $("#p_endtime").show(); } else{ $("#islong").val("0"); $("#p_endtime").hide(); } $.fancybox.resize();//调整高度自适应 }); //提交表单 $("#add_form").ajaxForm({ beforeSubmit: showRequest, //表单验证 success: showResponse //成功返回 }); //删除事件 $("#del_event").click(function(){ if(confirm("您确定要删除吗?")){ var eid = $("#eid").val(); var url = "。。。。。"; $.post(url,{id:eid},function(msg){ if(msg == '1'){//删除成功 alert("事件删除成功"); $.fancybox.close(); //从日程视图中删除该事件 $("#calendar").fullCalendar("removeEvents",eid); } else{ alert(msg); } }); } }); }); function showRequest(){ var events = $("#event").val(); if(events == ""){ alert("请输入日程内容!"); $("#event").focus(); return false; } if(confirm("请确定?")){ } else { return false; } } function showResponse(responseText, statusText, xhr, $form){ if(statusText=="success") { if(responseText == "1") { $.fancybox.close(); //重新获取所有事件数据 $("#calendar").fullCalendar("refetchEvents"); } else { alert(responseText); } } else { alert(statusText); } } </script> </html>
这里面带着删除一起给了,其实删除是最简单的一个。
点击日历控件中的一个事件时的回调函数,比如8月10号有一个日程是开会,那么当你点击开会这个事件时就会通过fancybox弹出窗口,将开会这个事件的详细信息回显到弹出的窗口上:
/******edit the event created ******/ eventClick: function(calEvent, jsEvent, view) { var s_Date = $.fullCalendar.formatDate(calEvent.start,'yyyy-MM-dd'); var e_Date = ""; if(calEvent.end){ e_Date = $.fullCalendar.formatDate(calEvent.end,'yyyy-MM-dd'); } var params = { "id" : calEvent.id, "title" : encodeURI(calEvent.title), "action" : "edit", "allDay" : calEvent.allDay, "fromdate" : s_Date, "todate" : e_Date }; var url = '000000/event.jsp?' + $.param(params); $.fancybox({ 'type':'ajax', 'href':url, }); }
在event.jsp页面上接收参数,处理后显示在文本框中,提交表单即可修改,修改和新增在一个方法中。
events是作为加载日历中的事件的数据源,可以直接给一个数组作为数据源,可以通过后台传递到前台的json对象。Events可以放在eventSources中使用。
此处通过加载后台传递过来的json数据加载数据:
/****** load all events ******/ events: { url: "00000", type: 'post', error: function() { alert('获取日程事件失败!'); }, //color:'yellow',// 背景色 //textColor:'black'// 文字颜色 }
// 自带参数 private long start; private long end; /** * get the JSON String to display the events * * @return */ public String loadJSON() { String json = agendaServ.loadSpan(start, end); response.setContentType("text/plain"); response.setCharacterEncoding("utf-8"); PrintWriter out = null; try { out = response.getWriter(); out.print(json); } catch (IOException e) { e.printStackTrace(); } finally { if (out != null) { out.flush(); out.close(); } } return null; }
如果需要向后台传递参数,即带着过滤条件加载数据,可以使用如下的方式:
为了实现当前用户不能看到其他用户的非公开日程的功能,在后台加载数据的时候进行了一下过滤,即从session中拿到当前用户,然后遍历查询到的日程,从中拿到非公开日程,如果日程的用户跟当前用户不一致,那么设置该日程的标题为个人日程。
public String loadJSON() { List<Agenda> alist = agendaService.loadSpan(start, end); SysUser sysUser = SessionUtils.getSysUserFromSession(request); for(Agenda agenda:alist){ if(!agenda.isOpen()){ if(agenda.getUser().trim().equals(sysUser.getCnname().trim())){ continue; }else{ agenda.setTitle("个人日程"); } } } String json = GsonUtil.getJson(alist); //String json = agendaService.loadSpan(start, end); response.setContentType("text/plain"); response.setCharacterEncoding("utf-8"); PrintWriter out = null; try { out = response.getWriter(); out.print(json); } catch (IOException e) { e.printStackTrace(); } finally { if (out != null) { out.flush(); out.close(); } } return null; }
eventMouseover:用户鼠标放在事件上时的回调函数;
// Has not been complete because of the JQUERY dialog using manner // To display the detail message about the event eventMouseover: function(calEvent, jsEvent, view) { $("#agenda-content").text(calEvent.title); $("#agenda-user").text(calEvent.user); $("#agenda-start").text($.fullCalendar.formatDate(calEvent.start,'yyyy-MM-dd')); $("#win").dialog( { title: '日程详细内容', width: 250, height: 140, hide: "slide", position: [jsEvent.clientX, jsEvent.clientY], overlay: { backgroundColor: 'blue', opacity: 0 }, } ); },
eventMouseout:用户鼠标移除事件时的回调函数;
eventMouseout: function(calEvent, jsEvent, view) { $("#win").hide(); $("#win").dialog("destroy"); },
弹出的div代码:
<div class="container"> <div class="row" id="win" style="display:none"> <div class="input-group"> <span class="input-group-addon">日程内容:</span> <label id='agenda-content' class="form-control"></label> </div> <div class="input-group"> <span class="input-group-addon">提交人员:</span> <label id='agenda-user' class="form-control"></label> </div> <div class="input-group"> <span class="input-group-addon">开始时间:</span> <label id='agenda-start' class="form-control"></label> </div> </div> </div>
boolean类型,默认为false,表示是否允许从外部拖拽一个事件放到日历中,设置为true表示允许,回调函数为drop。
使用从外部拖拽时间到日历中的功能需要JQUERY UI draggables支持,即需要引入JQUERY UI的js文件。
拖拽一个外部事件到日历中时的回调函数;
首先需要存在外部事件才可以进行拖拽,在显示日历页面时,查询事件表中的所有的事件,传递到前台显示:
显示日历页面时,将事件表中的事件查出,发送到前台
public String execute() { List<Events> eventList = eventDAO.findAll(); List<User> userList = userDAO.findAll(); List<Level> levelList = levelDAO.findAll(); HttpSession session = request.getSession(); session.setAttribute("eventArr", eventList); session.setAttribute("userArr", userList); session.setAttribute("levelArr", levelList); return SUCCESS; }
前台显示:
<div class="input-group input-group-sm"> <input type="text" id="event-title" class="form-control" placeholder="增加相关事件.."> <span class="input-group-btn"> <button type="button" id="add-event" class="btn btn-success"> 增加 <i class="glyphicon glyphicon-plus"></i> </button> </span> </div> <div class="divide-30"></div> <fieldset id='external-events'> <legend>我的任务</legend> <div id="event-box"> <c:forEach var="event" items="${ eventArr }"> <div class='external-event'> <c:out value="${ event.title }"/> </div> </c:forEach> </div> <p> <input id='drop-remove' type='checkbox' class="uniform"/> <label for='drop-remove'>事件添加后删除</label> </p> </fieldset>
数据列表上方有新增按钮,可以在此页面上新增事件(事件和日程是不一样的表,事件表示还没有插入到日程中去,在日历控件中显示的为日程):
$('#add-event').click(function () { var title = $('#event-title').val(); addEvent(title); });
var addEvent = function (title) { if(title.length == 0){ alert("事件名称不能为空!!!"); return; } var url = "00000"; $.post(url, {'title':title}, function(msg){ if(msg != 1){ alert("事件添加失败\n" + msg); return; } var html = $('<div class="external-event">' + title + '</div>'); jQuery('#event-box').append(html); initDrag(html); }); };
将添加成功后的事件显示在任务列表中,但是为了完成从外部拖拽事件到日历中形成日程的需求,还需要对添加的事件以及显示在该区域的事件进行处理:
var initDrag = function (el) { // create an Event Object var eventObject = { // use the element's text as the event title title: $.trim(el.text()) }; // store the Event Object in the DOM element so we can get it later el.data('eventObject', eventObject); // make the event draggable using jQuery UI el.draggable({ zIndex: 999, revert: true, // will cause the event to go back to its revertDuration: 0 // original position after the drag }); };
$('#external-events div.external-event').each(function () { initDrag($(this)); });
使用JQUERY的data函数,将事件的id和标题放到eventObject中,当需要的时候,可以使用data("eventObject")取出需要的数据。
// this function is called when something is dropped drop: function(date, allDay) { var $obj = $(this); // retrieve the dropped element's stored Event Object var eventOBJ = $obj.data('eventObject'); var start = $.fullCalendar.formatDate(date,'yyyy-MM-dd HH:mm'); // submit the agenda to the background and save to the database var url = "000000"; $.post(url,{title:eventOBJ.title,date:start,allday:allDay},function(msg){ if(msg != 1){ alert(msg); revertFunc(); } else { // is the "remove after drop" checkbox checked? if ($('#drop-remove').is(':checked')) { // if so, remove the element from the "Draggable Events" list $obj.remove(); } //重新获取所有事件数据 $("#calendar").fullCalendar("refetchEvents"); } }); },
Date:事件拖拽到的位置日期;
allDay:是否是整天事件;
后台实现:
private String title; private String date; private boolean allday; public String drop() throws IOException { response.setContentType("text/plain"); PrintWriter out = response.getWriter(); boolean success = agendaServ.drop(title, date, allday); if(success){ out.print("1"); } else { out.print("failed"); } out.flush(); out.close(); return null; }
service实现:
/** * drag the event outside to the calendar and add it to database * @return */ public boolean drop(String title, String date, boolean allday) { Agenda agenda = new Agenda(title); agenda.setAllDay(allday); agenda.setStart(DateUtil.parse2Date(date, "yyyy-MM-dd HH:mm")); return agendaDAO.save(agenda); }
上面基本就是整个的fullcalendar的操作,我把前台和后台实现都放上来了,当然还有其他的功能,按照需求查API也是可以做出来的,如果有什么不对的地方欢迎大家指正。