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,大家可以看看官网,非常详细。

  • 相关阅读:
    强制转换改变了对象的框架大小
    android应用程序fps meter[帧数显示]的分析 —— 浅谈root的风险 (1)
    父类virtual和overload,子类reintroduce; overload;
    MySQL版本与工具
    Verilog HDL实用教程笔记
    XE2安装JVCL
    解决Raize日历控件显示的问题
    hdu3415 Max Sum of Max-K-sub-sequence
    MFC重绘原理的关键理解
    常用代码页与BOM
  • 原文地址:https://www.cnblogs.com/zhangkunweb/p/date_picker.html
Copyright © 2011-2022 走看看