基于vue+moment+ant-design-vue+less的自定日历组件

介绍:
antdv日历组件默认支持单选,range选择,不支持多选,应对多选日历的需求基于vue+moment+ant-design-vue自定义日历组件,支持多选和单选;
实现核心逻辑:

  1. 日历第一行:以一周七天为周期,计算当前选中月份一号前后有几天,一号之前的日期要做置灰处理(enable属性为false),一号之后的日期进行正常加入日期列表(enable属性为true);
  2. 日历中间行:以一周七天为单位,使用向下取整计算方法计算出中间有几行数据,循环遍历生成中间行数如下图红框内数据:
    基于vue+moment+ant-design-vue+less的自定日历组件_第1张图片
  3. 日历最后一行:首先获取当前月份有多少天,拿当前月天数减去上面两个步骤计算出来的天数得到最后一行有几天,再以七天为周期计算最后一行属于下个月的天数,逻辑同步骤1;
    具体逻辑见下方代码:
<!-- 自定义日历表,支持多选,单选模式切换 -->
<template>
  <div>
    <div class="head f-r">
      <span>
        <a-select
          :options="years"
          style="width: 120px"
          v-model="year"
          @change="changeYear"
        >
        </a-select>
      </span>
      <span>
        <a-select
          :options="months"
          style="width: 120px"
          v-model="month"
          @change="changeMonth"
        >
        </a-select>
      </span>
    </div>
    <table>
      <thead>
        <tr><th v-for="item in weeks" :key="item.value">{{ item.label }}</th></tr>
      </thead>
      <tbody>
        <tr
          v-for="(item, index) in days"
          :key="index"
        >
          <td
            v-for="(cell, index) in item"
            :key="index"
            :class="
              [cell.enable ? 'enable' : 'disable',
               ((cell.value.getDay() === 0 || cell.value.getDay() === 6) && cell.enable) ? 'redcolor' : ''
              ]"
            @click="selectEvent(cell.value, cell.enable)"
          >
            <span
              class="signIcon"
              v-if="((module === 'muilt' && selectedArr.includes(moment(cell.value).format('YYYY-MM-DD')) && cell.enable) || (module === 'default' && selected === moment(cell.value).format('YYYY-MM-DD') && cell.enable))"
            ><a-icon style="color: #19BE6B;font-size: 14px;" type="check-circle" theme="filled" /></span>
            {{ cell.label }}
          </td>
        </tr>
      </tbody>
    </table>
  </div>
</template>

<script>
import moment from 'moment'
export default {
  props: { // 支持muilt(多选)、default(单选)
    module: {
      type: String,
      default: null
    }
  },
  name: 'Calendar',
  data () {
    return {
      weeks: [
        { label: '周日', value: 0 },
        { label: '周一', value: 1 },
        { label: '周二', value: 2 },
        { label: '周三', value: 3 },
        { label: '周四', value: 4 },
        { label: '周五', value: 5 },
        { label: '周六', value: 6 }
      ],
      years: [],
      months: [],
      days: [],
      year: null,
      month: null,
      selected: '',
      selectedArr: [],
      moment
    }
  },
  methods: {
    // 选择年份
    changeYear () {
       this.initDays()
    },
    // 选择月份
    changeMonth () {
       this.initDays()
       console.log(this.selectedArr, '已选项')
    },
    selectEvent (val, enable) {
      if (!enable) {
        return
      }
       const date = this.moment(val).format('YYYY-MM-DD')
       console.log(date, '返回date')
       if (this.module === 'muilt') {
        if (this.selectedArr.includes(date)) {
          const index = this.selectedArr.findIndex(item => item === date)
          this.selectedArr.splice(index, 1)
          return
        }
        this.selectedArr.push(date)
       } else {
        if (this.selected === date) {
          this.selected = ''
        } else {
          this.selected = date
        }
       }
    },
    initYears () {
      // 默认获取前后十年的年份列表
      const now = new Date()
      const nowYear = now.getFullYear()
      const nowMonth = now.getMonth() + 1
      this.years.push({ label: `${nowYear}`, value: nowYear })
      for (let i = 1; i < 11; i++) {
         this.years.unshift({ label: `${nowYear - i}`, value: (nowYear - i) })
         this.years.push({ label: `${nowYear + i}`, value: nowYear + i })
      }
      this.year = nowYear
      this.month = nowMonth
    },
    // 初始化月份
    initMonths () {
      for (let i = 0; i < 12; i++) {
        this.months.push({ label: `${i + 1}`, value: i + 1 })
      }
    },
    // 初始化日期,初始化日历数据函数
    initDays () {
      this.days = []
      const allDays = new Date(this.year, this.month, 0).getDate() // 获取选中月份有多少天
      const start = new Date(`${this.year}-${this.month}-1`).getDay()
      const afterStart = 6 - start // 以一周七天为限 计算当前月1号后面有几天
      const beforeStart = 7 - (afterStart + 1) // 以一周七天为限 计算当前月1号之前有几天
      const data = []
      // 处理日历第一行日期数据
      for (let i = 0; i <= afterStart; i++) {
          const date = new Date(`${this.year}-${this.month}-${1 + i}`).getDate()
          data.push({ label: date, value: new Date(`${this.year}-${this.month}-${date}`), enable: true })
      }
      const upMonthDate = new Date(this.year, this.month - 1, 0).getDate() // 获取上个月有多少天
      for (let i = 0; i < beforeStart; i++) {
          const date = new Date(`${this.year}-${this.month - 1}-${upMonthDate - i}`).getDate()
          data.unshift({ label: date, value: new Date(`${this.year}-${this.month - 1}-${date}`), enable: false })
      }
      this.days.push(data)
      // 处理日历中间日期数据
      const rows = Math.floor((allDays - (afterStart + 1)) / 7) // 获取本月在日历中完整的一周时间有几行
      for (let i = 0; i < rows; i++) { // 生成完整一周时间的日期表
        const data = []
        for (let j = 1; j <= 7; j++) {
           data.push({ label: afterStart + 1 + j + i * 7, value: new Date(`${this.year}-${this.month}-${afterStart + 1 + j + i * 7}`), enable: true })
        }
        this.days.push(data)
      }
      // 处理最后一行日历日期数据
      const lastlength = allDays - (afterStart + 1 + rows * 7)
      const arr = []
      for (let i = 1; i <= lastlength; i++) {
         arr.push({ label: afterStart + 1 + rows * 7 + i, value: new Date(`${this.year}-${this.month}-${afterStart + 1 + rows * 7 + i}`), enable: true })
      }
      const lastDisables = 6 - new Date(`${this.year}-${this.month}-${afterStart + 1 + rows * 7 + lastlength}`).getDay()
      for (let j = 0; j < lastDisables; j++) {
        arr.push({ label: j + 1, value: new Date(`${this.year}-${this.month + 1}-${j + 1}`), enable: false })
      }
      this.days.push(arr)
    }
  },
  created () {
    this.initMonths()
    this.initYears()
    this.initDays()
  }
}
</script>

<style lang="less" scoped>
table {
  width: 100%;
  thead {
    th {
      text-align: center;
    }
  }
  tbody {
    td {
      border: 1px solid #e8e8e8;
      text-align: center;
      padding: 10px;
      cursor: pointer;
      position: relative;
    }
    .disable {
      color: #e8e8e8;
      cursor: not-allowed;
    }
    .redcolor {
      color: #fa6a54;
    }
    .active {
      background: red;
    }
    .signIcon {
      position: absolute;
      right: 0;
      top: 0px;
    }
  }
}
</style>

你可能感兴趣的:(vue,vue.js,less,javascript)