用react寫一個日曆插件

說明

個人娛樂所寫(不保證BUG不存在),UI方面參照其他人的UI設計

詳情見本人github案例:案例

支持功能

  • 初始化日期
  • 高亮’今天’以及選擇日期
  • 歷史記錄選擇日期
  • 支持tag標識
  • 支持選擇日期回調
  • 屏幕適應

效果圖

這裏寫圖片描述

這裏寫圖片描述

基本思路

  1. 計算出這一年中的每個月對應的天數,其中需要根據年份來判斷2月份到底是28天還是29天,使用數組保存
  2. 計算出這個月的第一天是星期幾,來決定前面應該會有多少上個月的空格以及根據天數來判斷月後應該有多少天來彌補
  3. 渲染思路:

    • 使用三個數組,分別存有上一個月、當前月以及下一個月應該渲染的天數
    • 數組合並,根據每一行應該顯示7列的特性,將大數組劃分爲6個小數組,這6個小數組中即爲每一行應該顯示的星期數
  4. 具體代碼


/**
 * Created by Ryn on 2016/8/7.
 * 日曆組件
 */

import React from 'react';
import H from '../helpers/H';

const Calendar = React.createClass({

    /**
     * 默認的屬性
     */
    getDefaultProps() {
        return {
            row_number : 6,
            col_number : 7
        }
    },

    /**
     * 組件初始化狀態
     */
    getInitialState() {
        return {
            current_year : H.getFullYear(),
            current_month : H.getMonth(),
            current_day : H.getDate(),
            select_year : H.getFullYear(),
            select_month : H.getMonth(),
            select_day : H.getDate(),
            history_year : undefined,
            history_month : undefined,
            history_day : undefined,
            date_num_array : []
        }
    },

    componentWillReceiveProps(nextProps) {
        // todo
    },

    /**
     * 組件渲染完後執行
     */
    componentDidMount() {
        let { year, month, day} = this.props;

        // 初始化狀態
        if(year && month && day) {
            let date_num_array = this._initMonthDayNumber(year),
                first_day = H.weekOfMonth(new Date(year, month - 1));

            this.setState({
                select_year : year,
                select_month : month - 1,
                select_day : day,
                date_num_array : date_num_array,
                first_day : first_day
            });
        }
    },

    /**
     * 給月份數組附上每月天數
     * @param year 年份
     * @private
     */
    _initMonthDayNumber(year) {
        let _date_array = [];

        for (var i = 0; i < 12; i++) {
            switch (i + 1) {
                case 1:
                case 3:
                case 5:
                case 7:
                case 8:
                case 10:
                case 12:
                    _date_array.push(31);
                    break;
                case 4:
                case 6:
                case 9:
                case 11:
                    _date_array.push(30);
                    break;
                case 2:
                    if (H.isLeapYear(year)) {
                        _date_array.push(29);
                    } else {
                        _date_array.push(28);
                    }
                    break;
                default:
                    break;
            }
        }

        return _date_array;
    },

    /**
     * 組件將要掛載
     * 設置月份數組以及計算出每月的第一天星期幾
     */
    componentWillMount() {
        let date_num_array = this._initMonthDayNumber(this.state.current_year),
            first_day = H.weekOfMonth();

        this.setState({date_num_array : date_num_array, first_day : first_day});
    },

    /**
     * 日期選擇
     * @param s_day
     */
    selectDate(s_day) {
        let { select_year, select_month} = this.state;
        this.setState({
            history_year : select_year,
            history_month : select_month,
            history_day : s_day,
            select_day : s_day
        }, () => {
            this.props.onSelectDate(select_year, select_month + 1, s_day);
        });
    },

    /**
     * 前一個月
     */
    previousMonth() {
        let { current_year, current_month, current_day,
            select_year, select_month, select_day, date_num_array, first_day} = this.state;

        if (select_month === 0) {
            select_year = +select_year - 1;
            select_month = 11;
            date_num_array = this._initMonthDayNumber(select_year);
        } else {
            select_month = +select_month - 1;
        }

        first_day = H.weekOfMonth(new Date(select_year, select_month));

        if (current_year === select_year &&
            current_month === select_month) {
            select_day = current_day;
        } else {
            select_day = undefined;
        }

        this.setState({
            select_year : select_year,
            select_month : select_month,
            select_day : select_day,
            date_num_array : date_num_array,
            first_day : first_day
        })
    },

    /**
     * 之後一個月
     */
    nextMonth() {
        let { current_year, current_month, current_day,
            select_year, select_month, select_day, date_num_array, first_day} = this.state;

        if (select_month === 11) {
            select_year = +select_year + 1;
            select_month = 0;
            date_num_array = this._initMonthDayNumber(select_year);
        } else {
            select_month = +select_month + 1;
        }

        first_day = H.weekOfMonth(new Date(select_year, select_month));

        if (current_year === select_year &&
            current_month === select_month) {
            select_day = current_day;
        } else {
            select_day = undefined;
        }

        this.setState({
            select_year : select_year,
            select_month : select_month,
            select_day : select_day,
            date_num_array : date_num_array,
            first_day : first_day
        })
    },

    /**
     * 渲染頁面
     * @returns {XML}
     */
    render() {

        let { row_number, col_number, tags } = this.props;
        let { current_year, current_month, current_day,
            select_year, select_month, select_day,
            history_year, history_month, history_day,
            date_num_array, first_day} = this.state;

        let month_day = date_num_array[select_month],
            n_day = row_number * col_number - first_day - month_day,
            previous_month_days = undefined,
            previous_days = [],
            current_days = [],
            next_days = [],
            total_days = [],
            previous_month = undefined;

        if (select_month === 0) {
            previous_month = 11;
        } else {
            previous_month = select_month - 1;
        }

        previous_month_days = date_num_array[previous_month];
        for (let i = 0; i < first_day; i++) {
            let previous_link = (<li className="item-gray" key={'previous'+i}>
                <a href="javascript:;">{previous_month_days - (first_day - i) + 1}</a>
            </li>);
            previous_days.push(previous_link);
        }

        let currentClassName = '',
            currentText = '';
        for (let i = 0; i < month_day; i++) {

            // 今天樣式
            if (current_year == select_year && current_month == select_month && current_day == (i + 1)) {
                currentClassName = 'item-current';
                currentText = '今天';
            } else {
                currentText = i + 1;

                // 判斷選擇樣式與歷史樣式是否相等,相等激活
                if (select_year == history_year && select_month == history_month && history_day == (i + 1)) {
                    currentClassName = 'item-active';
                } else {
                    currentClassName = '';
                }
            }

            // 添加tag樣式
            if (tags.length > 0) {
                for (let j = 0; j < tags.length; j++) {
                    if ((i + 1) === tags[j]) {
                        currentClassName += 'item-tag';
                        break;
                    }
                }
            }

            let current_link = (<li className={currentClassName} key={'current'+i}>
                <a href="javascript:;" onClick={this.selectDate.bind(this, i + 1)}>
                    {currentText}
                </a>
            </li>);
            current_days.push(current_link);
        }

        for (let i = 0; i < n_day; i++) {
            let next_link = (<li className="item-gray" key={'next'+i}>
                <a href="javascript:;">{i + 1}</a>
            </li>);
            next_days.push(next_link);
        }

        total_days = previous_days.concat(current_days, next_days);

        let ul_list = [];
        if (total_days.length > 0) {
            for (let i = 0; i < row_number; i++) {
                let li_list = [],
                    start_index = i * col_number,
                    end_index = (i + 1) * col_number;
                for (let j = start_index; j < end_index; j++) {
                    li_list.push(total_days[j]);
                }
                ul_list.push(li_list);
            }
        }

        return (
            <div className="calendar">
                <div className="calendar-header">
                    <i className="icon-left" onClick={this.previousMonth}></i>
                    <span>{select_year} 年 {select_month + 1} 月</span>
                    <i className="icon-right" onClick={this.nextMonth}></i>
                </div>
                <div className="calendar-body">
                    <ul className="c-body-head">
                        <li></li>
                        <li></li>
                        <li></li>
                        <li></li>
                        <li></li>
                        <li></li>
                        <li></li>
                    </ul>
                    <div className="c-body-content">
                        {
                            ul_list.map((u, index) => {
                                return (<ul key={'ul'+index} className="content-row">{u}</ul>);
                            })
                        }
                    </div>
                </div>
            </div>
        );
    }
});

export default Calendar;

調用方式

import React from 'react';
import ReactDOM from 'react-dom';
import Calendar from './Calendar';

const App = React.createClass({
    render() {
        return (
            <Calendar onSelectDate={this.selectDate} 
                year='2016' 
                month='8' 
                day='7' 
                tags={[5, 22]} />
        );
    }
});

export default App;
發佈了60 篇原創文章 · 獲贊 29 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章