zoukankan      html  css  js  c++  java
  • Vue 组件(component)之 精美的日历

    公司的要求,需要开发一个精美的日历组件(IOS , 安卓, PC 的IE9+都能运行),写完后想把它分享出来,希望大家批评()。

    先来个截图 代码已经分享到 https://github.com/zhangKunUserGit/vue-component
       

    根据需求先说一下怎么用吧 (上面是:HTML, 下面是JS )

    <date-picker
        v-if="showDatePicker"
        :date="date"
        :min-date="minDate"
        :max-date="maxDate"
        @confirm="confirm"
        @cancel="cancel"
        ></date-picker>
    import DatePicker from './components/DatePicker.vue';
    import './style.scss';
    
    new Vue({
      el: '#app',
      data() {
        return {
          date: '2017-09-11',
          minDate: '2000-09-11',
          maxDate: '2020-09-11',
          showDatePicker: false,
          selectedDate: '点击选择日期',
        };
      },
      methods: {
        openDatePicker() {
          this.showDatePicker = true;
        },
        confirm(value) {
          this.showDatePicker = false;
          this.selectedDate = value;
        },
        cancel() {
          this.showDatePicker = false;
        },
      },
      components: {
        DatePicker,
      },
    });

    我们提供了最大值、最小值和初始值,唯一不足的地方是时间格式只能是YYYY-MM-DD (2017-12-12) ,大家可以从github上拉取代码运行看一下(由于没有仔细测试,可能会有bug和性能问题,希望指出)。

    (一)先画好界面

      这个不是重点,HTML 和 CSS,应该很简单,大家看我的css 可能感觉我的命名太长了,只因为我们公司用,担心其他样式影响它(可能有其他方式吧,希望大神指出)

    (二)组装日期列表

      先看代码:

    rows() {
      const { year, month } = this.showDate;
      const months = (new Date(year, month, 0)).getDate();
      const result = [];
      let row = [];
      let weekValue;
      // 按照星期分组
      for (let i = 1; i <= months; i += 1) {
        // 根据日期获取星期,并让开头是1,而非0
        weekValue = (new Date(year, month, i)).getDay() + 1;
        // 判断月第一天在星期几,并填充前面的空白区域
        if (i === 1 && weekValue !== 1) {
          this.addRowEmptyValue(row, weekValue);
          this.addRowDayValue(row, i);
        } else {
          this.addRowDayValue(row, i);
          // 判断月最后一天在星期几,并填充后面的空白区域
          if (i === months && weekValue !== 7) {
            this.addRowEmptyValue(row, (7 - weekValue) + 1);
          }
        }
        // 按照一周分组
        if (weekValue % 7 === 0 || i === months) {
          result.push(row);
          row = [];
        }
      }
      this.showDate.monthStr = monthJson[this.showDate.month];
      return result;
    },

    我的思路是:

      (1)获取月份天数,并按照星期分组;

      (2)如果月份第一天不在星期一,前面填充空值。同理,如何月份最后一天不在周日,最后面填充空值,目的是:让分的组 长度都是7,也就是一周。这样可以用flex布局方式快速开发了;

      (3)里面也包含一些限制,比如小于minDate和大于maxDate, 不让点击等等 

    (三)切换月份 

     (1)上一个月份

    /**
     * 切换到上一个月
     */
    prevMonth() {
      if (this.prevMonthClick) {
        return;
      }
      this.prevMonthClick = true;
      setTimeout(() => {
        this.prevMonthClick = false;
      }, 500);
      this.fadeXType = 'fadeX_Prev';
      // 如何当前月份已经小于等于minMonth 就不让其在执行
      if (this.isMinLimitMonth()) {
        return;
      }
      const { year, month } = this.showDate;
      // 判断当前月份,如果已经等于1(1就是一月,而不是二月)
      if (month <= 1) {
        this.showDate.year = year - 1;
        this.showDate.month = 12;
      } else {
        this.showDate.month -= 1;
      }
    },

    setTimeout()主要是让其显示动画后自动消失。 fadeXType 是动画类型

       (2)下一个月份

    /**
     * 切换到下一个月
     */
    nextMonth() {
      if (this.nextMonthClick) {
        return;
      }
      this.nextMonthClick = true;
      setTimeout(() => {
        this.nextMonthClick = false;
      }, 500);
      this.fadeXType = 'fadeX_Next';
      // 如何当前月份已经大于等于maxMonth 就不让其在执行
      if (this.isMaxLimitMonth()) {
        return;
      }
      const { year, month } = this.showDate;
      // 判断当前月份,如果已经等于12(12就是十二月)
      if (month >= 12) {
        this.showDate.year = year + 1;
        this.showDate.month = 1;
      } else {
        this.showDate.month += 1;
      }
    },

    这里面的setTimeout() 和prevMonth方法的原理一样。

    上面两种切换月份的功能主要注意:

       a. 因为有minDate和maxDate,所以首先考虑的是不能超出这个限制。

       b. 要考虑切换月份后年的变化,当月份大于12后,年加1 ,月变成 1。

    (四)选择年份

         (1)点击最上面的年,显示年份列表

    openYearList() {
      if (this.showYear) {
        this.showYear = false;
        return;
      }
      const index = this.yearList.indexOf(this.selectDate.year);
      this.showYear = true;
      // 打开年列表,让其定位到选中的位置上
      setTimeout(() => {
        this.$refs.yearList.scrollTop = (index - 3) * 40;
      });
    },

          (2)选择年份

    selectYear(value) {
      this.showYear = false;
      this.showDate.year = value;
      let type;
      // 当日期在最小值之外,月份换成最小值月份 或者 当日期在最大值之外,月份换成最大值月份
      if (this.isMinLimitMonth()) {
        type = 'copyMinDate';
      } else if (this.isMaxLimitMonth()) { // 当日期在最大值之外,月份换成最大值月份
        type = 'copyMaxDate';
      }
      if (type) {
        this.showDate.month = this[type].month;
        this.showDate.day = this[type].day;
        this.resetSelectDate(this.showDate.day);
        return;
      }
      let dayValue = this.selectDate.day;
      // 判断日是最大值,防止另一个月没有这个日期
      if (this.selectDate.day > 28) {
        const months = (new Date(this.showDate.year, this.showDate.month, 0)).getDate();
        // 当前月份没有这么多天,就把当前月份最大值赋值给day
        dayValue = months < dayValue ? months : dayValue;
      }
      this.resetSelectDate(dayValue);
    },

    在切换年份时注意一下方面:

        a. 考虑minDate和maxDate,  因为如果之前你选择的月份是1月,但是限制是9月,在大于minDate(比如2017) 年份没有问题,但是到了minDate 的具体年份(比如2010),那么月份最小值只能是九月,需要修改月份,maxDate同理。

     b. 如何之前你选择的day是31,由于切换年份后,这个月只有30天,记得把day 换成这个月最大值,也就是30。

    (五)处理原始数据

      其实这一条正常情况下,应该放在第一步讲,但是我是根据我的开发习惯来写步骤的。我一般都是先写功能,数据是模拟的,等写好了,再考虑原始数据格式和暴露具体的方法等等,因为这样不会改来改去,影响开发和心情。

    initDatePicker() {
      this.showDate = { ...this.splitDate(this.date, true) };
      this.copyMinDate = { ...this.splitDate(this.minDate) };
      this.copyMaxDate = { ...this.splitDate(this.maxDate) };
      this.selectDate = { ...this.showDate };
    },
    splitDate(date, addStr) {
      let result = {};
      const splitValue = date.split('-');
      try {
        if (!splitValue || splitValue.length < 3) {
          throw new Error('时间格式不正确');
        }
        result = {
          year: Number(splitValue[0]),
          month: Number(splitValue[1]),
          day: Number(splitValue[2]),
        };
        if (addStr) {
          result.week = (new Date(result.year, result.month, result.day)).getDay() + 1;
          result.monthStr = monthJson[result.month];
          result.weekStr = weekJson[result.week];
        }
      } catch (error) {
        console.error(error);
      }
      return result;
    },

    这里目的是:

     a. 处理原始数据,把原始数据查分,用json缓存下来,这样方便后面操作和显示。这里面我只兼容YYYY-MM-DD的格式,其他的都不兼容,如果你想兼容其他格式,你可以修改其代码,或者用moment.js 等其他库帮你做这件事情。

     b. 拆分后的格式如下:

    year: '',
    month: '',
    day: '',
    week: '',
    weekStr: '',
    monthStr: '',

    总结:

      上面就是开发这个组件的详细讲解,如有不妥,请指出批评。 仔细想想,其实这个不难,就是有点琐碎。仔细就好

    这里的所有动画都是用的Vue 的 transition,大家可以看看官网,非常详细。

  • 相关阅读:
    Populating Next Right Pointers in Each Node II
    Populating Next Right Pointers in Each Node
    Construct Binary Tree from Preorder and Inorder Traversal
    Construct Binary Tree from Inorder and Postorder Traversal
    Path Sum
    Symmetric Tree
    Solve Tree Problems Recursively
    632. Smallest Range(priority_queue)
    609. Find Duplicate File in System
    poj3159最短路spfa+邻接表
  • 原文地址:https://www.cnblogs.com/zhangkunweb/p/date_picker.html
Copyright © 2011-2022 走看看