今天给大家介绍一下,如何在vue中实现自定义日历组件,功能类似FullCalendar插件,但是却比较灵活。

先给大家看一下组件的效果图,如下所示:

 

自定义vue日历组件_vue

可以在每天的日期中添加相应的内容,内容可以是自己定义的,样式也可以是自己定义的,还有就是添加了复选框的功能。例如我用日历来体现每天价格,如下图所示:

自定义vue日历组件_vue_02

下面来具体介绍一下是怎么实现这个过程的,这次重点讲这个组件的实现过程,涉及其它的先暂时不讲,有疑问的可以找我私聊。

具体代码如下所示:

<template>
  <div>
    <div class="cf setcal">
      <div class="fl setcal-a">
        <el-button type="text" @click="setCalendar(false);getData();setCalendarDayClear()"><i
          class="el-icon-caret-left"></i>{{month == 1 ? 12 : month - 1}}{{$t('calendar.month')}}
        </el-button>
      </div>
      <div class="fl setcal-b">{{year}}{{$t('calendar.year')}}{{month}}{{$t('calendar.month')}}  
        <template>
        <el-checkbox v-model="checkedAll" @change="checkAll"></el-checkbox>
      </template>
      </div>
      <div class="fr setcal-a">
        <el-button type="text" @click="setCalendar(true);getData();setCalendarDayClear()">{{month == 12 ? 1 : month + 1}}{{$t('calendar.month')}}<i
          class="el-icon-caret-right"></i></el-button>
      </div>
    </div>
    <div class="tableHead cf">
      <div class="fl" v-for="(item,index) in internationalWeeks" :key="item.id">{{item.zh}} 
        <template>
        <el-checkbox v-model="item.checked" @change="checkWeekAll(item, index)"></el-checkbox>
      </template>
      </div>
    </div>
    <div v-if="loadInitCalendar">
      <template>
        <el-table :data="calendarTable" border style="width: 100%" :show-header="false">
          <el-table-column v-for="(item, index) in weeks" :key="item.id">
            <template slot-scope="scope">
              <div class="calendar-item" v-if="scope.row[item.en].data">
                <div class="cf">
                  <div class="fl">{{scope.row[item.en].data}}</div>
                  <div class="fr" v-if="checkTodayDateLater(year+'-'+month+'-'+scope.row[item.en].data)<=0">
                    <template>
                      <el-checkbox v-model="scope.row[item.en].checked"
                                   @change="checkDay(index,scope.$index);setData(year, month, scope.row[item.en],item.en)"></el-checkbox>
                    </template>
                  </div>
                </div>
                <div class="calendar-item-invenroty" v-if="scope.row[item.en].info.show"
                     :style="{color: checkTodayDateLater(year+'-'+month+'-'+scope.row[item.en].data)<=0 ? '#1D8CE0' : '#8492A6'}">
                  <p>{{$t('calendar.price')}}:{{scope.row[item.en].info.price}}</p>
                  <p>{{$t('calendar.salePrice')}}:{{scope.row[item.en].info.salePrice}}</p>
                  <p>{{$t('calendar.num')}}:{{scope.row[item.en].info.saleNum}}/{{scope.row[item.en].info.num}}</p>
                </div>
                <div class="calendar-item-invenroty" v-else style="opacity: 0">
                  <p>{{$t('calendar.price')}}:999</p>
                  <p>{{$t('calendar.salePrice')}}:999</p>
                  <p>{{$t('calendar.num')}}:0/0</p>
                </div>
              </div>
            </template>
          </el-table-column>
        </el-table>
      </template>
    </div>
  </div>
</template>

<script>
  import hotelProductPrice from '@/api/product/hotelProductPrice'
  import { parseTime } from '@/utils/index'
  function initCalendar(year, month) {
    var monthFirstDay = new Date(year, month - 1, 1)
    var firstDay = monthFirstDay.getDay() // 返回本月1号是星期几
    var monthLastDay = new Date(year, month, 0)
    var lastDay = monthLastDay.getDate()// 返回本月共有多少天,同时避免复杂的判断润年不润年
    var week = ['seven', 'one', 'two', 'three', 'four', 'five', 'six']
    var weekIndex = 0
    var dateList = []
    var dateData = {}
    var dateDataTemp = {}
    for (var i = 1; i <= 42; i++) {
      weekIndex++
      dateDataTemp = {}
      if (firstDay < i && i <= (lastDay + firstDay)) {
        const date = i - firstDay
        dateDataTemp.data = date// 几号对应星期几 o
        dateDataTemp.checked = false
      } else {
        dateDataTemp.data = false// 没数据
      }
      dateDataTemp.info = {
        show: false,
        price: '',
        salePrice: '',
        num: '',
        saleNum: '',
        id: ''
      }
      dateData[week[(weekIndex - 1) % 7]] = dateDataTemp
      if (i % 7 === 0) { // 新的一周
        dateList.push(dateData)
        dateData = {}
        weekIndex = 0
      }
    }
    let ifPop = true
    for (const key in dateList[5]) {
      // 日历第六行有数据
      if (dateList[5][key].data) {
        ifPop = false
        break
      }
    }
    ifPop ? dateList.pop() : ''
    return dateList
  }

  export default {
    props: ['productId', 'distributorMchId'],
    name: 'Calendar',
    data() {
      return {
        infoId: '',
        loadInitCalendar: false,
        calendarTable: [{
          seven: { data: '', checked: false },
          one: { data: '', checked: false },
          two: { data: '', checked: false },
          three: { data: '', checked: false },
          four: { data: '', checked: false },
          five: { data: '', checked: false },
          six: { data: '', checked: false }
        }],
        year: 0,
        month: 0,
        checkedAll: false,
        checkedAllLen: 0,
        checkedAllNum: 0,
        weeks: [
          { id: '7', en: 'seven', zh: '日', checked: false, checkedNum: 0, checkedLen: 0 },
          { id: '1', en: 'one', zh: '一', checked: false, checkedNum: 0, checkedLen: 0 },
          { id: '2', en: 'two', zh: '二', checked: false, checkedNum: 0, checkedLen: 0 },
          { id: '3', en: 'three', zh: '三', checked: false, checkedNum: 0, checkedLen: 0 },
          { id: '4', en: 'four', zh: '四', checked: false, checkedNum: 0, checkedLen: 0 },
          { id: '5', en: 'five', zh: '五', checked: false, checkedNum: 0, checkedLen: 0 },
          { id: '6', en: 'six', zh: '六', checked: false, checkedNum: 0, checkedLen: 0 }
        ]
      }
    },
    computed: {
      internationalWeeks() {
        return [
          { id: '7', en: 'seven', zh: this.$t('calendar.sunday'), checked: false, checkedNum: 0, checkedLen: 0 },
          { id: '1', en: 'one', zh: this.$t('calendar.monday'), checked: false, checkedNum: 0, checkedLen: 0 },
          { id: '2', en: 'two', zh: this.$t('calendar.tuesday'), checked: false, checkedNum: 0, checkedLen: 0 },
          { id: '3', en: 'three', zh: this.$t('calendar.wednesday'), checked: false, checkedNum: 0, checkedLen: 0 },
          { id: '4', en: 'four', zh: this.$t('calendar.thursday'), checked: false, checkedNum: 0, checkedLen: 0 },
          { id: '5', en: 'five', zh: this.$t('calendar.friday'), checked: false, checkedNum: 0, checkedLen: 0 },
          { id: '6', en: 'six', zh: this.$t('calendar.saturday'), checked: false, checkedNum: 0, checkedLen: 0 }]
      }
    },
    methods: {
      checkTodayDateLater(sDate2) { // sDate1和sDate2是2006-12-18格式
        var date = new Date()
        var sDate1 = date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate()
        var aDate, oDate1, oDate2, iDays
        aDate = sDate1.split('-')
        oDate1 = new Date(aDate[1] + '-' + aDate[2] + '-' + aDate[0])// 转换为12-18-2006格式
        aDate = sDate2.split('-')
        oDate2 = new Date(aDate[1] + '-' + aDate[2] + '-' + aDate[0])
        iDays = parseInt((oDate1 - oDate2) / 1000 / 60 / 60 / 24)// 把相差的毫秒数转换为天数
        return iDays
      },
      setCalendar(w) {
        if (w) {
          if (this.month === 12) {
            this.month = 1
            this.year++
          } else {
            this.month++
          }
        } else {
          if (this.month === 1) {
            this.month = 12
            this.year--
          } else {
            this.month--
          }
        }
        this.calendarTable = initCalendar(this.year, this.month)
        this.setCheckedLen()
        this.$emit('setCalendarDayClear')
      },
      // 每个复选框
      checkDay(x, y) {
        const week = this.weeks[x]
        this.calendarTable[y][week.en].checked ? week.checkedNum++ : week.checkedNum--
        this.calendarTable[y][week.en].checked ? this.checkedAllNum++ : this.checkedAllNum--
        week.checked = week.checkedNum === week.checkedLen
        this.checkedAll = this.checkedAllNum === this.checkedAllLen
      },
      // 全选
      checkAll() {
        this.checkedAllNum = this.checkedAll ? this.checkedAllLen : 0
        for (let j = 0; j < this.weeks.length; j++) {
          const week = this.weeks[j]
          week.checked = this.checkedAll
          week.checkedNum = week.checked ? week.checkedLen : 0
        }
        for (let i = 0; i < this.calendarTable.length; i++) {
          const obj = this.calendarTable[i]
          for (const key in obj) {
            if (obj[key].data) {
              obj[key].checked = this.checkedAll
            }
          }
        }
        const _this = this
        this.calendarTable.forEach(function(value, index) {
          const week = ['seven', 'one', 'two', 'three', 'four', 'five', 'six']
          week.forEach(item => {
            if (value[item].data) {
              if (_this.checkTodayDateLater(_this.year + '-' + _this.month + '-' + value[item].data) <= 0) {
                _this.setData(_this.year, _this.month, value[item], item)
              }
            }
          })
        })
      },
      // 周x全选
      checkWeekAll(item, index) {
        const week = this.weeks[index]
        week.checked ? this.checkedAllNum += (week.checkedLen - week.checkedNum) : this.checkedAllNum -= week.checkedLen
        week.checkedNum = week.checked ? week.checkedLen : 0
        this.checkedAll = this.checkedAllNum === this.checkedAllLen
        for (let i = 0; i < this.calendarTable.length; i++) {
          this.calendarTable[i][week.en].checked = week.checked
        }
        const _this = this
        this.calendarTable.forEach(function(value, index) {
          if (_this.checkTodayDateLater(_this.year + '-' + _this.month + '-' + value[item.en].data) <= 0) {
            _this.setData(_this.year, _this.month, value[item.en], item.en)
          }
        })
      },
      // 周有多少个 用于全选
      setCheckedLen() {
        const tempObj = {
          seven: { checkedLen: 0 },
          one: { checkedLen: 0 },
          two: { checkedLen: 0 },
          three: { checkedLen: 0 },
          four: { checkedLen: 0 },
          five: { checkedLen: 0 },
          six: { checkedLen: 0 }
        }
        const _this = this
        let checkedAllLen = 0
        let flag = false
        for (let i = 0; i < this.calendarTable.length; i++) {
          const obj = this.calendarTable[i]
          for (const key in obj) {
            if (!flag) {
              if (_this.checkTodayDateLater(_this.year + '-' + _this.month + '-' + obj[key].data) <= 0) {
                flag = true
              }
            }
            if (obj[key].data && flag) {
              tempObj[key].checkedLen++
              checkedAllLen++
            }
          }
        }
        this.checkedAllLen = checkedAllLen
        this.checkedAllNum = 0
        this.checkedAll = false
        for (let j = 0; j < this.weeks.length; j++) {
          const week = this.weeks[j]
          week.checkedLen = tempObj[this.weeks[j].en].checkedLen
          week.checked = false
          week.checkedNum = 0
        }
      },
      getData(infoid) {
        if (infoid) {
          this.infoId = infoid
        }
        const week = ['seven', 'one', 'two', 'three', 'four', 'five', 'six']
        hotelProductPrice.list(this.productId, this.distributorMchId, this.year, this.month).then(res => {
          this.loadInitCalendar = true
          const result = res.data
          const _this = this
          if (result) {
            result.forEach(function(apiValue, apiIndex) {
              let priceDate = parseTime(apiValue.priceDate, '{y}-{m}-{d}')
              priceDate = Number(priceDate.split('-')[2])
              _this.calendarTable.forEach(function(value, index) {
                week.forEach(item => {
                  if (value[item].data && value[item].data === priceDate) {
                    value[item].info = {
                      show: true,
                      id: apiValue.id,
                      price: apiValue.priceCodex,
                      salePrice: apiValue.salePriceCodex,
                      num: apiValue.stock,
                      saleNum: apiValue.saleNum
                    }
                  }
                })
              })
            })
          }
        })
      },
      setData(year, month, obj, week) {
        this.$emit('setData', year, month, obj, week)
      },
      setCalendarDayClear() {
        this.$emit('setCalendarDayClear')
      },
      initData(year, month) {
        this.calendarTable = initCalendar(year, month)
        this.setCheckedLen()
        this.getData()
      }
    },
    mounted() {
      var date = new Date()// 系统时间对象
      this.year = date.getFullYear()// 完整的年份,千万不要使用getYear,firfox不支持
      this.month = date.getMonth() + 1// 注意获取的月份比实现的小1
      this.calendarTable = initCalendar(this.year, this.month)
      this.setCheckedLen()
      this.getData()
    }
  }
</script>

<style scoped>
  .setcal {
    line-height: 20px;
    border: 1px solid #D3DCE6;
    line-height: 50px;
  }

  .setcal-a {
    text-align: center;
    width: 20%;
  }

  .setcal-b {
    width: 60%;
    text-align: center;
    border-right: 1px solid #D3DCE6;
    border-left: 1px solid #D3DCE6;
  }

  .calendar-item {
    padding: 5px;
  }

  .calendar-item-invenroty {
    font-size: 12px;
  }

  .tableHead div {
    width: calc(100% / 7);
    text-align: center;
    line-height: 3;
    background: #eef1f6;
    font-weight: bold;
  }

  p {
    margin: 0;
    padding: 0;
  }
</style>

具体代码就是上面所示,该日历组件还实现了中英文切换的功能。

因为时间仓促,所以不能一个个分析代码和原理,只能先贴出具体的代码

自定义vue日历组件_vue_03