1. 程式人生 > >自定義小程式元件-日曆元件

自定義小程式元件-日曆元件

前言

  日曆是我們開發過程中經常會使用到的一個功能,po主在開發小程式的過程中就遇到一個場景需要使用日曆元件。首先上網搜尋一番,但是沒有找到合適自己的,於是便決定自己寫一款小程式日曆元件。

先上效果圖:

 

1.功能分析

  這款日曆元件主要是提供日期選擇功能。首先是日期,然後是時分秒。並且提供配置開始時間和結束時間設定,以及週末是否可選的設定項。

  總體來說分為兩部分:

  • 第一頁的日期選擇
  • 第二頁的時間選擇

2.實現分析:

2.1日期選擇: 

  這個頁面首先我們 setMonthData 函式構造一個 7 * 6 的資料結構, 這個資料結構包含當月,以及部分上月和下月的天數。在構造的時候通過isValidDay函式判斷當天是否是可以選擇的

然後加上 valid 一個標識,以便在渲染的時候用於顯示和點選判斷。

// 根據條件判斷當天日期是否有效
isValidDay(date) {
    let {startDate, endDate, needWeek} = this.data;

    let valid = false;

    if (startDate && endDate) { // 開始日期和結束日期都存在
        if (date >= startDate && date < endDate) {
            valid = true;
        }
    } else if (startDate && !endDate) { // 開始日期存在結束日期不存在
        if (date >= startDate) {
            valid = true;
        }
    } else if (!startDate && endDate) { // 開始日期不存在結束日期存在
        if (date < endDate) {
            valid = true;
        }
    } else {
        valid = true;
    }

    if (!needWeek) {
        let weekDay = date.getDay();
        if (weekDay === 0 || weekDay === 6) {
            valid = false;
        }
    }
    return valid;
}

//  根據傳入的日期構造資料
setMonthData() {
    const currentDate = this.data.currentDate;

    let year = currentDate.getFullYear(), month = currentDate.getMonth();

    // 當月所有天數的資料結構
    let currentData = [];

    // 獲取當月1號的星期0~6
    let firstDayWeek = new Date(year, month, 1).getDay();

    // 天數, 用來標識當前是本月中的哪一天
    let dayIndex = 0;

    // 第1行
    let firstCol = [];

    for (let i = 0; i < 7; i++) {
        if (i < firstDayWeek) {
            let date = new Date(year, month, dayIndex - (firstDayWeek - i) + 1);
            let valid = this.isValidDay(date);
            firstCol.push({
                date: date,
                dateStr: date.toString(),
                day: date.getDate(),
                valid: valid,
                currentMonth: false
            })
        } else {
            dayIndex += 1;
            let date = new Date(year, month, dayIndex);
            let valid = this.isValidDay(date);
            firstCol.push({
                date: date,
                dateStr: date.toString(),
                day: dayIndex,
                valid: valid,
                currentMonth: true
            });
        }
    }

    currentData.push(firstCol);

    // 第2~4行
    for (let i = 0; i < 3; i++) {
        let col = [];
        for (let j = 0; j < 7; j++) {
            dayIndex += 1;
            let date = new Date(year, month, dayIndex);
            let valid = this.isValidDay(date);
            col.push({
                date: date,
                dateStr: date.toString(),
                day: dayIndex,
                valid: valid,
                currentMonth: true
            });
        }
        currentData.push(col);
    }

    // 第5行
    let lastCol = [];

    // 餘下一行中本月的天數
    let restDay = new Date(year, month + 1, 0).getDate() - dayIndex;

    for (let i = 0; i < 7; i++) {
        if (i < restDay) {
            dayIndex += 1;
            let date = new Date(year, month, dayIndex);
            let valid = this.isValidDay(date);
            lastCol.push({
                date: date,
                dateStr: date.toString(),
                day: dayIndex,
                valid: valid,
                currentMonth: true
            });
        } else {
            let date = new Date(year, month + 1, i - restDay + 1);
            let valid = this.isValidDay(date);
            lastCol.push({
                date: date,
                dateStr: date.toString(),
                day: date.getDate(),
                valid: valid,
                currentMonth: false
            });
        }
    }

    currentData.push(lastCol);

    let restDay2 = restDay - 7;

    // 第6行
    let lastCol2 = [];

    for (let i = 0; i < 7; i++) {
        if (i < restDay2) {
            dayIndex += 1;
            let date = new Date(year, month, dayIndex);
            let valid = this.isValidDay(date);
            lastCol2.push({
                date: date,
                dateStr: date.toString(),
                day: dayIndex,
                valid: valid,
                currentMonth: true
            });
        } else {
            let date = new Date(year, month + 1, i - restDay2 + 1);
            let valid = this.isValidDay(date);
            lastCol2.push({
                date: date,
                dateStr: date.toString(),
                day: date.getDate(),
                valid: valid,
                currentMonth: false
            });
        }
    }

    currentData.push(lastCol2);

    this.setData({
        currentData: currentData
    });

}

構造完當月資料後我們就可以渲染直接在頁面渲染這些資料,渲染的時候需要加一些判斷條件,這樣我們的日曆才能把能把這個月的天和上個月的天以及禁用的天區分開

<view class='date-body' wx:if="{{mode==='date'}}">
            <view class='tr'>
                <view class='th'><view class="cell">日</view></view>
                <view class='th'><view class="cell">一</view></view>
                <view class='th'><view class="cell">二</view></view>
                <view class='th'><view class="cell">三</view></view>
                <view class='th'><view class="cell">四</view></view>
                <view class='th'><view class="cell">五</view></view>
                <view class='th'><view class="cell">六</view></view>
            </view>
            <view class="tr"
                  wx:for="{{currentData}}"
                  wx:key="{{i}}"
                  wx:for-item="tr"
                  wx:for-index="i">
                <view class="td"
                      wx:for="{{tr}}"
                      wx:key="{{j}}"
                      wx:for-item="td"
                      wx:for-index="j"
                      data-i="{{i}}"
                      data-j="{{j}}"
                      bindtap="chooseDate">
                    <view hover-class="none"
                          class="cell {{td.currentMonth ? '' : 'otherMonth'}} {{td.valid ? '' : 'disabled'}} {{td.date && td.dateStr === selectedDateStr ? 'cur' : ''}}">
                        {{td.day}}
                    </view>
                </view>
            </view>
        </view>

至此這個頁面的功能基本完成了,但是頂部還有一些功能箭頭,用於切換下月,下一年,以及上月,上一年。

這個我們通過下面的changeDate方法去改變切換後的月份日期,然後重新呼叫 setMonthData 即可,到此年月日的選擇就完成了

changeDate(event) {
    let currentDate = this.data.currentDate;
    let year = currentDate.getFullYear(), month = currentDate.getMonth();
    let type = event.currentTarget.dataset.type;

    switch (type) {
        case 'year-':
            currentDate.setFullYear(year - 1)
            break;
        case 'year+':
            currentDate.setFullYear(year + 1)
            break;
        case 'month-':
            currentDate.setMonth(month - 1)
            break;
        case 'month+':
            currentDate.setMonth(month + 1)
            break;
    }

    this.setCurrentDate(currentDate);

    this.setMonthData();
}

2.2日期選擇: 

  時間選擇,這裡我們直接使用小程式的自帶的 picker-view元件,構造好時分秒資料結構傳遞渲染即可。

hours: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23],
minutes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 26, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59],
seconds: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 26, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59],
initTimeValue: [0, 0, 0], // 初始化選中時間的時,分,秒
timeValue: [0, 0, 0], // 選中時間的時,分,秒


<view class='time-body' wx:if="{{mode==='time'}}">
    <picker-view indicator-class="selectItem" value="{{initTimeValue}}" bindchange="bindChange">
        <picker-view-column>
            <view wx:for="{{hours}}" wx:key="*this" class="item">{{item}}時</view>
        </picker-view-column>
        <picker-view-column>
            <view wx:for="{{minutes}}" wx:key="*this" class="item">{{item}}分</view>
        </picker-view-column>
        <picker-view-column>
            <view wx:for="{{seconds}}" wx:key="*this" class="item">{{item}}秒</view>
        </picker-view-column>
    </picker-view>
</view>

到此整個日曆元件便完成了~~, 

3.程式碼實現

  具體程式碼我放在github上了,感興趣的同學可以自取,如有不滿足需求的地方可以盡情修改,有問題可以在評論區@我

  • 小程式日曆元件原始碼

&n