Holding hands with a small and beautiful calendar component

Holding hands with a small and beautiful calendar component

foreword

Calendar is one of the common business components in front-end development. Although there are many ready-made calendar components, we often need to create one when we need to customize it. At this time, we need to understand the principle of calendar generation. In fact, it is not as complicated as imagined.
Today I will bring you a tutorial that will take you through a calendar component. This tutorial uses Vue to develop a small and beautiful calendar. It mainly explains the basic ideas of implementing the calendar. Friends who find it useful remember to follow it.

Environmental preparation

For the vue2.0 project built using vue scaffolding, the css preprocessor can choose scss

Get started

1. Ideation Calendar Prototype

Draw a sketch of the calendar to build a common skeleton of the calendar. Of course, you can also learn from the appearance of some existing calendars. Here I adopt a more common appearance, including basic date display, switching month and year, and displaying the current month.
insert image description here
First, we create a new component, name it casually, and write the basic structure. We don’t talk about styles here, so put all the style codes first:

<template>
  <div class="calendar">
  	<div class="header"></div>
  	<div class="body"></div>
  </div>
</template>

<script>
export default {
      
      
  name: "DemoCalendar",
};
</script>

<style lang="scss" scoped>
$primary-color: #3464e0;

.calendar {
      
      
  background-color: #fff;
}

.header {
      
      
  padding: 0 12px;
  display: flex;
  justify-content: center;
  border-bottom: 1px solid #eee;

  .current-date {
      
      
    text-align: center;
    font-size: 17px;
    padding: 16px 8px;
    color: #030303;
  }

  .btn-group {
      
      
    display: flex;
    align-items: center;
    justify-content: center;

    .btn {
      
      
      display: flex;
      align-items: center;
      justify-content: center;
      background: #fff;
      color: #606266;
      text-align: center;
      box-sizing: border-box;
      font-weight: 500;
      width: 34px;
      height: 26px;
      font-size: 18px;
    }
  }
}

.body {
      
      
  border-bottom: 1px solid #eee;

  .weeks {
      
      
    display: flex;
    font-size: 15px;
    padding: 16px 0;

    .week-item {
      
      
      flex: 1;
      text-align: center;
      font-weight: bold;
    }
  }

  .day-list {
      
      
    display: flex;
    flex-wrap: wrap;

    .day-item {
      
      
      display: flex;
      justify-content: center;
      align-items: center;
      width: 14.285%;
      height: 40px;
      text-align: center;
      padding: 12px 0;
      font-size: 18px;
      color: #c8c8c8;

      &.current-month-day {
      
      
        display: flex;
        align-items: center;
        justify-content: center;
        color: #171717;
        font-weight: bold;
      }

      &.active {
      
      
        .text {
      
      
          display: flex;
          align-items: center;
          justify-content: center;
          width: 40px;
          height: 40px;
          border-radius: 100%;
          box-sizing: border-box;
          background-color: #ddd;
          padding: 14px 0;
        }
      }

      &.active {
      
      
        .text {
      
      
          position: relative;
        }
      }
    }
  }
}
</style>

The whole page can be divided into two parts. I named the header and body
header part of the code:

<div class="header">
  <div class="btn-group">
    <div class="btn btn-prev">
      <span>«</span>
    </div>
    <div class="btn btn-prev">
      <span></span>
    </div>
    <div class="current-date">10月24日</div>
    <div class="btn btn-next">
      <span></span>
    </div>
    <div class="btn btn-next">
      <span>»</span>
    </div>
  </div>
</div>

The body can be divided into the week title and the date list. It is more appropriate to use a loop for both parts. Of course, since the title is unchanged, it can also be written directly.

<!--写死-->
<div class="weeks">
  <div class="week-item"></div>
  <div class="week-item"></div>
  <div class="week-item"></div>
  <div class="week-item"></div>
  <div class="week-item"></div>
  <div class="week-item"></div>
  <div class="week-item"></div>
</div>
 
<!--循环-->
<div class="weeks">
  <div class="week-item" v-for="week in weeks" :key="week">
    {
   
   { week }}
  </div>
</div>

The next step is to design the date list, we can still write it to death

<div class="day-list">
  <div class="day-item" v-for="i of 35" :key="i">
    <span class="text">{
   
   { i }}</span>
  </div>
</div>

Well, here we have implemented such a calendar.
insert image description here
At this time, the color of our date is dark. In order to highlight the color of the currently displayed month, we can add a class name, such as current-month-day, and the effect is as follows:

<div class="day-list">
  <div class="day-item current-month-day" v-for="i of 35" :key="i">
    <span class="text">{
   
   { i }}</span>
  </div>
</div>

insert image description here
In order to dynamically control this state, it is best to design each date as an object, which can store more information:

{
    
    
	type: "current-month-day", // 日期类型,取值为[pre|current|next]-month-day,表示[上|今|下]月的日期
	day: 1 // 日期数字
}

Therefore, we transform this loop:

<div class="day-list">
  <div
    class="day-item"
    :class="[day.type]"
    v-for="(day, index) in dayList"
    :key="index"
  >
    <span class="text">{
   
   { day.day }}</span>
  </div>
</div>

So far, our code is:

<template>
  <div class="calendar">
    <div class="header">
      <div class="btn-group">
        <div class="btn btn-prev">
          <span>«</span>
        </div>
        <div class="btn btn-prev">
          <span></span>
        </div>
        <div class="current-date">10月24日</div>
        <div class="btn btn-next">
          <span></span>
        </div>
        <div class="btn btn-next">
          <span>»</span>
        </div>
      </div>
    </div>
    <div class="body">
      <div class="weeks">
        <div class="week-item" v-for="week in weeks" :key="week">
          {
   
   { week }}
        </div>
      </div>
      <div class="day-list">
        <div
          class="day-item"
          :class="[day.type]"
          v-for="(day, index) in dayList"
          :key="index"
        >
          <span class="text">{
   
   { day.day }}</span>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
      
      
  name: "ACalendar",
  data() {
      
      
    return {
      
      
      dayList: [], // 获取需要渲染的日期列表
      weeks: ["日", "一", "二", "三", "四", "五", "六"], // 星期数组
    };
  },
  created() {
      
      
    // 填充测试数据
    this.dayList = this.dayList.concat(
      new Array(5).fill({
      
       day: 0, type: "prev-month-day" })
    );
    this.dayList = this.dayList.concat(
      new Array(26).fill({
      
       day: 1, type: "current-month-day" })
    );
    this.dayList = this.dayList.concat(
      new Array(4).fill({
      
       day: 2, type: "next-month-day" })
    );
    console.log(this.dayList);
  },
  methods: {
      
      },
};
</script>

<style lang="scss" scoped>
/* 参考上面已给出的代码 */
</style>

2. Ideation Date List

In the previous step, we have built the prototype of the calendar. In this step, we need to implement the generation algorithm of the date list. The general idea is not difficult, it is nothing more than:

  • Generate a date list at the end of the previous month and push it into the date list
  • Generate a date list of this month and push it into the date list
  • Generate a date list at the beginning of the next month and push it into the date list
    insert image description here

Among these three lists, it is difficult to generate a list of the date of the end of the previous month and a list of the date of the beginning of the next month.
How do we determine from which date the date list at the end of the previous month is generated, how to know which of the 28th, 29th, 30th, and 31st the end of the month is, and how to determine when the date list is generated at the beginning of the next month.
For generating a list of dates at the end of the previous month, we can take the following steps:

  1. Get the M number at the end of the previous month
  2. Get the 1st of this month is the week N
  3. Loop N times from this Sunday to week N, generate from MN number to M number

For example, if the 1st of this month is Friday, the index is 5, and 5-0=5 is generated from Sunday to Friday, 5 dates need to be generated, and the end of last month is 31st, then the generated date list is [27, 28, 29, 30, 31], represented by a loop:

for(let i = week(当前月第一天是星期几) - 1(减一是因为要少一次循环); i >= 0; i--) {
    
    
	dayList.push(i)
}

Similarly, the date list at the beginning of the next month is also generated according to this rule:

for (let i = week(当前月最后一天是星期几) + 1; i <= 6; i++) {
    
    
	dayList.push(i)
}

Of course, the above code is just to demonstrate the idea, let’s talk about the specific implementation:

According to the appeal rules, we can write several tool methods to obtain the calculation results of the date by passing in the current year and month, which facilitates our construction of the date list. They are:

  • Get the year and month of the previous month based on the current year and month
  • Get the year and month of the next month based on the current year and month
  • Get the last day of a certain year and month
 /**
  * 根据当前年月获取上个月的年月
  * @param {Object} year 年
  * @param {Object} month 月
  */
 getPrevMonthInfo(year, month) {
    
    
   let prevMonthYear = year;
   let prevMonth = month - 1;

   if (prevMonth < 1) {
    
    
     prevMonth = 12;
     prevMonthYear -= 1;
   }

   return {
    
    
     prevMonthYear,
     prevMonth,
   };
 },
 
 /**
  * 根据当前年月获取下个月的年月
  * @param {Object} year 年
  * @param {Object} month 月
  */
 getNextMonthInfo(year, month) {
    
    
   let nextMonthYear = year;
   let nextMonth = month + 1;

   if (nextMonth > 12) {
    
    
     nextMonth = 1;
     nextMonthYear += 1;
   }

   return {
    
    
     nextMonthYear,
     nextMonth,
   };
 },
 
 /**
  * 获取某年某月最后一天
  * @param year 年
  * @param month 月
  * @returns {number}
  */
 getMonthLastDay(year, month) {
    
    
   // 获取下个月的年月
   const {
    
     nextMonthYear, nextMonth } = this.getNextMonthInfo(year, month);
   // 获取下个月1号的时间戳
   const firstDayTimeStamp = new Date(
     `${
      
      nextMonthYear}/${
      
      nextMonth}/1`
   ).getTime();
   // 一天24小时的毫秒数
   const oneDayTimeStamp = 24 * 60 * 60 * 1000;
   // 当月最后一天即为下个月一号减去一天的时间
   return new Date(firstDayTimeStamp - oneDayTimeStamp).getDate();
 },

3. Generate a list of dates

With the appeal algorithm and tool method in place, we can proceed to implement

3.1 Generate a list of dates at the end of the previous month

/**
  * 获取上个月月末的日期列表
  * @param {Object} year 年
  * @param {Object} month 月
  */
getPrevMonthDays(year, month) {
    
    
  // 获取上个月的年月
  const {
    
     prevMonthYear, prevMonth } = this.getPrevMonthInfo(year, month);
  // 获取上个月的最后一天
  const prevMonthLastDay = this.getMonthLastDay(prevMonthYear, prevMonth);
  // 获取这个月第一天
  const date = new Date(`${
      
      year}/${
      
      month}/1`);
  // 获取这个月第一天是星期几
  const week = date.getDay();

  const days = [];
  // 生成上个月月末的日期列表
  for (let i = week - 1; i >= 0; i--) {
    
    
    // 从头加入
    days.push({
    
    
      type: "prev-month-day", // 类型
      year: prevMonthYear, // 年
      month: prevMonth, // 月
      day: prevMonthLastDay - i, // 显示的日
    });
  }

  return days;
}

3.2 Generate a list of dates for this month

This is relatively simple, that is, the sequence is generated from the 1st to the last day of the month

/**
  * 获取某年某月的日期列表
  * @param {Object} year 年
  * @param {Object} month 月
  */
getCurrentMonthDays(year, month) {
    
    
  // 获取当前月的最后一天
  const currentMonthLastDay = this.getMonthLastDay(year, month);
  const days = [];

  for (let i = 1; i <= currentMonthLastDay; i++) {
    
    
    days.push({
    
    
      type: "current-month-day", // 类型
      active: false, // 是否点击选中
      year, // 年
      month, // 月
      day: i, // 显示的日
    });
  }

  return days;
}

3.3 Generate a list of dates at the beginning of the next month

/**
  * 获取下个月月初的日期列表
  * @param {Object} year 年
  * @param {Object} month 月
  */
 getNextMonthDays(year, month) {
    
    
   // 获取当前月的最后一天
   const currentMonthLastDay = this.getMonthLastDay(year, month);
   // 获取下个月的年和月
   const {
    
     nextMonthYear, nextMonth } = this.getNextMonthInfo(year, month);
   // 获取当前月最后一天是星期几
   const week = new Date(`${
      
      year}/${
      
      month}/${
      
      currentMonthLastDay}`).getDay();

   const days = [];
   let day = 0;
   // 生成下个月月初的日期列表
   for (let i = week + 1; i <= 6; i++) {
    
    
     day++;
     days.push({
    
    
       type: "next-month-day",
       year: nextMonthYear,
       month: nextMonth,
       day,
     });
   }

   return days;
 }

3.4 List of merged dates

After generating the three date lists, just merge them together. Here, the expansion operator of ES6 is used, which is more concise and beautiful.

/**
  * 生成某年某月的日期列表
  * 由上个月的日期+这个月的日期+下个月的日期组成
  * @param {Object} year 年
  * @param {Object} month 月
  * @returns 数组[日期数组]
  */
getDayList(year, month) {
    
    
  // 生成上个月的日期列表
  const prevMonthDays = this.getPrevMonthDays(year, month);
  // 生成这个月的日期列表
  const currentMonthDays = this.getCurrentMonthDays(year, month);
  // 生成下个月的日期
  const nextMonthDays = this.getNextMonthDays(year, month);

  // 返回日期数组
  return [...prevMonthDays, ...currentMonthDays, ...nextMonthDays];
},

4. Render the date list

Since we have the function of switching the year and month, we need to cache the current year, month and day, so add a few fields to data

data() {
    
    
	return {
    
    
		// 此处省略已有的属性...
		year: null,
		month: null,
		day: null,
	}
}

Since this method will be used every time the date is switched, it is first extracted and written as a separate method. Before generating the date list each time, the year, month, and day displayed by the current calendar can be cached. The day can be omitted, but I don’t have it here. to omit

/**
  * 渲染日期列表
  * @param year 年
  * @param month 月
  * @param day 日
  */
renderDate(year = this.year, month = this.month, day = this.day) {
    
    
  // 将当前年月日设置为传入的年月日
  this.year = year;
  this.month = month;
  this.day = day;
  // 设置需要渲染的日期列表
  this.dayList = this.getDayList(year, month);
}

5. Simple test

Test whether the current month can be rendered in the created statement cycle function

created() {
    
    
  const {
    
     year, month, day } = this.getCurrentDate(); // 获取今天所在的年月日
  this.renderDate(year, month, day); // 渲染日期列表
}

insert image description here

6. Supplement other details

6.1 Supplementary headings

Just use computed properties:
html part:

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

js part:

computed: {
    
    
  currentDate() {
    
    
    const year = this.year;
    const month = this.month < 10 ? "0" + this.month : this.month;
    return `${
      
      year}${
      
      month}`;
  },
}

6.2 Click to switch the year

Add two tool methods:

  • Get the year and month of the previous year based on the current year and month
  • Get the year and month of the next year based on the current year and month
/**
  * 根据当前年月获取上年的年月
  * @param {Object} year 年
  * @param {Object} month 月
  */
getPrevYearInfo(year, month) {
    
    
  return {
    
    
    prevYear: year - 1,
    month,
  };
},

/**
 * 根据当前年月获取下年的年月
 * @param {Object} year 年
 * @param {Object} month 月
 */
getNextYearInfo(year, month) {
    
    
  return {
    
    
    nextYear: year + 1,
    month,
  };
},

Write logic, very simple and easy to understand


/**
  * 点击上一年
  */
handleSwitchPrevYear() {
    
    
  const {
    
     prevYear, month } = this.getPrevYearInfo(this.year, this.month);
  this.renderDate(prevYear, month);
},

/**
 * 点击下一年
 */
 handleSwitchNextYear() {
    
    
   const {
    
     nextYear, month } = this.getNextYearInfo(this.year, this.month);
   this.renderDate(nextYear, month);
 },

6.3 Click to switch month

No need to write tool methods, we have written them before, this is the subtlety of the calendar component, you can reuse many tool methods to
write logic, very simple and easy to understand

/**
  * 点击上个月
  */
handleSwitchPrevMonth() {
    
    
  const {
    
     prevMonthYear, prevMonth } = this.getPrevMonthInfo(
    this.year,
    this.month
  );
  this.renderDate(prevMonthYear, prevMonth);
},
/**
 * 点击下个月
 */
handleSwitchNextMonth() {
    
    
  const {
    
     nextMonthYear, nextMonth } = this.getNextMonthInfo(
    this.year,
    this.month
  );
  this.renderDate(nextMonthYear, nextMonth);
},

6.4 Add click event

The method of switching the year and month is finished, just add the event

<div class="header">
  <div class="btn-group">
     <div class="btn btn-prev" @click="handleSwitchPrevYear()">
       <span>«</span>
     </div>
     <div class="btn btn-prev" @click="handleSwitchPrevMonth()">
       <span></span>
     </div>
     <div class="current-date">{
   
   { currentDate }}</div>
     <div class="btn btn-next" @click="handleSwitchNextMonth()">
       <span></span>
     </div>
     <div class="btn btn-next" @click="handleSwitchNextYear()">
       <span>»</span>
     </div>
   </div>
 </div>

7. Code summary

<template>
  <div class="calendar">
    <div class="header">
      <div class="btn-group">
        <div class="btn btn-prev" @click="handleSwitchPrevYear()">
          <span>«</span>
        </div>
        <div class="btn btn-prev" @click="handleSwitchPrevMonth()">
          <span></span>
        </div>
        <div class="current-date">{
   
   { currentDate }}</div>
        <div class="btn btn-next" @click="handleSwitchNextMonth()">
          <span></span>
        </div>
        <div class="btn btn-next" @click="handleSwitchNextYear()">
          <span>»</span>
        </div>
      </div>
    </div>
    <div class="body">
      <div class="weeks">
        <div v-for="week in weeks" :key="week" class="week-item">
          {
   
   { week }}
        </div>
      </div>
      <div class="day-list">
        <div
          v-for="(day, index) in dayList"
          :key="index"
          :class="[day.type]"
          class="day-item"
        >
          <span class="text">{
   
   { day.day }}</span>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
      
      
  name: "StaticCalendar",
  data() {
      
      
    return {
      
      
      year: null,
      month: null,
      day: null,
      dayList: [], // 获取需要渲染的日期列表
      weeks: ["日", "一", "二", "三", "四", "五", "六"], // 星期数组
    };
  },
  computed: {
      
      
    currentDate() {
      
      
      const year = this.year;
      const month = this.month < 10 ? "0" + this.month : this.month;
      return `${ 
        year}${ 
        month}`;
    },
  },
  created() {
      
      
    const {
      
       year, month, day } = this.getCurrentDate();
    this.renderDate(year, month, day);
  },
  methods: {
      
      
    /**
     * 渲染日期列表
     * @param year 年
     * @param month 月
     * @param day 日
     */
    renderDate(year = this.year, month = this.month, day = this.day) {
      
      
      // 将当期年月日设置为传入的年月日
      this.year = year;
      this.month = month;
      this.day = day;
      // 设置需要渲染的日期列表
      this.dayList = this.getDayList(year, month);
    },
    /**
     * 生成某年某月的日期列表
     * 由上个月的日期+这个月的日期+下个月的日期组成
     * @param {Object} year 年
     * @param {Object} month 月
     * @returns 数组[日期数组]
     */
    getDayList(year, month) {
      
      
      // 生成上个月的日期列表
      const prevMonthDays = this.getPrevMonthDays(year, month);
      // 生成这个月的日期列表
      const currentMonthDays = this.getCurrentMonthDays(year, month);
      // 生成下个月的日期
      const nextMonthDays = this.getNextMonthDays(year, month);

      // 返回日期数组
      return [...prevMonthDays, ...currentMonthDays, ...nextMonthDays];
    },
    /**
     * 获取某个日期的年月日
     * @returns 对象{年月日}
     */
    getCurrentDate(date = new Date()) {
      
      
      const year = date.getFullYear();
      const month = date.getMonth() + 1;
      const day = date.getDate();

      return {
      
      
        year,
        month,
        day,
      };
    },
    /**
     * 根据当前年月获取上年的年月
     * @param {Object} year 年
     * @param {Object} month 月
     */
    getPrevYearInfo(year, month) {
      
      
      return {
      
      
        prevYear: year - 1,
        month,
      };
    },
    /**
     * 根据当前年月获取下年的年月
     * @param {Object} year 年
     * @param {Object} month 月
     */
    getNextYearInfo(year, month) {
      
      
      return {
      
      
        nextYear: year + 1,
        month,
      };
    },
    /**
     * 根据当前年月获取上个月的年月
     * @param {Object} year 年
     * @param {Object} month 月
     */
    getPrevMonthInfo(year, month) {
      
      
      let prevMonthYear = year;
      let prevMonth = month - 1;

      if (prevMonth < 1) {
      
      
        prevMonth = 12;
        prevMonthYear -= 1;
      }

      return {
      
      
        prevMonthYear,
        prevMonth,
      };
    },
    /**
     * 根据当前年月获取下个月的年月
     * @param {Object} year 年
     * @param {Object} month 月
     */
    getNextMonthInfo(year, month) {
      
      
      let nextMonthYear = year;
      let nextMonth = month + 1;

      if (nextMonth > 12) {
      
      
        nextMonth = 1;
        nextMonthYear += 1;
      }

      return {
      
      
        nextMonthYear,
        nextMonth,
      };
    },
    /**
     * 获取某年某月最后一天
     * @param year 年
     * @param month 月
     * @returns {number}
     */
    getMonthLastDay(year, month) {
      
      
      // 获取下个月的年月
      const {
      
       nextMonthYear, nextMonth } = this.getNextMonthInfo(year, month);
      // 获取下个月1号的时间戳
      const firstDayTimeStamp = new Date(
        `${ 
        nextMonthYear}/${ 
        nextMonth}/1`
      ).getTime();
      // 一天24小时的毫秒数
      const oneDayTimeStamp = 24 * 60 * 60 * 1000;
      // 当月最后一天即为下个月一号减去一天的时间
      return new Date(firstDayTimeStamp - oneDayTimeStamp).getDate();
    },
    /**
     * 获取上个月月末的日期列表
     * @param {Object} year 年
     * @param {Object} month 月
     */
    getPrevMonthDays(year, month) {
      
      
      // 获取上个月的年月
      const {
      
       prevMonthYear, prevMonth } = this.getPrevMonthInfo(year, month);
      // 获取上个月的最后一天
      const prevMonthLastDay = this.getMonthLastDay(prevMonthYear, prevMonth);
      // 获取这个月第一天
      const date = new Date(`${ 
        year}/${ 
        month}/1`);
      // 获取这个月第一天是星期几
      const week = date.getDay();

      const days = [];
      // 生成上个月月末的日期列表
      for (let i = week - 1; i >= 0; i--) {
      
      
        // 从头加入
        days.push({
      
      
          type: "prev-month-day", // 类型
          year: prevMonthYear, // 年
          month: prevMonth, // 月
          day: prevMonthLastDay - i, // 显示的日
        });
      }

      return days;
    },
    /**
     * 获取某年某月的日期列表
     * @param {Object} year 年
     * @param {Object} month 月
     */
    getCurrentMonthDays(year, month) {
      
      
      // 获取当前月的最后一天
      const currentMonthLastDay = this.getMonthLastDay(year, month);
      const days = [];

      for (let i = 1; i <= currentMonthLastDay; i++) {
      
      
        days.push({
      
      
          type: "current-month-day", // 类型
          active: false, // 是否点击选中
          year, // 年
          month, // 月
          day: i, // 显示的日
        });
      }

      return days;
    },
    /**
     * 获取下个月月初的日期列表
     * @param {Object} year 年
     * @param {Object} month 月
     */
    getNextMonthDays(year, month) {
      
      
      // 获取当前月的最后一天
      const currentMonthLastDay = this.getMonthLastDay(year, month);
      // 获取下个月的年和月
      const {
      
       nextMonthYear, nextMonth } = this.getNextMonthInfo(year, month);
      // 获取当前月最后一天是星期几
      const week = new Date(`${ 
        year}/${ 
        month}/${ 
        currentMonthLastDay}`).getDay();

      const days = [];
      let day = 0;
      // 生成下个月月初的日期列表
      for (let i = week + 1; i <= 6; i++) {
      
      
        day++;
        days.push({
      
      
          type: "next-month-day",
          year: nextMonthYear,
          month: nextMonth,
          day,
        });
      }

      return days;
    },
    /**
     * 点击上一年
     */
    handleSwitchPrevYear() {
      
      
      const {
      
       prevYear, month } = this.getPrevYearInfo(this.year, this.month);
      this.renderDate(prevYear, month);
    },
    /**
     * 点击下一年
     */
    handleSwitchNextYear() {
      
      
      const {
      
       nextYear, month } = this.getNextYearInfo(this.year, this.month);
      this.renderDate(nextYear, month);
    },
    /**
     * 点击上个月
     */
    handleSwitchPrevMonth() {
      
      
      const {
      
       prevMonthYear, prevMonth } = this.getPrevMonthInfo(
        this.year,
        this.month
      );
      this.renderDate(prevMonthYear, prevMonth);
    },
    /**
     * 点击下个月
     */
    handleSwitchNextMonth() {
      
      
      const {
      
       nextMonthYear, nextMonth } = this.getNextMonthInfo(
        this.year,
        this.month
      );
      this.renderDate(nextMonthYear, nextMonth);
    },
  },
};
</script>

<style lang="scss" scoped>
$primary-color: #3464e0;

.calendar {
      
      
  background-color: #fff;
}

.header {
      
      
  padding: 0 12px;
  display: flex;
  justify-content: center;
  border-bottom: 1px solid #eee;

  .current-date {
      
      
    text-align: center;
    font-size: 17px;
    padding: 16px 8px;
    color: #030303;
  }

  .btn-group {
      
      
    display: flex;
    align-items: center;
    justify-content: center;

    .btn {
      
      
      display: flex;
      align-items: center;
      justify-content: center;
      background: #fff;
      color: #606266;
      text-align: center;
      box-sizing: border-box;
      font-weight: 500;
      width: 34px;
      height: 26px;
      font-size: 18px;
    }
  }
}

.body {
      
      
  border-bottom: 1px solid #eee;

  .weeks {
      
      
    display: flex;
    font-size: 15px;
    padding: 16px 0;

    .week-item {
      
      
      flex: 1;
      text-align: center;
      font-weight: bold;
    }
  }

  .day-list {
      
      
    display: flex;
    flex-wrap: wrap;

    .day-item {
      
      
      display: flex;
      justify-content: center;
      align-items: center;
      width: 14.285%;
      height: 40px;
      text-align: center;
      padding: 12px 0;
      font-size: 18px;
      color: #c8c8c8;

      &.current-month-day {
      
      
        display: flex;
        align-items: center;
        justify-content: center;
        color: #171717;
        font-weight: bold;
      }

      &.active {
      
      
        .text {
      
      
          display: flex;
          align-items: center;
          justify-content: center;
          width: 40px;
          height: 40px;
          border-radius: 100%;
          box-sizing: border-box;
          background-color: #ddd;
          padding: 14px 0;
        }
      }

      &.active {
      
      
        .text {
      
      
          position: relative;
        }
      }
    }
  }
}
</style>

Summarize

Well, a small and beautiful calendar component has been completed so far. For those who insist on reading it, remember to give it a three-link~
Please add a picture description

Guess you like

Origin blog.csdn.net/m0_46491549/article/details/127502607