fullcalendar是個很強大的日歷控件,可以用它進行排班、排會議、拍任務,很直觀,用戶體驗良好。
看下效果圖:
#parse("index/head.vm") <link rel="stylesheet" href="$!{rc.contextPath}/modules/devappwithfullcanlendar/css/mainstructure.css"> <link rel="stylesheet" href="$!{rc.contextPath}/modules/devappwithfullcanlendar/css/maincontent.css"> <!-- Jquery and Jquery UI --> <script type="text/javascript" src="$!{rc.contextPath}/modules/devappwithfullcanlendar/js/jquery-1.4.2.min.js"></script> <script type="text/javascript" src="$!{rc.contextPath}/modules/devappwithfullcanlendar/js/jquery-ui-1.8.6.custom.min.js"></script> <script type="text/javascript" src="$!{rc.contextPath}/modules/devappwithfullcanlendar/js/jquery-ui-timepicker-addon.js"></script> <link rel="stylesheet" href="$!{rc.contextPath}/modules/devappwithfullcanlendar/css/redmond/jquery-ui-1.8.1.custom.css"> <!-- Jquery and Jquery UI --> <script src="$!{rc.contextPath}/modules/devappwithfullcanlendar/js/formValidator/js/jquery.validationEngine.js" type="text/javascript"></script> <script src="$!{rc.contextPath}/modules/devappwithfullcanlendar/js/formValidator/js/jquery.validationEngine-en.js" type="text/javascript"></script> <link rel="stylesheet" href="$!{rc.contextPath}/modules/devappwithfullcanlendar/js/formValidator/css/validationEngine.jquery.css" type="text/css" media="screen" charset="utf-8" /> <!-- FullCalender --> <link rel='stylesheet' type='text/css' href='$!{rc.contextPath}/modules/devappwithfullcanlendar/js/fullcal/css/fullcalendar.css' /> <script type='text/javascript' src='$!{rc.contextPath}/modules/devappwithfullcanlendar/js/fullcal/fullcalendar.js'></script> <BODY> <STYLE type=text/css>#loading { TOP: 0px; RIGHT: 0px } .tooltip { PADDING-BOTTOM: 25px; PADDING-LEFT: 25px; WIDTH: 160px; PADDING-RIGHT: 25px; DISPLAY: none; BACKGROUND: url(images/black_arrow.png); HEIGHT: 70px; COLOR: #fff; FONT-SIZE: 12px; PADDING-TOP: 25px; z-order: 100 } </STYLE> <DIV id=wrap> <SCRIPT type=text/javascript> $(document).ready(function() { $("#groupName").click(function(){ dialog({ content: 'url:$!{rc.contextPath}/duty/dutyGroup/lookup?txtId=groupId&txtName=groupName&from=canlendar', zIndex:20000, title: '選擇', lock: false, width: 300, height: 300, }); }); $("#reserveformID").validationEngine({ validationEventTriggers:"keyup blur", // 鍵盤按鍵觸發驗證 openDebug: true }) ; $("#addhelper").hide(); var calendar =$('#calendar').fullCalendar({ header:{ right: 'prev,next today', center: 'title', left: 'month,agendaWeek,agendaDay' }, monthNames: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"], monthNamesShort: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"], dayNames: ["周日", "周一", "周二", "周三", "周四", "周五", "周六"], dayNamesShort: ["周日", "周一", "周二", "周三", "周四", "周五", "周六"], today: ["今天"], firstDay: 1, buttonText: { today: '今天', month: '月', week: '周', day: '日', prev: '上一月', next: '下一月' }, theme: true, editable: false,//日歷項拖拽 allDaySlot : false, events: function(start, end , callback){//生成日歷 //alert(calendar.fullCalendar('getDate')); var events = []; $.ajax({ 'url':"$!{rc.contextPath}/duty/dutyScheduling/canlendarModel?time="+new Date().getTime(), 'data': { timeStart:$.fullCalendar.formatDate(start,"yyyy-MM-dd HH:mm:ss"), timeEnd:$.fullCalendar.formatDate(end,"yyyy-MM-dd HH:mm:ss") }, 'dataType': 'json', 'type': 'post', 'error': function(data){ alert("保存失敗"); return false; }, 'success': function(doc) { $(doc).each(function(i) { events.push({ sid: doc[i].id, uid: doc[i].id, title: 'Daily Scrum meeting', start: doc[i].startTime, end: doc[i].endTime, fullname: doc[i].groupName,// 此處改變,列表和明細都會改變 confname: doc[i].orderName, groupPersons: doc[i].personNames, confshortname: doc[i].orderName.substring(0, 1), confcolor: '#ff3f3f', confid: 'test1', allDay: false, topic: doc[i].orderName, description : doc[i].remark, id: 1 }); }); callback(events);// } }); }, dayClick: function(date, allDay, jsEvent, view) {// 單機日歷內空白 新增事件 var selectdate = $.fullCalendar.formatDate(date, "yyyy-MM-dd"); $( "#dutyDate" ).val(selectdate); // dutyDate $( "#reservebox" ).dialog({ autoOpen: false, height: 450, width: 400, title: '新增排班' + selectdate ,// 此處聲明title,會將reservebox中的title屬性覆蓋 modal: true, position: "center", draggable: true,// 可拖拽 beforeClose: function(event, ui) { $.validationEngine.closePrompt("#orderId"); $.validationEngine.closePrompt("#groupId"); $.validationEngine.closePrompt("#groupName"); }, buttons: {// 彈出窗右下角的按鈕 "關 閉": function() { $( this ).dialog( "close" ); }, "保 存": function() { if($("#reserveformID").validationEngine({returnIsValid:true})){ var orderId = $("#orderId").val(); var dutyDate = $("#dutyDate").val(); var groupId = $("#groupId").val(); var groupName = $("#groupName").val(); var remark = $("#remark").val(); var schdata = {orderId:orderId, dutyDate:dutyDate, groupId:groupId, groupName:groupName,remark:remark}; $.ajax({ 'url':"$!{rc.contextPath}/duty/dutyScheduling/save?time="+new Date().getTime(), 'data': schdata, 'dataType': 'json', 'type': 'post', 'error': function(data){ alert("保存失敗"); return false; }, 'success': function(data) { window.location.reload(); } }); } } } }); $( "#reservebox" ).dialog( "open" ); return false; }, timeFormat: 'HH:mm{ - HH:mm}', eventClick: function(event) {// 單機日歷內已有事件 var tempStart = $.fullCalendar.formatDate(event.start, "yyyy/MM/dd"); var tempEnd = $.fullCalendar.formatDate(event.end, "yyyy/MM/dd"); if(tempStart==tempEnd){//若在同一天,結束日期省略 var fstart = $.fullCalendar.formatDate(event.start, "yyyy/MM/dd HH:mm"); var fend = $.fullCalendar.formatDate(event.end, "HH:mm"); }else{ var fstart = $.fullCalendar.formatDate(event.start, "yyyy/MM/dd HH:mm"); var fend = $.fullCalendar.formatDate(event.end, "yyyy/MM/dd HH:mm"); } var schdata = {sid:event.sid, deleted:1, uid:event.uid}; $( "#reserveinfo" ).dialog({ autoOpen: false, height: 280, width: 400, modal: true, position: "center", draggable: true, buttons: {// 這里貌似不可以自定義添加其他按鈕 "close": function() { $( this ).dialog( "close" ); } } }); if(1==1||2==schdata.uid){ $("#reserveinfo").dialog("option", "buttons", { "關閉": function() { $( this ).dialog( "close" ); } }); } var showtopic = ''; if(event.topic.length>15){// 題目過長處理 showtopic = event.topic.substring(0, 15) + '...'; }else{ showtopic = event.topic; } //明細彈出窗 描述 $("#revdesc").html('<div style="font-weight:bold;color:#5383c2;border-bottom: 1px dotted #5383c2; padding: 3px 0px 3px;">'+showtopic+'</div>' +'<table style="width:100%;font-family:'+"宋體"+'; line-height:28px;"><tr height="40px;"><td colspan="2">' +'<div style="padding:0px 5px;color:#1d5987;font-weight:bold;font-size:9px; text-align:left; font-size:14px;background:#A4C3E3; ">' +'<span style="background:#ff3f3f;width:14px;height:14px;color:#E3E3E3;font-size:10px;position:relative;left:0;top:0;font-size:14px;">'+event.confshortname +'</span> '+event.confname+'值班'+event.fullname +'</div></td></tr>' +'<tr height="40px;"><td style="width:70px;color:#4b4b4b;">'+'值班人員'+'</td><td style="padding-left:10px;">'+event.groupPersons+'</td></tr>' +'<tr height="40px;"><td valign="top" style="width:70px;color:#4b4b4b;">'+'值班備注'+'</td><td style="padding-left:10px;"><textarea readonly rows=3 cols=35>'+event.description+'</textarea></td></tr>' +'</table>'); $( "#reserveinfo" ).dialog( { title: fstart + "-" + fend + " " + showtopic } ); $( "#reserveinfo" ).dialog( "open" ); return false; }, loading: function(bool) { if (bool) $('#loading').show(); else $('#loading').hide(); }, eventMouseover: function(calEvent, jsEvent, view) { var fstart = $.fullCalendar.formatDate(calEvent.start, "yyyy/MM/dd HH:mm"); var fend = $.fullCalendar.formatDate(calEvent.end, "HH:mm"); $(this).attr('title', fstart + " - " + fend + " " + calEvent.topic + " : " + calEvent.description); $(this).css('font-weight', 'normal'); $(this).tooltip({ effect:'toggle', cancelDefault: true }); }, eventMouseout: function(calEvent, jsEvent, view) { $(this).css('font-weight', 'normal'); }, eventRender: function(event, element) { var fstart = $.fullCalendar.formatDate(event.start, "HH:mm"); var fend = $.fullCalendar.formatDate(event.end, "HH:mm"); // Bug in IE8 // element.html('<a href=#>' + fstart + "-" + fend + '<div // style=color:#E5E5E5>' + event.title + "</div></a>"); }, eventAfterRender : function(event, element, view) { // alert($.fullCalendar.formatDate($('#calendar').fullCalendar('getView').start,"yyyy-MM-dd"),); var fstart = $.fullCalendar.formatDate(event.start, "HH:mm"); var fend = $.fullCalendar.formatDate(event.end, "HH:mm"); // element.html('<a href=#><div>Time: ' + fstart + "-" + fend + // '</div><div>Room:' + event.confname + '</div><div // style=color:#E5E5E5>Host:' + event.fullname + "</div></a>"); var confbg=''; if(event.confid==1){ confbg = confbg + '<span class="fc-event-bg">什么?</span>'; }else if(event.confid==2){ confbg = confbg + '<span class="fc-event-bg">什么?</span>'; }else if(event.confid==3){ confbg = confbg + '<span class="fc-event-bg">什么?</span>'; }else if(event.confid==4){ confbg = confbg + '<span class="fc-event-bg">什么?</span>'; }else if(event.confid==5){ confbg = confbg + '<span class="fc-event-bg">什么?</span>'; }else if(event.confid==6){ confbg = confbg + '<span class="fc-event-bg">什么?</span>'; }else{ confbg = confbg + '<span class="fc-event-bg">什么?</span>'; } var titlebg = '<span class="fc-event-conf" style="background:'+ event.confcolor +'">' + event.confshortname + '</span>'; // if(event.repweeks>0){ // titlebg = titlebg + '<span class="fc-event-conf" // style="background:#fff;top:0;right:15;color:#3974BC;font-weight:bold">R</span>'; // } if(view.name=="month"){ var evtcontent = '<div class="fc-event-vert"><a>'; evtcontent = evtcontent + confbg; evtcontent = evtcontent + '<span class="fc-event-titlebg">' + fstart + " - " + fend + titlebg + '</span>'; evtcontent = evtcontent + '<span>班次: ' + event.confname + ' 班組:' + event.fullname + '</span>'; //evtcontent = evtcontent + '<span>班組: ' + event.fullname + '</span>'; evtcontent = evtcontent + '</a><div class="ui-resizable-handle ui-resizable-e"></div></div>'; element.html(evtcontent); }else if(view.name=="agendaWeek"){ var evtcontent = '<a>'; evtcontent = evtcontent + confbg; evtcontent = evtcontent + '<span class="fc-event-time">' + fstart + "-" + fend + titlebg + '</span>'; evtcontent = evtcontent + '<span>' + event.confname + ' by ' + event.fullname + '</span>'; // evtcontent = evtcontent + '<span>' + event.fullname + '</span>'; evtcontent = evtcontent + '</a><span class="ui-icon ui-icon-arrowthick-2-n-s"><div class="ui-resizable-handle ui-resizable-s"></div></span>'; element.html(evtcontent); }else if(view.name=="agendaDay"){ var evtcontent = '<a>'; evtcontent = evtcontent + confbg; evtcontent = evtcontent + '<span class="fc-event-time">' + fstart + " - " + fend + titlebg + '</span>'; evtcontent = evtcontent + '<span>班次: ' + event.confname + '</span>'; evtcontent = evtcontent + '<span>班組: ' + event.fullname + '</span>'; evtcontent = evtcontent + '<span>組員: ' + event.groupPersons + '</span>'; evtcontent = evtcontent + '</a><span class="ui-icon ui-icon-arrow-2-n-s"><div class="ui-resizable-handle ui-resizable-s"></div></span>'; element.html(evtcontent); } }, eventDragStart: function( event, jsEvent, ui, view ) { ui.helper.draggable("option", "revert", true); }, eventDragStop: function( event, jsEvent, ui, view ) { }, eventDrop: function( event, dayDelta, minuteDelta, allDay, revertFunc, jsEvent, ui, view ) { if(1==1||2==event.uid){ var schdata = {startdate:event.start, enddate:event.end, confid:event.confid, sid:event.sid}; }else{ revertFunc(); } }, eventResizeStart: function( event, jsEvent, ui, view ) { alert('開始調整大小'); }, eventResize: function(event,dayDelta,minuteDelta,revertFunc) { if(1==1||2==event.uid){ var schdata = {startdate:event.start, enddate:event.end, confid:event.confid, sid:event.sid}; }else{ revertFunc(); } } }); // goto date function if($.browser.msie){// 轉到某一日 $("#calendar .fc-header-right table td:eq(0)").before('<td><div class="ui-state-default ui-corner-left ui-corner-right" style="border-right:0px;padding:1px 3px 2px;" >' +'<input type="text" id="selecteddate" size="10" style="padding:0px;"></div></td>' +'<td><div class="ui-state-default ui-corner-left ui-corner-right">' +'<a><span id="selectdate" class="ui-icon ui-icon-search">goto</span></a></div></td>' +'<td><span class="fc-header-space"></span></td>'); }else{ $("#calendar .fc-header-right table td:eq(0)").before('<td><div class="ui-state-default ui-corner-left ui-corner-right" style="border-right:0px;padding:3px 2px 4px;" >' +'<input type="text" id="selecteddate" size="10" style="padding:0px;"></div></td>' +'<td><div class="ui-state-default ui-corner-left ui-corner-right">' +'<a><span id="selectdate" class="ui-icon ui-icon-search">goto</span></a></div></td>' +'<td><span class="fc-header-space"></span></td>'); } $("#selecteddate").datepicker({ dateFormat:'yy-mm-dd', beforeShow: function (input, instant) { setTimeout( function () { $('#ui-datepicker-div').css("z-index", 15); }, 100 ); } }); $("#selectdate").click(function() { var selectdstr = $("#selecteddate").val(); var selectdate = $.fullCalendar.parseDate(selectdstr, "yyyy-mm-dd"); alert(selectdate.getFullYear()+"年"+selectdate.getMonth()+"月"+selectdate.getDate()+"日"); var now = new Date(); alert(now.getFullYear()+"年"+now.getMonth()+"月"+now.getDate()+"日");//月份0開始 $('#calendar').fullCalendar( 'gotoDate', selectdate.getFullYear(), selectdate.getMonth(), selectdate.getDate()); }); //var view = $('#calendar').fullCalendar('getView'); //alert("The view's title is " + view.title); }); </SCRIPT> <!-- 日歷背景 --> <DIV id=calendar></DIV> <!-- 明細彈出窗 --> <DIV id=reserveinfo title=Details> <DIV id=revtitle></DIV> <DIV id=revdesc></DIV></DIV> <!-- 新增彈出窗 --> <DIV style="DISPLAY: none" id=reservebox> <FORM id="reserveformID" action="$!{rc.contextPath}/duty/dutyScheduling/save"> <DIV class=sysdesc> </DIV> <DIV class=rowElem><LABEL>日期:</LABEL> <input type="text" id="dutyDate" name="dutyDate" class="ui-input Wdate validate[required]" readonly="readonly" onclick="WdatePicker({dateFmt:'yyyy-MM-dd'})" value="$!{dutyScheduling.dutyDate}" size=22 /> </DIV> <DIV class=rowElem><LABEL>班次:</LABEL> <select name="orderId" id="orderId" class=validate[required]> <option value="">請選擇班次</option> #foreach($!list in $!listDutyOrder) <option value="$!{list.id}">$!{list.orderName}</option> #end </select> </DIV> <DIV class=rowElem><LABEL>班組:</LABEL> <input class="ui-input" type="hidden" readonly="readonly" id="groupId" name="groupId" value="$!{dutyScheduling.groupId}" /> <input class="ui-input validate[required]" type="text" readonly="readonly" id="groupName" name="groupName" value="$!{dutyScheduling.groupName}" /> </DIV> <DIV class=rowElem><LABEL>備注:</LABEL> <TEXTAREA id="remark" rows=3 cols=43 name=remark>$!{dutyScheduling.remark}</TEXTAREA> </DIV> <DIV class=rowElem> </DIV> <DIV class=rowElem> </DIV> <DIV id=addhelper class=ui-widget> <DIV style="PADDING-BOTTOM: 5px; PADDING-LEFT: 5px; PADDING-RIGHT: 5px; PADDING-TOP: 5px" class="ui-state-error ui-corner-all"> <DIV id=addresult></DIV> </DIV> </DIV> </FORM> </DIV> </DIV> </BODY> </HTML>
后台取日歷上數據的canlendarModel方法(springMVC):
@ResponseBody @RequestMapping(value = "canlendarModel") public List<DutyScheduling> canlendarModel(ModelMap model,Date timeStart,Date timeEnd) throws Exception { List<DutyOrder> listDutyOrder = dutyOrderService.findAll(); model.put("listDutyOrder", listDutyOrder); Date[] dd = {timeStart,timeEnd}; List<DutyScheduling> listMonthDutySchedulings = dutySchedulingService.findBetweenTimes(dd); //String jsonListMonthDutySchedulings = JsonUtil.JsonFromObject(listMonthDutySchedulings); //model.put("listSize", listMonthDutySchedulings.size()); //model.put("jsonListMonthDutySchedulings", jsonListMonthDutySchedulings); return listMonthDutySchedulings; }
后台代碼沒什么,要點在於頁面上的
var calendar =$('#calendar').fullCalendar({
。。。
events: function(start, end , callback){//生成日歷
。。。。
}
}
此處start和end為當前加載進來的日歷的起始時間和結束時間,需要將這兩個時間傳到后台取數據。這里我之前翻了個錯誤,開始我同步取數據,即頁面加載時候,把當天所在月的數據都取出來。當前月份是顯示出來了,可是新的問題是頁面右上角的 prev: '上一月',next: '下個月',不知道怎樣再去跟后台交互了!查閱大量資料,終於被我發現這里問題所在,這個地方一定要異步去取,把當前日歷(一般顯示的是一個月加月前月后幾天)的起始、結束時間傳到后台。這樣做之后,點擊prev: '上一月',next: '下個月',它會自己再把新的月份的起始、結束時間傳到后台,重新加載var calendar =$('#calendar').fullCalendar({。。});。
此處還有個地方要注意,當加載完頁面之后,左上角有個按日期查詢當日排班(任務)的功能。如果選的日期是當前日歷月份(即桌面上顯示7*6的格子)范圍以內的,它會定位到直接定位到當天,而數據則是之前已經加載好的;如果選的日期不在當前日歷月份范圍以內的,它會再調用var calendar =$('#calendar').fullCalendar({。。});。傳新的時間去后台取數據,這一點設計的非常好。
events: function(start, end , callback){//生成日歷
。。。
start: doc[i].startTime,
end: doc[i].endTime,
}
上面這個是生成日歷項的地方,主要就是這兩個參數,直接決定在日歷上面顯示的任務的起始和結束時間,如果不在同一天,那就會跨格子顯示。
我傳的是一個String格式 yyyy-MM-dd HH:mm:ss 的日期,沒問題,fullcalendar可以識別。
另外fullcalendar還支持日歷的拖拽等,還有很多別的有意思的方法,還待研究。