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,大家可以看看官網,非常詳細。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM