手写日历组件

效果预览

各种各样的产品层出不穷,公司层面严抓产品风格,寻求在设计上脱颖而出,这就加大了对前端的开发难度,定制化的场景越来越多通用型的组件库已经不能够满足我们的需求了,摸鱼的时候立马对这方面的能力进行加强~~

实现思路

  • 算出这个月的第一天是从星期几开始,也就是日历列表的前置空白部分。
  • 封装一个函数根据年份,月份计算出这个月到底有多少天,与前置空白部分进行拼接渲染。
  • 日历有两种模式,单选以及选择起始-结束,根据传入的参数不同选择不同的模式,进行处理。
  • 选中号数时,大于当前号数的无法选择,年份,月份都是如此。例如:今天是23号,大于23号的全部无法选中。

事件处理

入口函数:

$(function(){
    
    function init() {
        ChangeData();   // 计算本月有多少天
        render();       // 渲染页面
        bindEvent();    // 初始化事件绑定
    }
    
    init();

})(document)
复制代码

立即执行函数+init是很多插件的写法 例如:new swiper().init()

function ChangeData() {
        // 获取一个月有多少天
        days = getMonthDay( year, month );
        // 创建当前这个月的日期天数
        daysArr = generateArray( 1, days );
        // 前置空白部分的个数
        weekday = getweekday( year, month );
        // 前置空白部分的集合
        weekdayArr = generateArray( 1, weekday );
        // 当前日期
        showTitle = `${year}${month}月`
    }
复制代码

切换月份的事件:

function changeMonthHandler( isAdd, callback ) {
    // isAdd 为 1 是前进 为 0 是后退
    if ( isAdd === '1' ) {
        month += 1;
        let Year = month > 12 ? year + 1 : year;
        if ( !checkRange( Year ) ) {
            month = month > 12 ? 1 : month;
            year = Year;
            _obj.month = month;
            _obj.year = Year;
            ChangeData();
            callback(_obj);
        }

    } else {
        month -= 1;
        let Year = month < 1 ? year - 1 : year;
        if ( !checkRange( Year ) ) {
            month = month < 1 ? 12 : month;
            year = Year;
            _obj.month = month;
            _obj.year = Year;
            ChangeData();
            callback(_obj);
        }
    }
}
// 判断传入的年份是否越界
function checkRange( year ) {
    let overstep = false;
    if ( year < minYear || year > maxYear ) {
        overstep = true;
    }
    return overstep;
}

复制代码

子级触发父级事件并传递前进后退的标识和回调,前进则月份+1,并且判断当前月份是否超过12月超过则把年份+1,并触发 ChangeData 事件重新计算此月有多少天, 后退的事件同理。

单选的点击事件:

function openDisAbled( day, callback ) {
        let bool = true;
        if ( day == '' ) return bool;
        
        let date = `${year}/${month}/${day}`;
        // 最小的日期限制
        let minTime = `${min.year}/${min.month}/${min.day}`;
        // 最大的日期限制
        let maxTime = `${max.year}/${max.month}/${max.day}`;
        // 当前日期转换时间戳
        let timestamp = new Date(date).getTime();
        // 如果点击的日期再此月内就执行
        if ( timestamp >= new Date(minTime).getTime() && timestamp <= new Date(maxTime).getTime() ) {
            bool = false;
        }
        callback( bool, _obj );
    }
复制代码

子级进行点击触发父级事件传入号数以及回调,前置空白部分是空的,所以如果等于空直接退出无法点击。上述代码的判断是决定是否可选择的日期,例如下个月的日期我是无法选中的。如果当前如期大于最小日期并且当前日期小于最大日期则bool置为false,回调回子级,可进行点击。minTime的默认值是1950,maxTime的最大值是2050

子级接收进行处理:

selectTime( el ) {
    let day = $(el).attr('index');
    ScreenAbled( 'section', { day }, ( res, { mode }) => {
        // 如果 bool 传回来是 true 直接退出
        if ( res ) return;
        // date 单选模式
        if ( mode === 'date' ) {
            $(el)
                .addClass('active')      // 选中的添加高亮样式
                .siblings('.active')     // 筛选出同级带有 active 的元素
                .removeClass('active');  // 删除 active 类
                // 触发父级事件 写入选中日期
                ScreenAbled('alone', { day: day });    
        }
    })
}
复制代码

起始-结束事件:

  • 父级
function ChangeDouble( callback, day ) {
        let date = `${year}-${month}-${day}`;
        // startDate存放起始时间
        let compare = new Date(date.replace(/\-/g, '/')).getTime() < new Date( startDate.replace(/\-/g,'/') ).getTime()
        if ( isStart || compare ) {
            startDate = date;
            isStart = false;
            callback( 'start' );
            startTime = `${year}-${month}-${day}`;
        } else {
            isStart = true;
            callback( 'end' );
            endTime = `${year}-${month}-${day}`;
        }
    }
复制代码

compare的作用是筛选出结束时间大于起始时间的情况,例如:起始5/23 结束5/13,这种情况不成立则需要操作者重新选择时间,

  • 子级
// 触发父级事件
ScreenAbled( 'ChangeDouble', { day }, type => {
    // 起始时间处理
    if ( type === 'start' ) {
        // 获取出所有小字的元素进行重置
        $('.seat').find('.slot').text(' ');
        // 为当前点击的元素添加小字提示
        $(el).find('.slot').text('起始');
        
        $(el)
            .addClass('active')    // 为当前选中元素添加高亮
            .siblings('.active')   // 筛选出带 active 的元素
            .removeClass('active') // 删除 active 类

        $(el)            
            .siblings('.section')   // 筛选出带 section 的元素
            .removeClass('section') // 删除 section 类
            
        // 存储开始的下标
        CollectIndex = day;
        return false;
    }
    // 结束时间处理
    $(el)
        .addClass('active')      // 添加高亮样式
        .siblings('.sign')       // 筛选出带有 sign 的元素
        .slice( CollectIndex, day - 1 )   // 筛选出 起始 - 结束日期区间的元素
        .addClass('section')              // 为此区间的元素添加高亮样式

    $(el).find('.slot').text('结束');
});
复制代码

选中起始以及结束时需要在盒子内用小字提示操作者,子级的处理就是动态添加删除样式。

最后:

以后碰到定制化需求闭上眼睛思考片刻,立马给提需求的人来个过肩摔再加上一套军体拳,让他上不了班这个需求就不用搞了,哈哈。 以上只说明了比较重要的几个函数完整的 源码在这 感谢大家的阅读,希望看完大家能有所收获! 觉得有用就点个赞吧,你的点赞是我创作的最大动力~

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享