vue 实现日历组件

组件代码

<template>
  <div class="air-calendar-box">
    <table class="sn-calendar-table">
      <thead>
        <th class="fc-day-header">日</th>
        <th class="fc-day-header">一</th>
        <th class="fc-day-header">二</th>
        <th class="fc-day-header">三</th>
        <th class="fc-day-header">四</th>
        <th class="fc-day-header">五</th>
        <th class="fc-day-header">六</th>
      </thead>
      <tbody>
        <tr>
          <td 
            v-for="(item, idx) in calendarData"
            :key="idx"
            :style="{'height': height}"
            :class="{
              'include': item.include,
              'active': activeData && item.date === activeData.date
            }"
            @click="clickDay(item)">
            <slot name="day" :row="item">
              <div class="day">
                {{ item.day2 }}
              </div>
            </slot>
          </td>
        </tr>
      </tbody>
    </table>
  </div>
</template>
<script>
export default {
  name: 'AirCalendarBox',
  props: {
    type: {
      // all 可选中显示的所有日期 portion 只能选中当前月份的数据
      type: String,
      default: 'portion'
    },
    // 默认值
    defaultMonth: {
      type: Date,
      default () {
        let year = new Date().getFullYear() // 当月年份
        let month = new Date().getMonth() + 1 // 当月月份
        return new Date(year, month - 1, 1)
      }
    },
    height: {
      type: String,
      default: '52px'
    }
  },
  watch: {
    defaultMonth () {
      this.setDefaultValue()
      this.getCalendarDay()
    }
  },
  data () {
    return {
      calendarData: [],
      activeData: null
    }
  },
  created () {
    this.getCalendarDay()
  },
  methods: {
    // 获取日历天数
    getCalendarDay () {
      let year = new Date(this.defaultMonth).getFullYear() // 当月年份
      let month = new Date(this.defaultMonth).getMonth() + 1 // 当月月份
      let days = new Date(year, month, 0).getDate() // 当月天数

      let prevYear = month === 1 ? (year - 1) : year // 上一年年份
      let prevMonth = month === 1 ? 12 : (month - 1) // 上月月份
      let prevDays = new Date(prevYear, prevMonth, 0).getDate() // 上一月天数

      let lastYear = month === 12 ? (year + 1) : year // 下一年年份
      let lastMonth = month === 12 ? 1 : (month + 1) // 下月月份
      // let lastDays = new Date(lastYear, lastMonth, 0).getDate() // 下一月天数

      let prevMonthArr = [] // 上月数据
      let nextMonthArr = [] // 下月数据
      let currentMonthArr = [] // 当月数据
      // 判断当月1号是星期几
      let firstDay = new Date(year, month - 1, 1).getDay()
      // 获取显示上月的数据
      for (let i = 0; i < firstDay; i++) {
        let day = prevDays - firstDay + (i + 1)
        let day2 = day >= 10 ? day : ('0' + day)
        let month2 = prevMonth >= 10 ? prevMonth : ('0' + prevMonth)
        prevMonthArr.push({
          year: prevYear,
          month: prevMonth,
          month2: month2,
          day: day,
          day2: day2,
          date: `${prevYear}-${prevMonth}-${day}`,
          date2: `${prevYear}-${month2}-${day2}`,
          include: false
        })
      }
      // 获取显示的下一月的数据
      for (let i = 0; i < (42 - firstDay - days); i++) {
        let day = i + 1
        let day2 = day >= 10 ? day : ('0' + day)
        let month2 = lastMonth >= 10 ? lastMonth : ('0' + lastMonth)
        nextMonthArr.push({
          year: lastYear,
          month: lastMonth,
          month2: month2,
          day: day,
          day2: day2,
          date: `${lastYear}-${lastMonth}-${day}`,
          date2: `${lastYear}-${month2}-${day2}`,
          include: false
        })
      }
      // 获取当月显示数据
      for (let i = 1; i <= days; i++) {
        let day2 = i >= 10 ? i : ('0' + i)
        let month2 = month >= 10 ? month : ('0' + month)
        currentMonthArr.push({
          year: year,
          month: month,
          month2: month2,
          day: i,
          day2: day2,
          date: `${year}-${month}-${i}`,
          date2: `${year}-${month2}-${day2}`,
          include: true
        })
      }
      this.calendarData = [...prevMonthArr, ...currentMonthArr, ...nextMonthArr]
    },
    // 设置默认值
    setDefaultValue (date) {
      if (!date) {
        this.activeData = null
        return
      }
      let year = new Date(date).getFullYear() // 当月年份
      let month = new Date(date).getMonth() + 1 // 当月月份
      let month2 = month >= 10 ? month : ('0' + month)
      let day = new Date(date).getDate()
      let day2 = day >= 10 ? day : '0' + day
      let row = {
        year: year,
        month: month,
        month2: month2,
        day: day,
        day2: day2,
        date: `${year}-${month}-${day}`,
        date2: `${year}-${month2}-${day2}`,
        include: true
      }
      this.clickDay(row)
    },
    clickDay (row) {
      if (this.type === 'portion' && !row.include) {
        this.$message({
          type: 'info',
          message: '只能选择当前月份的日期'
        })
        return
      }
      this.activeData = { ...row }
      this.$emit('click', { ...row })
    }
  }
}
</script>
<style lang="less" scoped>
.air-calendar-box {
  .sn-calendar-table {
     100%;
    border-radius: 4px;
    display: block;
    overflow: hidden;
    thead,
    tbody {
      display: flex;
       100%;
    }
    tr {
       100%;
    }
    th {
      background:#D9F5F5;
      height: 32px;
      line-height: 32px;
      text-align: center;
      color: #3E597B;
      box-sizing: border-box;
      display: inline-block;
       14.28%;
    }
    td {
       14.28%;
      display: inline-block;
      position: relative;
      font-size: 16px;
      font-weight: 500;
      box-sizing: border-box;
      text-align: center;
      cursor: pointer;
      border: solid 2px transparent;
      color: rgba(62, 89, 123, 0.3);
      &.include {
        color: #3E597B;
      }
      &.active {
        border: 2px solid #00BBBB;
      }
      .day {
        height: 100%;
        display: flex;
        align-items: center;
        justify-content: center;
      }
      &:nth-child(1),
      &:nth-child(7),
      &:nth-child(8),
      &:nth-child(14),
      &:nth-child(15),
      &:nth-child(21),
      &:nth-child(22),
      &:nth-child(28),
      &:nth-child(29),
      &:nth-child(35),
      &:nth-child(36),
      &:nth-child(42) {
        background: #EBFAFA;
      }
    }
    tbody {
      .clickDay {
        border: solid 2px #00BBBB;
      }
    }
  }
}

</style>

// 页面效果

原文地址:https://www.cnblogs.com/fczbk/p/14985259.html