[Sticky] 那种生活一辈子都不会腻吧

当我不再年轻了,我的儿女问我你年轻时曾经最爱的人是谁的时候。我不希望我要做的是翻出一本旧相册。我希望我能抬起手指着屋子另一头说:”她就坐在那里啊!“

fullCalendar - List view, custom columns(五)

以上四篇已经把代码贴全了,下面进行解释。

过程如下:

  1. 初始化一个 fullCalendar (initFullCalendar)

  2. 切换日视图 调用 viewRender function,此时会调用 changeView ,传递的参数是 fakeDate ,当前日期

  3. changeView 方法中会调用两个 ajax, 因为ajax 是异步,所以在 ajax 的回调中 调用另一个ajax ,然后在第二个 ajax 中 调用 render 方法。render 方法有三个参数, 当前日期、展示的events[],列数据[]。

  4. 遍历列数据[],计算出列数据的个数,记录下来,然后把列数据对应下的数据增加天数,时间不变( i=1 则增加1天 ),然后保存回 events[]。

  5. 在进行初始化 initFullCalendar1 ,传递参数三个, 分别是 列数据的个数、 events、 当前时间 fakeDate。

  6. 替换标题、重写新的 fullCalendar 按钮相关方法。


其实新的视图使用的是api提供的默认视图、展示默认天数的数据。我们计算出列数据长度之后生成一个展示列数据长度天数的视图,然后把events的天数对应加几天,此时events的分布就到了响应的列中(实际上系统认为第二列是第一列的后一天、以此类推),触发dayClick事件获取列的ID也在代码中注释了。

此方法可以解决问题,但是切换试图每次都要 destory 然后 init 页面就会闪动、影响用户体验,没有 月 和 周 视图切换的那么流畅。

fullCalendar - List view, custom columns(四)

function initFullCalendar1(i, data, fakeDate){
    $('#calendar').fullCalendar({
        header: {
            left: 'prev,next today',
            center: 'title',
            right: 'month,agendaWeek,agendaDay'
        },
        today: ["今天"],
        buttonText: {
            today: '今天',
            month: '月',
            week: '周',
            day: '日'
        },
        defaultDate:fakeDate,
        defaultView: 'agendaFourDay',
        events: data,
        views: {
            agendaFourDay: {
                type: 'agenda',
                duration: { days: i },
                buttonText: '4 day'
            }
        },
        viewRender: function(view, element){
            if(view.type != "agendaFourDay"){
                $('#calendar').fullCalendar('destroy');
                initFullCalendar(view.start, view.type);
            }
        },
        dayClick: function(date, jsEvent, view) {
            var startDate = $.fullCalendar.moment(date).format().replace(/T/g, " ");
            var ymd = $.fullCalendar.moment(fakeDate).format('YYYY-MM-DD');
            $("#startDate").val(ymd + " " + startDate.split(" ")[1]);
            var brId = $("#hideBrId_" + startDate.split(" ")[0] ).val();
            // brId 就是每列的ID
        },
        eventClick:function(event, jsEvent, view){

        }
    });

    // 移除 “today” 样式
    $(".fc-day.fc-widget-content.fc-today.fc-state-highlight").removeClass("fc-today");
    // 将 “日” 按钮设置为激活状态
    $(".fc-agendaDay-button.fc-button.fc-state-default.fc-corner-right").addClass("fc-state-active");

    // 重写 上一日 方法
    $(".fc-prev-button").off("click").on("click",function(){
        var moment = $('#calendar').fullCalendar('getDate').format('YYYY-MM-DD');
        var date = new Date(Date.parse(moment.replace(/-/g, "/")));
        var newDate = new Date(date.getTime() - (1000 * 60 * 60 * 24));
        $('#calendar').fullCalendar('destroy');
        var format = $.fullCalendar.moment(newDate).format('YYYY-MM-DD');
        changeView(format);
    })

    //重写 下一日 方法
    $(".fc-next-button").off("click").on("click",function(){
        var moment = $('#calendar').fullCalendar('getDate').format('YYYY-MM-DD');
        var date = new Date(Date.parse(moment.replace(/-/g, "/")));
        var newDate = new Date(date.getTime() + (1000 * 60 * 60 * 24));
        $('#calendar').fullCalendar('destroy');
        var format = $.fullCalendar.moment(newDate).format('YYYY-MM-DD');
        changeView(format);
    })

    // 判断当前是否是今日,如果是今日,则将今日按钮设置为激活、禁用状态
    var moment = $('#calendar').fullCalendar('getDate').format('YYYY-MM-DD');
    var date = new Date(Date.parse(moment.replace(/-/g, "/")));
    var format = $.fullCalendar.moment(date).format('YYYY-MM-DD');
    var format1 = $.fullCalendar.moment(new Date()).format('YYYY-MM-DD');
    if(format != format1){
        $(".fc-today-button").attr("disabled",false);
        $(".fc-today-button").removeClass("fc-state-disabled");
    }

    // 重写 今日 按钮方法
    $(".fc-today-button").off("click").on("click",function(){
        $('#calendar').fullCalendar('destroy');
        var format = $.fullCalendar.moment(new Date()).format('YYYY-MM-DD');
        changeView(format);
    });
}

这个 initFullCalendar1 与 initFullCalendar 区别还是很大的,部分逻辑已经以注释的方式解释了。下篇会对整体逻辑进行解释。

fullCalendar - List view, custom columns(三)

接上篇,changeView如下:

function changeView(fakeDate){
    // 此个ajax 是获取当天所有events
    // zWeb.zAjax.requestJson(ajaxOption) 是封装的 调用 ajax 方法,直接用原生的 jquery ajax即可
    var ajaxOption = {
            url : ROOT_URL + operateUrl.getEvents,
            params : {
                // 查询条件
                start:fakeDate
            },
            success : function(data) {
                var events = [];
                if(data.isSuccess){
                    $(data.results).each(function() {
                        events.push({
                            id: $(this).attr('id'),
                            title: $(this).attr('title'),
                            start: $(this).attr('start'),
                            end: $(this).attr('end'),
                        });
                    });
                    // 此个 ajax 是获取日视图下表格列需要展示的所有数据
                    // zWeb.zAjax.requestJson(ajaxOption) 是封装的 调用 ajax 方法,直接用原生的 jquery ajax即可
                    var ajaxOption = {
                            url : ROOT_URL + operateUrl.getData,
                            params : {
                                // 查询条件
                            },
                            success : function(data) {
                                if(data.isSuccess){
                                    render(events, fakeDate, data.results);
                                }
                            }
                        };
                    zWeb.zAjax.requestJson(ajaxOption);                     
                }
            }
        };
    zWeb.zAjax.requestJson(ajaxOption);
}

此时通过 ajax 获取了所有需要展示的 events 和 列数据,然后调用渲染 render 方法。

function render(data, fakeDate, brData){
    $("input[id^='hideBrId_']").remove();
    var rooms = {};
    var i = 0;
    var idMap = {};
    brData.forEach(function(a){
        if(!rooms[a.name]){
            var id = i++;
            rooms[a.name] = {
                id:id,
                brid:a.id,
                name:a.name,
                events:[]
            }
            idMap[id] = rooms[a.name];
        }
        data.forEach(function(b){
            if(a.id == b.brId){
                var computedDate = (new Date(fakeDate).getTime()) + 86400000 * rooms[a.name].id;
                var dateStr = $.fullCalendar.moment(computedDate).format('YYYY-MM-DD');
                b.start = dateStr + 'T' + b.start.split("T")[1];
                b.end = dateStr + 'T' + b.end.split("T")[1];
                rooms[a.name].events.push(b);
            }
        });
    });
    initFullCalendar1(i, data, fakeDate);
    var title = $.fullCalendar.moment(fakeDate).format('YYYY年MM月DD日');
    $(".fc-header-toolbar").find(".fc-center").children().eq(0).text(title);

    // 将标题替换
    $('.fc-widget-header.fc-day-header span').each(function(idx){
        $(this).text(idMap[idx].name);
        $(this).attr("brId",idMap[idx].brid);
        $(this).css("cursor","pointer");
        // 重写 nvsLink 的 click 方法
        $(this).off("click").on("click",function(){
            var brId = $(this).attr("brId");
            $('#calendar').fullCalendar('destroy');
            initFullCalendar(fakeDate, "agendaWeek");
        })
        // 将列数据的ID放入隐藏域中
        var dateStr = $(this).parent().attr("data-date");
        $("body").append("<input type='hidden' value='" + idMap[idx].brid + "' id='hideBrId_" + dateStr + "'/>");
    });
}

我们可以看到,此时我们调用 initFullCalendar1 重新初始化了一个 fullCalendar,这个 initFullCalendar1 和 initFullCalendar 不同支出会在下篇给出。

先看实现,最后说原理

fullCalendar - List view, custom columns(二)

我们按照官方API正常引用fullCalendar插件,init如下。

function initFullCalendar(defaultDate, defaultView){
    if(defaultDate == null) defaultDate = new Date();
    if(defaultView == null) defaultView = 'month';
    $('#calendar').fullCalendar({
        header: {
            left: 'prev,next today',
            center: 'title',
            right: 'month,agendaWeek,agendaDay'
        },
        defaultDate:defaultDate,
        defaultView: defaultView,
        today: ["今天"],
        buttonText: {
            today: '今天',
            month: '月',
            week: '周',
            day: '日'
        },
        events: function(start, end, timezone, callback){
                // callback(events);
        },
        viewRender: function(view, element){
            if(view.type == "agendaDay"){
                $('#calendar').fullCalendar('destroy');
                changeView(view.start);
            }
        },
        dayClick: function(date, jsEvent, view, resourceObj) {
        },
        eventClick:function(event, jsEvent, view){
        }
    });
}

调用时defaultView 和 defaultDate 使用如下调用方式即可。

initFullCalendar(new Date(),"month");

说明初始默认视图是月视图,初始事件是当前日期。

需要注意的是

viewRender: function(view, element){
            if(view.type == "agendaDay"){
                $('#calendar').fullCalendar('destroy');
                changeView(view.start);
            }
        },

viewRender 是切换试图时候会触发回调function,如果当前触发了切换日视图,
(if(view.type == "agendaDay")),
则将当前fullCalendar进行destroy,然后执行changeView,changeView方法下篇给出。

fullCalendar - List view, custom columns(一)

fullCalendar 是一款优秀的 日历 (日程表) 插件,但是有些个性化功能还需要自己完善,自创视图功能官方 API 提供了一些方法,但是很晦涩难懂,并不能及时满足需求要求,所以有些时候只能另辟蹊径了。

前几日遇到一个需求是 用 fullCalendar 展示日程表时候,在日视图下,需要用某些自定义的数据展示列,如下图:

1.jpg

此时,官方API提供 Custom Views(https://fullcalendar.io/docs/views/Custom_Views/)很难满足这一需求,
Coding a View From Scratch 很是晦涩难懂,官方API是这样说的:

2.jpg

对于高级开发人员,fullcalendar提供与JavaScript代码的无限灵活性定制视图API。采用面向对象的编程原则,一个可以继承基类View,实现或重写每一个具体的行为作为一种方法,然后注册新的类fullcalendar。

这就意味着使用 Coding a View From Scratch 是需要翻看 fullCalendar 的源代码,我们选择换一种实现方式。