日历组件封装——周、月日历

前言:日历组件在我们日常开发中非常常见,用于查看指定日期的数据,例如:人员排班等

一、周日历(显示星期和日期) ------ 根据指定的日期,获取当前日期所在周以及切换前、后一周的日期

代码实现:

公用工具函数 utils/index.js

js 复制代码
export function parseTime(time, cFormat, noSameYearOrDay = false) {
    if (arguments.length === 0) {
        return null;
    }
    let format = cFormat || 'y-m-d h:i:s';
    let date;
    if (time && typeof time == 'object') {
        date = time;
    } else {
        if (('' + time).length === 10) time = parseInt(time) * 1000;
        date = new Date(time);
    }
    let today = new Date();
    let time_str = '';
    if (today.getFullYear() == date.getFullYear() &&
        today.getMonth() == date.getMonth() &&
        today.getDate() == date.getDate() &&
        !noSameYearOrDay
    ) {
        time_str = '今天';
    } else if (today.getFullYear() == date.getFullYear() && !noSameYearOrDay) {
        format = 'm-d';
        const formatObj = {
            m: date.getMonth() + 1,
            d: date.getDate(),
        };
        time_str = format.replace(/(y|m|d|h|i|s|a)+/g, (result, key) => {
            let value = formatObj[key];
            if (result.length > 0 && value < 10) {
                value = '0' + value;
            }
            return value || 0;
        });
    } else {
        const formatObj = {
            y: date.getFullYear(),
            m: date.getMonth() + 1,
            d: date.getDate(),
            h: date.getHours(),
            i: date.getMinutes(),
            s: date.getSeconds(),
            a: date.getDay(),
        };
        time_str = format.replace(/(y|m|d|h|i|s|a)+/g, (result, key) => {
            let value = formatObj[key];
            if (result.length > 0 && value < 10) {
                value = '0' + value;
            }
            return value || 0;
        });
    }

    return time_str;
}

/**
 * 获取指定日期的上一个月
 * @param date
 * @return {Date}
 */
export function prevMonth(date) {
    const year = date.getFullYear();
    const month = date.getMonth();
    return month === 0 ?
        changeYearMonthAndClampDate(date, year - 1, 11) :
        changeYearMonthAndClampDate(date, year, month - 1);
};

/**
 * 获取指定日期的下一个月
 * @param date
 * @return {Date}
 */
export function nextMonth(date) {
    const year = date.getFullYear();
    const month = date.getMonth();
    return month === 11 ?
        changeYearMonthAndClampDate(date, year + 1, 0) :
        changeYearMonthAndClampDate(date, year, month + 1);
};

/**
 * 获取指定月份的天数
 * @param year - 年份
 * @param month - 月份
 * @return {number} 当月天数
 */
export function getDayCountOfMonth(year, month) {
    if (month === 3 || month === 5 || month === 8 || month === 10) {
        return 30;
    }

    if (month === 1) {
        if (year % 4 === 0 && year % 100 !== 0 || year % 400 === 0) {
            return 29;
        }
        return 28;
    }

    return 31;
};

export function changeYearMonthAndClampDate(date, year, month) {
    // clamp date to the number of days in `year`, `month`
    // eg: (2010-1-31, 2010, 2) => 2010-2-28
    const monthDate = Math.min(date.getDate(), getDayCountOfMonth(year, month));
    return modifyDate(date, year, month, monthDate);
};


export function modifyDate(date, y, m, d) {
    return new Date(y, m, d, date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds());
};

week-calendar.vue

JS 复制代码
<template>
    <div class="week-calendar-wrapper">
        <h3>周日历</h3>

        <div class="content-wrapper">
            <div class="prev-week">
                <el-button
                    icon="el-icon-arrow-left"
                    :disabled="prevDisabled"
                    @click="handlePrev"
                ></el-button>
            </div>
            <div class="current-week">
                <div class="week-wrapper">
                    <div class="col week-item" v-for="item in weekList" :key="item">{{ item }}</div>
                </div>
                <div class="date-wrapper">
                    <div
                        :class="['col', 'data-item', {'today': item.isToday}]"
                        v-for="item in dateList"
                        :key="item.date"
                    >{{ item.date }}</div>
                </div>
            </div>
            <div class="next-week">
                <el-button icon="el-icon-arrow-right" @click="handleNext"></el-button>
            </div>
        </div>
    </div>
</template>

<script>
import { parseTime } from '@/utils/index';
export default {
    name: 'WeekCalendar',
    data() {
        return {
            weekList: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
            startDate: '',
            today: new Date(),
        }
    },
    computed: {
        weekStartDate() {
            const date = this.startDate ? new Date(this.startDate) : this.today;
            const nowYear = date.getFullYear(); //当前年
            const nowMonth = date.getMonth(); //当前月
            const nowDay = date.getDate(); //当前日
            const nowDayOfWeek = date.getDay(); // 今天本周的第几天

            if (nowDayOfWeek) {
                return new Date(nowYear, nowMonth, nowDay - nowDayOfWeek + 1);
            }
            return new Date(nowYear, nowMonth, nowDay - 6);

        },

        weekEndDate() {
            const year = this.weekStartDate.getFullYear();
            const month = this.weekStartDate.getMonth();
            const day = this.weekStartDate.getDate();
            return new Date(year, month, day + 6);
        },

        prevDisabled() {
            const now = Date.now();
            return this.weekStartDate.getTime() <= now && this.weekEndDate.getTime() >= now;
        },
        dateList() {
            const arr = [];
            const year = this.weekStartDate.getFullYear();
            const month = this.weekStartDate.getMonth();
            const day = this.weekStartDate.getDate();

            for (let i = 0; i < 7; i++) {
                const time = new Date(year, month, day + i);
                const isToday = parseTime(time, 'y-m-d', true) === parseTime(this.today, 'y-m-d', true);

                arr.push({
                    date: isToday ? '今天' : parseTime(time, 'm-d', true),
                    isToday
                });
            }
            return arr;
        },
    },
    methods: {
        parseTime,
        handlePrev() {
            const startDate = this.weekStartDate.getTime() - 24 * 60 * 60 * 1000 * 7;
            this.setStartDate(startDate)
        },
        handleNext() {
            const startDate = this.weekStartDate.getTime() + 24 * 60 * 60 * 1000 * 7;
            this.setStartDate(startDate)
        },
        setStartDate(startDate) {
            const date = new Date(startDate);
            this.startDate = parseTime(date, 'y-m-d', true);
        }
    }
}
</script>

<style scoped lang="scss">
.week-calendar-wrapper {
    display: flex;
    justify-content: center;
    flex-direction: column;

    .content-wrapper {
        display: flex;
        align-items: center;

        .current-week {
            display: flex;
            flex-direction: column;
            margin: 0 20px;

            .week-wrapper,
            .date-wrapper {
                display: flex;
                width: 100%;


                .col {
                    display: flex;
                    justify-content: center;
                    width: 40px;

                    & + .col {
                        margin-left: 10px;
                    }

                    &.week-item {
                        color: #aab4bf;
                    }


                    &.data-item {
                        font-size: 14px;
                        color: #7a8794;
                    }

                    &.today {
                        color: #459eff
                    }
                }
            }
        }
    }
}
</style>

二、月日历(显示日期数字) ------ 根据指定的日期,获取当前日期所在月以及切换前、后一月的日期

只展示当月日期:

代码实现:

month-calendar.vue

JS 复制代码
<template>
    <div class="month-calendar-wrapper">
        <h3>月日历</h3>

        <div class="content-wrapper">
            <div class="btn-group">
                <div class="pre-month">
                    <el-button
                        size="small"
                        icon="el-icon-arrow-left"
                        @click="handlePrev"
                    ></el-button>
                </div>
                <div class="today">
                    <el-button size="small" @click="handleToday">今天</el-button>
                </div>
                <div class="next-month">
                    <el-button size="small" icon="el-icon-arrow-right" @click="handleNext"></el-button>
                </div>

                <div class="current-date">【 {{ currentDate }} 】</div>
            </div>

            <div class="month-wrapper">
                <div class="week-wrapper">
                    <div class="col week-item" v-for="item in weekList" :key="item">{{ item }}</div>
                </div>
                <div class="date-list-wrapper">
                    <div class="date-wrapper" v-for="(row, index) in showDateList" :key="index">
                        <template v-for="item in row">
                            <div
                                v-if="item.type === 'current'"
                                :key="item.date"
                                :class="['col', 'data-item',{
                                    'today': item.isToday,
                                    'disabled': item.type !== 'current',
                                    'selected': item.date === selectedDate,
                                }]"
                                @click="handleSelectedDate(item)"
                            >
                                {{ item.text }}
                            </div>
                            <div
                                v-else
                                :key="item.date"
                                :class="['col', 'data-item',{
                                    'today': item.isToday,
                                    'disabled': item.type !== 'current',
                                    'selected': item.date === selectedDate,
                                }]"
                            >
                            </div>
                        </template>
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

<script>
import {nextMonth, parseTime, prevMonth} from "@/utils/index";

export default {
    name: 'MonthCalendar',
    data() {
        return {
            weekList: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
            date: new Date(),
            today: parseTime(new Date(), 'y-m-d', true),
            selectedDate: parseTime(new Date(), 'y-m-d', true)
        }
    },
    computed: {
        currentDate() {
            return parseTime(this.date, 'y-m', true)
        },
        dateList() {
            const dates = [];
            // 获取当前第一天是周几
            let firstDay = this.getFirstDayOfMonth(this.date);
            firstDay = firstDay === 0 ? 7 : firstDay;
            // 获取当前最后一天是周几
            let lastDay = this.getLastDayOfMonth(this.date);
            lastDay = lastDay === 0 ? 7 : lastDay;
            // 获取上个月后(firstDay - 1)的日期
            const prevMonthDays = this.getPrevMonthDays(this.date, firstDay - 1);
            const currentMonthDays = this.getMonthDays(this.date);
            const nextMonthDays = this.getNextMonthDays(this.date, 7 - lastDay);

            dates.push(...prevMonthDays, ...currentMonthDays, ...nextMonthDays);
            return dates
        },

        showDateList() {
            return this.dateList.reduce((arr, item, index) => {
                if (index % 7 === 0) {
                    arr.push([]);
                }
                arr[arr.length - 1].push(item)
                return arr;
            }, [])
        }
    },
    methods: {
        getFirstDayOfMonth(date) {
            // 获取当前第一天是周几
            const temp = new Date(date.getFullYear(), date.getMonth(), 1);
            return temp.getDay();
        },
        getLastDayOfMonth(date) {
            // 获取当前最后一天是周几
            const temp = new Date(date.getFullYear(), date.getMonth() + 1, 0);
            return temp.getDay();
        },
        getPrevMonthDays(date, amount) {
            if (amount <= 0) return []
            // 获取上个月份的日期
            const temp = new Date(date.getFullYear(), date.getMonth(), 0);
            const prefix = parseTime(temp, 'y-m', true);
            const days = temp.getDate();
            return new Array(amount).fill(0).map((item, index) => {
                const text = days - amount + 1 + index;
                return {
                    date: `${prefix}-` + `${text}`.padStart(2, '0'),
                    text,
                    isToday: false,
                    type: 'prev'
                }
            })
        },
        getMonthDays(date) {
            // 获取当前月份的天数
            const temp = new Date(date.getFullYear(), date.getMonth() + 1, 0);
            const prefix = parseTime(temp, 'y-m', true);
            const days = temp.getDate();
            return new Array(days).fill(0).map((item, index) => {
                const text = index + 1;
                const date = `${prefix}-` + `${text}`.padStart(2, '0')
                return {
                    date,
                    text: date === this.today ? '今': text,
                    isToday: date === this.today,
                    type: 'current'
                };
            })
        },
        getNextMonthDays(date, amount) {
            if (amount <= 0) return []
            // 获取下个月份的日期
            const temp = new Date(date.getFullYear(), date.getMonth() + 2, 0);
            const prefix = parseTime(temp, 'y-m', true);
            return new Array(amount).fill(0).map((item, index) => {
                const text = index + 1;
                return {
                    date: `${prefix}-` + `${text}`.padStart(2, '0'),
                    text: index + 1,
                    isToday: false,
                    type: 'next'
                }
            })
        },
        handlePrev() {
            this.date = prevMonth(this.date);
        },
        handleToday() {
            this.selectedDate = parseTime(new Date(), 'y-m-d', true);
            this.date = new Date();
        },
        handleNext() {
            this.date = nextMonth(this.date);
        },
        handleSelectedDate(item) {
            this.selectedDate = item.date;
        }
    }
}
</script>

<style scoped lang="scss">
.month-calendar-wrapper {

    .content-wrapper {
        .btn-group {
            display: flex;
            align-items: center;
            gap: 10px;
        }

        .month-wrapper {
            margin-top: 12px;
            width: 420px;
            padding: 10px;
            border: 1px solid #ccc;
            border-radius: 4px;
            box-sizing: border-box;

            .week-wrapper,
            .date-wrapper {
                width: 100%;
                display: grid;
                grid-template-columns: repeat(7, 40px);
                gap: 10px 20px;
                cursor: pointer;

                .col {
                    display: flex;
                    justify-content: center;
                    align-items: center;
                    width: 40px;

                    &.week-item {
                        color: #aab4bf;
                        line-height: 30px;
                    }

                    &.data-item {
                        height: 40px;
                        box-sizing: border-box;

                        &:hover:not(.disabled):not(.selected) {
                            background-color: #c6e2ff;
                            border: 3px solid #f9fafc;
                            border-radius: 50%;
                        };
                    }

                    &.today {
                        color: #459eff;
                    }

                    &.disabled {
                        color: #ccc;
                    }

                    &.selected {
                        color: #fff;
                        background-color: #459eff;
                        border-radius: 50%;
                        border: 3px solid #ebf4ff;
                    }
                }
            }
        }
    }
}
</style>
相关推荐
小小小小宇几秒前
React 的 DOM diff笔记
前端
小小小小宇7 分钟前
react和vue DOM diff 简单对比
前端
我在北京coding9 分钟前
6套bootstrap后台管理界面源码
前端·bootstrap·html
Carlos_sam12 分钟前
Opnelayers:封装Popup
前端·javascript
前端小白从0开始1 小时前
Vue3项目实现WPS文件预览和内容回填功能
前端·javascript·vue.js·html5·wps·文档回填·文档在线预览
難釋懷2 小时前
Vue解决开发环境 Ajax 跨域问题
前端·vue.js·ajax
特立独行的猫a2 小时前
Nuxt.js 中的路由配置详解
开发语言·前端·javascript·路由·nuxt·nuxtjs
咸虾米2 小时前
在uniCloud云对象中定义dbJQL的便捷方法
前端·javascript
梨子同志2 小时前
JavaScript Proxy 和 Reflect
前端·javascript