从0到1纯手写一个基于Vue的datepicker组件

1,396 阅读4分钟

在日常开发过程中,我们经常会有选择日期和时间的需求,这时我们需要datepicker组件来帮助我们完成该需求。那么datepicker组件是如何实现的呢,下面我来带大家一起实现一个基于Vue的datepicker组件吧。

Date对象

在Javascript中,Date是关于操作日期和时间的对象,在实现datepicker的过程中,我们需要知道如何获取一个月中的第一天和最后一天。

let date = new Date(year,month,1)//获取某年某月第一天

由于Date对象具有越界退位的机制,所以获取最后一天只需要获取下个月的第0天即可。

let date = new Date(year,month+1,0)//获取某年某月最后一天

月份

因为每年的月份都是固定不变的,这里我们只需要将月份数据写为常量并且通过一个函数输出即可,然后在Vue组件中通过table标签进行渲染。

getMonths(rows) {
    let result = [];
    let months = ['01','02','03','04','05','06','07','08','09','10','11','12'];
    let cols = Math.round(months.length / rows);
    for(let i=0;i<rows;i++) {
        result[i] = months.slice(i*cols,i*cols+cols);
    }
    return result;
}
<table>
    <tr v-for="(row,index) in months" :key="index">
        <td v-for="(m,i) in row" :key="i">
            <span
                class="fu-month-item"
                @click.stop="selectMonth(m)"
                :class="{'current':isCurrentMonth(m),'actived':isActivedMonth(m)}"
            >{{isCurrentMonth(m)?'当月':m}}</span>
        </td>
    </tr>
</table>

日期

为了方便后期代码维护,我们将创建一个获取某年某月全部日期的工具方法。

getYearMonthDates(year, month) {
    if (!year || !month) {
        let _date = new Date();
        year = _date.getFullYear();
        month = _date.getMonth() + 1;
    }
    let dates = [];
    //第一天
    let firstOfMonth = new Date(year, month - 1, 1);
    let firstDateDay = firstOfMonth.getDay();
    if (firstDateDay === 0) {
        //周日
        firstDateDay = 7
    }
    //最后一天
    let lastOfMonth = new Date(year, month, 0);
    let lastDateDay = lastOfMonth.getDay();
    if (lastDateDay === 0) {
        lastDateDay = 7;
    }
    let lastDateOfMonth = lastOfMonth.getDate();

    let prevMonthDayCount = firstDateDay - 1;
    let lastDateOfPrevMonth = new Date(year, month - 1, 0).getDate();

    for (let i = 0; i < 7 * 6; i++) {
        let enable = false;
        let _month = month;
        let date = i + 1 - prevMonthDayCount;
        let _showDate = date;
        if (date <= 0) {
            _month = month - 1;
            _showDate = lastDateOfPrevMonth + date;
        } else if (date > lastDateOfMonth) {
            _month = month + 1;
            _showDate = _showDate - lastDateOfMonth;
        } else {
            enable = true;
        }
        if (_month === 0) {
            _month = 12;
        }
        if (_month === 13) {
            _month = 1;
        }
        dates.push({
            year,
            month: _month,
            date: date,
            showDate: _showDate,
            enable
        })
    }
    return {
        year,
        month,
        days: dates
    }
}

有了这个方法,我们就可以获取日期数据并且渲染界面了。

<div class="fu-datepicker-date" v-show="pickDate">
    <table>
        <thead>
            <th></th>
            <th></th>
            <th></th>
            <th></th>
            <th></th>
            <th></th>
            <th></th>
        </thead>
        <tbody>
            <tr v-for="(week,index) in days" :key="index">
                <td v-for="(day,index) in week" :key="index">
                    <span
                        :class="{'gray':!day.enable,'actived':isActived(day),'today':isToday(day)}"
                        class="fu-date-item"
                        @click.stop="selectDate(day)"
                    >{{isToday(day)?'今':day.showDate}}</span>
                </td>
            </tr>
        </tbody>
    </table>
</div>

时间

我们通过下面三个方法获取时间数据:

getHours() {
    let result = [];
    for (let i = 0; i < 24; i++) {
        let hour = `${i}`.padStart(2, '0');
        result.push(hour);
    }
    return result
},
getMinOrSec() {
    let result = [];
    for (let i = 0; i < 60; i++) {
        let hour = `${i}`.padStart(2, '0');
        result.push(hour);
    }
    return result
},
getMonths(rows) {
    let result = [];
    let months = ['01','02','03','04','05','06','07','08','09','10','11','12'];
    let cols = Math.round(months.length / rows);
    for(let i=0;i<rows;i++) {
        result[i] = months.slice(i*cols,i*cols+cols);
    }
    return result;
}

将获取到的数据用列表渲染出来:

<div class="time-picker-area">
    <div
        class="fu-time-item"
        v-for="(hour,index) in hours"
        :key="index"
        @click.stop="changeHour(hour)"
        :class="{'actived':isHourActived(hour)}"
    >{{hour}}</div>
</div>

<div class="time-picker-area">
    <div
        class="fu-time-item"
        v-for="(min,index) in minAndSecs"
        :key="index"
        @click.stop="changeMin(min)"
        :class="{'actived':isMinActived(min)}"
    >{{min}}</div>
</div>
<div class="time-picker-area">
    <div
        class="fu-time-item"
        v-for="(sec,index) in minAndSecs"
        :key="index"
        @click.stop="changeSec(sec)"
        :class="{'actived':isSecActived(sec)}"
    >{{sec}}</div>
</div>

这样我们就完成了一个最基本的datepicker组件。

Demo地址

总结

总体来说,实现datepicker组件的最关键思路在对于Javascript内置的Date对象的操作上面。在展示年、月份、日期、星期、时间等数据时,利用Date对象的越界退位机制获取对应的数据,然后利于Vue将对应的数据渲染出来。

由于篇幅有限,无法将所有的代码细节一一展现在这里,如果有需要的童鞋可以访问GitHub项目仓库fu-datepicker,有错误或者不明白的地方可以留言,我会及时回复大家,喜欢这个组件的可以点个赞😊,共勉!