<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <script src="https://cdn.bootcss.com/vue/2.6.8/vue.js"></script>
    <title>Document</title>
    <style>
        .wrapper {
            margin-left: 150px;
            margin-top: 200px
        }
        .calendar-head {
            width: 400px;
            display: flex;
            justify-content: space-between;
            margin-bottom: 20px
        }
        .last-month {
            border: 10px solid #abcdef;
            border-left-color: transparent;
            border-top-color: transparent;
            border-bottom-color: transparent;
            cursor: pointer;
        }
        .next-month {
            border: 10px solid #abcdef;
            border-right-color: transparent;
            border-top-color: transparent;
            border-bottom-color: transparent;
            cursor: pointer;
        }
        .calendar-content {
            width: 400px; 
        }
        .week {
            width: 100%;
            display: flex;
            background: #abcdef;
            margin-bottom: 4px; 
        }
        .week > div {
            flex: 1;
            text-align: center;
        }
        .everydday-wrapper {
            width: 100%;
            display: flex;
        }
        .day {
            width: 14.28%;
            display: inline-block;
            cursor: pointer;
            line-height: 50px;
            text-align: center;
            box-sizing: border-box
        }
        .day > div {
            width: 100%;
            height: 100%;
            box-sizing: border-box;
            margin-bottom: 2px;
            user-select: none;
        }
        .other {
            color: #ccc;
        }
        .now {
            background: #abcdef;
            color: #f50 !important;
        }
        .active {
            box-sizing: border-box;
            border: 2px solid #abcdef;
            color: #abcdef;
        }
    </style>
</head>
<body>
    <div class="wrapper" id="app">
        <div class="calendar-head">
            <div class="last-month" @click="last"></div>
            <div class="now-date">{{`${year}年${month}月${day}日`}}</div>
            <div class="next-month" @click="next"></div>
        </div>
        <div class="calendar-content">
            <div class="week">
                <div>日</div>
                <div>一</div>
                <div>二</div>
                <div>三</div>
                <div>四</div>
                <div>五</div>
                <div>六</div>
            </div>
            <div class="everyday-wrapper">
                <div v-for="(item, index) in 42" class="day" :key="index">
                    <!-- 对于显示在第一行的某些日期, 他可能是上一个月的
                        如果满足 item - beginDay + prevDays 他就是上一个月的日期
                    -->
                    <div class="other" v-if="item - beginDay <= 0">{{ item - beginDay + prevDays }}</div>
                    <div v-else-if=" item - beginDay <= totalDays "
                        @click="chooseDay(item - beginDay)"
                        :class="{
                            'active': `${year}-${month}-${item - beginDay}` === `${year}-${month}-${day}`, 
                            'now': `${year}-${month}-${item - beginDay}` === curDate}">{{ item - beginDay }}</div>
                    <!-- 如果不满足上一个条件, 剩下的肯定是下一个月的日期, 应该从 1 开始 -->
                    <div class="other" v-else>{{ item - beginDay - totalDays }}</div>
                </div>
            </div>
        </div>
    </div>
    <script>
        new Vue({
            el: '#app',
            data: {
                year: null,
                month: null,
                day: null,
                curDate: ''
            },
            methods: {
                // 初始化日期
                initDate () {
                    let date = new Date()
                    this.year = date.getFullYear()
                    this.month = date.getMonth() + 1
                    this.day = date.getDate()

                    // 记录下当前日期
                    this.curDate = `${this.year}-${this.month}-${this.day}`
                },
                // 处理切换上个月
                last () {
                    if(this.month === 1){
                        this.month = 12
                        this.year --
                    } else {
                        this.month --
                    }
                },
                next () {
                    // 处理切换下月
                    if(this.month === 12) {
                        this.month = 1
                        this.year ++
                    } else {
                        this.month ++ 
                    }
                },
                chooseDay (day) {
                    this.day = day
                }
            },
            created() {
                this.initDate()
            },
            computed: {
                beginDay () {
                    // 计算本月第一天是星期几, 然后将所有的日期减去这个值, 就会在第一天对应的星期下显示为 1
                    return new Date(this.year, this.month - 1, 1).getDay()
                },
                totalDays () {
                    // 计算本月的总天数
                    return new Date(this.year, this.month, 0).getDate()
                },
                prevDays () {
                    return new Date(this.year, this.month - 1, 0).getDate()
                }
            }
        })
    </script>
</body>
</html>
calendar.png