日历组件

image.png

日历组件在平时项目中是非常常见的一个组件,那么我们就来实现一下。实现之前要知道有哪几个需要注意的点。
-一、日期要怎么显示?
每个月需要展示不同的日期,那么我们就来找找规律吧。
1.每个月需要展示固定的42天。
2.每个月的一号是周几,比如上图中的一号是周二,那么我们可以使用时间的getDay函数获取一号的是2,然后让第一天往前推2天,就是我们需要展示在日历上的第一天了。

  • 二、日期框里的年月不能根据输入框里的日期变化,因为后面还会涉及到切换问题
  • 三、每次切换都要更新日历板中的日期,使其与对应月份展示的相同
  • 四、每次点击日历中的日期需要隐藏日历板(点击切换年月按钮不隐藏)
  • 五、点击日历板上的日期之后再次点击输入框会展示对应月份对应时间,对应时间需要给一个特殊展示(如上图蓝色部分)
  • 六、不是当月的时间需要和当前月的日期有区分(颜色区分)
  • 七、选中了某一天需要更新父组件中的数据
  • 八、输入框中录入年月日,点击enter键,再次点击input会弹出对应的日期并且标蓝色

上面就是封装这个日历所需要注意到的点,那么下面看代码吧;

代码实现

父组件
<template>
  <div id="datePicker">
    <Calendar :time="time" v-model="time"></Calendar>
    <div style="display: inline-block">{{time}}</div>
  </div>
</template>
<script>
  import Calendar from '../components/calendar';

  export default {
    name: 'datePicker',
    components: {
      Calendar
    },
    data() {
      return {
        time: new Date()
      };
    },
    methods: {}
  };
</script>
<style>
</style>

子组件

<template>
  <div id="calendar" v-click-outside>
    <input type="text" ref="input" class="input" :value="formatterTime" @focus="handlClick" @keydown="enterInput">
    <div class="content" v-if="isShow">

      <div class="title">
        <div class="oper">
          <span class="iconfont icon-shuangjiantouzuo" @click="handlChangeDate('year',-1)"></span>
          <span class="iconfont icon-zuojiantou" @click="handlChangeDate('month',-1)"></span>
        </div>
        <div class="date">{{`${timeCopy.year}年${timeCopy.month}月`}}</div>
        <div class="oper">
          <span class="iconfont icon-youjiantou" @click="handlChangeDate('month',+1)"></span>
          <span class="iconfont icon-shuangjiantouyou" @click="handlChangeDate('year',+1)"></span>
        </div>
      </div>
      <div class="week">
        <span class="week-item" v-for="(v,i) in weekList" :key="i">{{v}}</span>
      </div>
      <ul class="days">
        <li class="days-row" v-for="(v,i) in 6" :key="i">
              <span class="days-item" v-for="(vs,j) in 7" :key="j"
                    @click="chooseDay(getCurrentMonthDays[i*7+j])"
                    :class="[
                    {'gray-day':getCurrentMonthDays[i*7+j].getMonth()+1!=timeCopy.month},
                    {'high-light':getCurrentMonthDays[i*7+j].getDate()==timeCopy.day}
                    ]"
              >
                {{getCurrentMonthDays[i*7+j].getDate()}}
              </span>
        </li>
      </ul>
    </div>
  </div>
</template>

<script>
  export default {
    name: 'calendar',
    props: {
      time: {type: Date}
    },
    directives: {
      'clickOutside': {
        bind(el, builing, vNode) {
          let handler = (e) => {
            let input = document.getElementsByClassName('input')[0];
            let title = document.getElementsByClassName('title')[0];
            if (!title) return;
            if (!input.contains(e.target) && !title.contains(e.target)) {
              vNode.context.isShow = false;
            }
          };
          document.addEventListener('click', handler);
        }
      }
    },
    computed: {

      formatterTime() {
        let {year, month, day} = this.timeCopy;
        return `${year}-${month}-${day}`;
      },
      getCurrentMonthDays() {
        let {year, month} = this.timeCopy;
        let firstDay = new Date(`${year}-${month}-01`);
        let week = firstDay.getDay();
        let beginDay;
        if (week === 0) {
          beginDay = firstDay - 7 * 1000 * 60 * 60 * 24;
        } else {
          beginDay = firstDay - week * 1000 * 60 * 60 * 24;
        }
        let dateArr = [];
        for (let i = 0; i < 42; i++) {
          dateArr.push(new Date(beginDay + i * 1000 * 60 * 60 * 24));
        }
        return dateArr;
      }
    },
    data() {
      return {
        isShow: false,
        timeCopy: {},
        weekList: ['日', '一', '二', '三', '四', '五', '六']
      };
    },
    methods: {
      enterInput(e) {
        if (e.keyCode == '13') {
          let val = e.srcElement.value;
          let {month, day} = this.setData(new Date(val));
          if (isNaN(month) || isNaN(day)) {
            alert('输入不合法哦');
            return;
          }
          this.timeCopy = this.setData(new Date(e.srcElement.value));
          this.$emit('input', new Date(e.srcElement.value));
          this.isShow = false;
          this.$refs.input.blur();
        }
      },
      handlChangeDate(flag, val) {
        this.timeCopy[flag] += val;
        if (this.timeCopy.month > 12) {
          this.timeCopy.month = 1;
          this.timeCopy.year += 1;
        }
        if (this.timeCopy.month < 1) {
          this.timeCopy.month = 12;
          this.timeCopy.year -= 1;
        }
      },
      chooseDay(date) {
        this.$emit('input', date);
        this.timeCopy = this.setData(date);
      },
      setData(time) {
        let year = time.getFullYear();
        let month = time.getMonth() + 1;
        let day = time.getDate();
        return {year, month, day};
      },
      handlClick() {
        this.isShow = true;
      }
    },
    created() {
      this.timeCopy = this.setData(this.time);
    }
  };
</script>

<style lang="less" scoped>
  * {
    margin: 0;
    padding: 0;
  }

  ul li {
    list-style: none;
  }

  #calendar {
    @commonColor: #00a0ff;
    text-align: center;
    display: inline-block;
    input {
      width: 268px;
      background-color: #fff;
      border-radius: 4px;
      border: 1px solid #dcdfe6;
      color: #606266;
      height: 40px;
      line-height: 40px;
      outline: none;
      padding: 0 15px;
    }
    .content {
      user-select: none;
      position: absolute;
      width: 300px;
      z-index: 100;
      background: #fff;
      color: #606266;
      border: 1px solid #e4e7ed;
      box-shadow: 0 2px 12px 0 rgba(0, 0, 0, .1);
      border-radius: 4px;
      line-height: 30px;
      margin: 5px 0;
      .title {
        display: flex;
        height: 25px;
        line-height: 25px;
        margin-bottom: 15px;
        .oper {
          font-size: 16px;
          cursor: pointer;
          flex: 1;
        }
        .date {
          flex: 1;
        }
      }
      .week {
        display: flex;
        border-bottom: 1px solid #bfc4cc;
        .week-item {
          flex: 1;
          font-size: 14px;
        }
      }
      .days {
        display: flex;
        flex-direction: column;
        padding: 5px;
        font-size: 12px;
        justify-content: space-evenly;
        .days-row {
          display: flex;
          .days-item {
            flex: 1;
            cursor: pointer;
            &:hover {
              color: @commonColor;
            }
          }
        }
      }
    }
    .gray-day {
      color: #bfc4cc !important;
    }
    .high-light {
      color: @commonColor;
    }
  }
</style>

mengyuhang4879
13 声望7 粉丝

« 上一篇
深拷贝
下一篇 »
菜单权限