为了账号安全,请及时绑定邮箱和手机立即绑定

看我的案例:用react写一个日历控件!

说明

个人娱乐所写,UI方面参照其他人的UI设计

支持功能
  • 初始化日期
  • 高亮'今天'以及选择日期
  • 历史记录选择日期
  • 支持tag标识
  • 支持选择日期回调
  • 支持上一月/下一月选择回调
  • 支持tags动态切换
  • 屏幕适应
效果图

这里写图片描述

这里写图片描述

基本思路
  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({

    /**
     * 初始状态
     * @returns {{tags: number[]}}
     */
    getInitialState() {
        return {
            tags : [5, 21]
        }
    },

    /**
     * 选择日期
     * @param year
     * @param month
     * @param day
     */
    selectDate(year, month, day) {
        console.log("选择时间为:" + year + '年' + month + '月' + day + '日' );
    },

    /**
     * 上一个月
     * @param year
     * @param month
     */
    previousMonth(year, month) {
        console.log("当前日期为:" + year + '年' + month + '月');
        this.setState({tags : [7, 11]});
    },

    /**
     * 下一个月
     * @param year
     * @param month
     */
    nextMonth(year, month) {
        console.log("当前日期为:" + year + '年' + month + '月');
        this.setState({tags : [8, 23]});
    },

    /**
     * 组件渲染
     * @returns {XML}
     */
    render() {
        return (
            <Calendar
                onSelectDate={this.selectDate}
                onPreviousMonth={this.previousMonth}
                onNextMonth={this.nextMonth}
                year="2016"
                month="8"
                day="7"
                tags={this.state.tags} />
        );
    }
});

export default App;
点击查看更多内容
20人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
JAVA开发工程师
手记
粉丝
70
获赞与收藏
513

关注作者,订阅最新文章

阅读免费教程

感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消