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

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

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

代码实现:

公用工具函数 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>
相关推荐
GISer_Jing2 小时前
前端面试通关:Cesium+Three+React优化+TypeScript实战+ECharts性能方案
前端·react.js·面试
落霞的思绪3 小时前
CSS复习
前端·css
咖啡の猫5 小时前
Shell脚本-for循环应用案例
前端·chrome
百万蹄蹄向前冲7 小时前
Trae分析Phaser.js游戏《洋葱头捡星星》
前端·游戏开发·trae
朝阳5818 小时前
在浏览器端使用 xml2js 遇到的报错及解决方法
前端
GIS之路8 小时前
GeoTools 读取影像元数据
前端
ssshooter9 小时前
VSCode 自带的 TS 版本可能跟项目TS 版本不一样
前端·面试·typescript
Jerry9 小时前
Jetpack Compose 中的状态
前端
dae bal10 小时前
关于RSA和AES加密
前端·vue.js
柳杉10 小时前
使用three.js搭建3d隧道监测-2
前端·javascript·数据可视化