美文网首页
vue中日期与日历联动组件(自定义日历组件)

vue中日期与日历联动组件(自定义日历组件)

作者: 随遇而安丶 | 来源:发表于2022-05-05 16:19 被阅读0次

因为项目需要用到日历组件,没有找到适用的组件,只能无奈自己写一个了,达到项目使用所需的功能,同时保留了较高的扩展性。

先贴上效果图

screenshots.gif

具体day的点击事件逻辑就不贴出来了,有需要的根据自己的项目需求加上click即可

代码分享

-父组件代码

有使用到 elementui 的 el-button el-date-picker,照抄的同学记得下载依赖
<template>
  <div>
    <div class="top clearfix">
      <div class="top-left fl">
        <div class="tlt clearfix">
          <span class="title1 fl">我的课表</span>
          <el-button class="fr ml10" type="primary">下载排课表</el-button>
          <div class="block fr">
            <el-date-picker
              v-model="value"
              type="month"
              :clearable=false
              format="yyyy年 MM月"
              @change="changeDate()"
              placeholder="查看月份">
            </el-date-picker>
          </div>
        </div>
        <Calendar :defaultMonth.sync='defaultMonth'></Calendar>
      </div>
    </div>
  </div>
</template>

<script>
import Calendar from '@/components/Calendar'

export default {
  components:{Calendar},
  data() {
    return {
      value:'',
      defaultMonth:''
    }
  },
  methods: {
    changeDate(){
      this.defaultMonth = this.value
    }
  },
  created(){
    let time = new Date()
    this.value = time
    this.defaultMonth = time
  }
}
</script>

<style lang="stylus" scoped>
.clearfix::before,
.clearfix::after {
  content: '';
  display: table;
  clear: both;
}
.fl{
  float:left;
}
.fr{
  float:right;
}
.title1{
  color: #333
  font-size: 20px;
  font-weight 900
}
.top-left
  height 750px
  width 70%
  background-color #fff
  padding:20px;
  border-radius: 10px;
  &>.tlt
    line-height 40px;
    padding-bottom:10px;
</style>

-子组件代码

<template>
  <!-- 日历组件 -->
  <div class="calendar-box">
    <table class="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, index) in calendarData"
            :key="index"
            :style="{'height': height}"
            @click="clickDay(item)">
            <div class="includeContent"
              :class="{
                'include': item.include,
                'active': defaultDate.indexOf(item.date)>-1
              }"
            >
              <slot name="day" :row="item">
                <div class="day">
                  <span class="d_one">{{ item.day2 }}</span>
                  <span class="d_two" v-if="defaultDate.indexOf(item.date)>-1">课</span>
                  <span class="d_three" v-if="defaultDate.indexOf(item.date)>-1" v-text="courseList[defaultDate.indexOf(item.date)]"></span>
                </div>
              </slot>
            </div>
          </td>
        </tr>
      </tbody>
    </table>
  </div>
</template>
<script>
export default {
  name: 'calendarBox',
  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: { //设置td高度
      type: String,
      default: '94px'
    }
  },
  data () {
    return {
      calendarData: [],
      activeData: null,
      defaultDate:['2022-4-1','2022-4-7','2022-4-13','2022-4-18','2022-4-26','2022-4-28','2022-5-5','2022-5-15','2022-5-18','2022-5-20','2022-5-21','2022-5-30'],
      courseList:['美术课','武器课','瑜伽课','音乐课','吃饭','睡觉','看电影','逛街','考试','答辩','毕业','结婚'],
      // allDate:[  //这里是模拟数据,一般是从后台接口获取而来,需要自己获取的时候拆分成 defaultDate 和 courseList
      //   {date:'2022-4-1',course:'美术课'},
      //   {date:'2022-4-7',course:'武器课'},
      //   {date:'2022-4-13',course:'瑜伽课'},
      //   {date:'2022-4-18',course:'音乐课'},
      //   {date:'2022-4-26',course:'吃饭'},
      //   {date:'2022-4-28',course:'睡觉'},
      //   {date:'2022-5-5',course:'看电影'},
      //   {date:'2022-5-13',course:'逛街'},
      //   {date:'2022-5-14',course:'毕业'},
      //   {date:'2022-5-20',course:'答辩'},
      //   {date:'2022-5-25',course:'结婚'},
      // ] 
    }
  },
  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.closeAll()
        this.$message({
          type: 'info',
          message: '只能选择当前月份的日期'
        })
        return
      }
      this.activeData = { ...row }
      this.$emit('click', { ...row })
      // console.log(this.activeData);
    }
  },
  watch: {
    defaultMonth(){
      this.setDefaultValue()
      this.getCalendarDay()
    }
  }
}
</script>
<style lang="stylus" scoped>
$blue = #0176F6
.calendar-box {
  background-color #f6f6f6
  padding 10px;
  .calendar-table {
    width: 100%;
    border-radius: 4px;
    display: block;
    overflow: hidden;
    thead,
    tbody {
      display: flex;
      width: 100%;
    }
    tr {
      width: 100%;
    }
    th {
      height: 50px;
      line-height: 50px;
      text-align: center;
      color: #3E597B;
      box-sizing: border-box;
      display: inline-block;
      width: 14.28%;
    }
    td {
      width: 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);
      background-color: #f5f5f5;
      padding: 5px;
        &>.includeContent{
          border: 1px solid #DDD;
          height 80px;
          &.include {
            color: #3E597B;
            background-color: #fff;
          }
          &.active {
            border: 2px solid $blue;
            background: #BCDCFF;
            color: $blue;
          }
          &:hover{
            border: 2px solid $blue;
          }
      }
      .day {
        height: 100%;
        display: flex;
        align-items: center;
        justify-content: center;
        position: relative;
        &>.d_one{
          position: absolute
          top 5px
          left 5px
        }
        &>.d_two{
          position: absolute
          top 5px
          right 5px
        }
        &>.d_three{
          width 100%
          position: absolute
          top 40px
          left 50%;
          transform: translateX(-50%)
        }
      }
      &: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: #f6f6f6;  //根据自己需要,设置td周末的背景颜色
      }
    }
    tbody {
      .clickDay {
        border: solid 2px #00BBBB;
      }
    }
  }
}

</style>

使用注意事项

1.<style lang="stylus" scoped>这里我用的是stylu
2.子组件props中的 type 和 height 我在父组件中没有传,使用的是子组件的默认值
3.data(){} 中 defaultDate、courseList、allDate参数请根据项目实际获取参数使用

结束 over

相关文章

网友评论

      本文标题:vue中日期与日历联动组件(自定义日历组件)

      本文链接:https://www.haomeiwen.com/subject/kqtoyrtx.html