Browse Source

日历组件

胡耀汉 8 years ago
commit
69a76d51e7

+ 3 - 0
.gitignore

@@ -0,0 +1,3 @@
+.idea
+react/node_modules
+

+ 23 - 0
normal/index.html

@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name ="viewport" content ="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no">
+    <title>datepicker</title>
+    <style>
+        body{
+            font-family: '黑体';
+            font-size : 14px;
+        }
+        .container {
+            border: 1px solid #ccc;
+        }
+    </style>
+    <link rel="stylesheet" href="./style/style.css">
+</head>
+<body>
+    <div id="datePicker" class="container"></div>
+    <script src="./script/helpers.js"></script>
+    <script src="./script/calendar.js"></script>
+</body>
+</html>

+ 276 - 0
normal/script/calendar.js

@@ -0,0 +1,276 @@
+/**
+ * Created by Ryn on 2016/8/6.
+ * 日历脚本
+ */
+
+;(function(document, window, H) {
+
+    // 一些私有变量
+    var _date_array = [],                           // 记录每一月有多少天数
+        current_day = new Date(),
+        current_year = H.getFullYear(current_day),
+        current_month = H.getMonth(current_day),
+        calendar_elem = null,
+        calendar_head = null,
+        calendar_body = null,
+        icon_left_elem = null,
+        icon_right_elem = null,
+        week_map = {
+            0 : '日',
+            1 : '一',
+            2 : '二',
+            3 : '三',
+            4 : '四',
+            5 : '五',
+            6 : '六'
+        };
+
+    // 一些常量
+    var MONTH_NUMBER = 12,
+        ROW_NUMBER = 6,
+        COL_NUMBER = 7;
+
+    /**
+     * 给月份数组附上每月天数
+     * @param year 年份
+     * @private
+     */
+    function _initMonthDayNumber(year) {
+        _date_array = [];
+
+        for (var i = 0; i < MONTH_NUMBER; 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;
+            }
+        }
+    }
+
+    /**
+     * 渲染最外层容器
+     * @param outerEle
+     * @private
+     */
+    function _renderContainer(outerEle) {
+        calendar_elem = document.createElement('div');
+        calendar_elem.className = 'calendar';
+        outerEle.appendChild(calendar_elem);
+    }
+
+    /**
+     * 渲染头部
+     * @param date
+     * @private
+     */
+    function _renderHeader(date) {
+        calendar_head = document.createElement('div');
+        icon_left_elem = document.createElement('i');
+        icon_right_elem = document.createElement('i');
+
+        var spanEle = document.createElement('span');
+
+        if (!date) {
+            date = current_day;
+        }
+        var year = H.getFullYear(date);
+        var month = H.getMonth(date);
+
+        calendar_head.className = 'calendar-header';
+        icon_left_elem.className = 'icon-left';
+        icon_right_elem.className = 'icon-right';
+        spanEle.textContent = year + ' 年 ' + (month + 1) + ' 月';
+        calendar_head.appendChild(icon_left_elem);
+        calendar_head.appendChild(spanEle);
+        calendar_head.appendChild(icon_right_elem);
+        calendar_elem.appendChild(calendar_head);
+    }
+
+    /**
+     * 渲染内容部分星期汉字
+     * @private
+     */
+    function _renderBodyWeek() {
+        calendar_body = document.createElement('div');
+        calendar_body.className = 'calendar-body';
+
+        var cBodyHead = document.createElement('ul');
+        cBodyHead.className = 'c-body-head';
+        for (var i = 0; i < COL_NUMBER; i++) {
+            var liEle = document.createElement('li');
+            liEle.textContent = week_map[i];
+            cBodyHead.appendChild(liEle);
+        }
+        calendar_body.appendChild(cBodyHead);
+        calendar_elem.appendChild(calendar_body);
+    }
+
+    /**
+     * 渲染具体内容
+     * @param date
+     * @private
+     */
+    function _renderBodyDay(date) {
+
+        if (!date) date = current_day;
+
+        var cBodyContent = document.createElement('div'),
+            firstDay = H.weekOfMonth(date),
+            month = H.getMonth(date),
+            current_date = H.getDate(date),
+            dayNumber = _date_array[month],
+            numberIndex = 0,
+            currentMonthIndex = 1,
+            preMonthIndex = 1,
+            nextMonthIndex = 1;
+
+        cBodyContent.className = 'c-body-content';
+
+        for (var i = 0; i < ROW_NUMBER; i++) {
+            var ulRow = document.createElement('ul');
+            ulRow.className = 'content-row';
+
+            for (var j = 0; j < COL_NUMBER; j++) {
+                var liCol = document.createElement('li'),
+                    linkCol = document.createElement('a');
+                linkCol.href = 'javascript:;';
+                liCol.appendChild(linkCol);
+
+                if (numberIndex < firstDay) {
+                    if (month === 0) month = 12;
+                    liCol.className += ' item-gray';
+                    linkCol.textContent = _date_array[month - 1] - (firstDay - preMonthIndex);
+                    ulRow.appendChild(liCol);
+                    preMonthIndex++;
+                    numberIndex++;
+                } else if(numberIndex > dayNumber + firstDay - 1) {
+                    liCol.className += ' item-gray';
+                    linkCol.textContent = nextMonthIndex;
+                    ulRow.appendChild(liCol);
+                    nextMonthIndex++;
+                    numberIndex++;
+                } else {
+                    linkCol.className = 'item-link';
+                    linkCol.textContent = currentMonthIndex;
+
+                    if (currentMonthIndex === current_date) {
+                        liCol.className += ' item-current';
+                        linkCol.textContent = '今天';
+                    }
+
+                    ulRow.appendChild(liCol);
+                    currentMonthIndex++;
+                    numberIndex++;
+                }
+            }
+            cBodyContent.appendChild(ulRow);
+        }
+
+        calendar_body.appendChild(cBodyContent);
+    }
+
+    /**
+     * 渲染视图
+     */
+    function renderUI() {
+        _renderContainer(document.getElementById('datePicker'));
+        _renderHeader();
+        _renderBodyWeek();
+        _renderBodyDay();
+    }
+
+    /**
+     * 删除UI
+     * @private
+     */
+    function _removeUI() {
+        calendar_head.remove();
+        calendar_body.remove();
+    }
+
+    /**
+     * 同步视图
+     * @param date
+     */
+    function syncUI(date) {
+        _removeUI();
+        _initMonthDayNumber(current_year);
+        _renderHeader(date);
+        _renderBodyWeek();
+        _renderBodyDay(date);
+        bindEvents();
+    }
+
+    /**
+     * 绑定事件
+     */
+    function bindEvents() {
+        icon_left_elem.addEventListener('click', function() {
+            if (current_month === 0) {
+                current_month = 11;
+                current_year = current_year - 1;
+            } else {
+                current_month = current_month - 1;
+            }
+            syncUI(new Date(current_year, current_month));
+        });
+
+        icon_right_elem.addEventListener('click', function() {
+            if (current_month === 11) {
+                current_month = 0;
+                current_year = current_year + 1;
+            } else {
+                current_month = current_month + 1;
+            }
+            syncUI(new Date(current_year, current_month));
+        });
+
+        var itemLinks = document.getElementsByClassName('item-link'),
+            linksArray = Array.prototype.slice.call(itemLinks);
+
+        for (var i = 0; i < linksArray.length; i++) {
+            linksArray[i].addEventListener('click', function(e) {
+                e.preventDefault();
+                var activeLinks = document.getElementsByClassName('item-current'),
+                activeArray = Array.prototype.slice.call(activeLinks);
+                for (var j = 0; j < activeArray.length; j++) {
+                    activeArray[j].className = activeArray[j].className.replace(/item-current/, '');
+
+                }
+                this.parentNode.className += ' item-current';
+                alert("当前日期为:" + current_year + '年' + (current_month + 1) + '月' + this.textContent + '日' );
+            });
+        }
+
+    }
+
+    function init() {
+        _initMonthDayNumber(current_year);
+        renderUI();
+        bindEvents();
+    }
+
+    init();
+
+})(document, window, H);

+ 90 - 0
normal/script/helpers.js

@@ -0,0 +1,90 @@
+/**
+ * Created by Ryn on 2016/8/6.
+ * 帮助函数
+ */
+
+var H = (function() {
+
+    // 一些私有变量
+
+    // 一些公有变量、函数
+
+    /**
+     *
+     * 判断这一年是闰年还是平年
+     * @param year {String/Number} 年份
+     * @returns {boolean}
+     */
+
+    function isLeapYear(year) {
+        if (!typeof +year === 'number') {
+            throw new Error("年份格式不正确");
+        }
+
+        if (+year < 1790) {
+            throw new Error("年份不能低于1790年");
+        }
+
+        // 计算闰年方法
+        // 1.能被4整除而不能被100整除
+        // 2.能被400整除
+
+        return (year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0);
+    }
+
+    /**
+     * 返回月份中的第一天是星期几
+     * @returns {number}
+     * 1 星期一
+     * 2 星期二
+     * 3 星期三
+     * 4 星期四
+     * 5 星期五
+     * 6 星期六
+     * 0 星期天
+     */
+    function weekOfMonth(date) {
+        if (!date) date = new Date();
+        return new Date(getFullYear(date), getMonth(date), 1).getDay();
+    }
+
+    /**
+     * 获取月份
+     * @param date
+     * @returns {*|number}
+     */
+    function getMonth(date) {
+        if (!date) date = new Date();
+        return date.getMonth();
+    }
+
+    /**
+     * 获取年份
+     * @param date
+     * @returns {number}
+     */
+    function getFullYear(date) {
+        if (!date) date = new Date();
+        return date.getFullYear();
+    }
+
+    /**
+     * 获取一月中的某一天
+     * @param date
+     * @returns {number}
+     */
+    function getDate(date) {
+        if (!date) date = new Date();
+        return date.getDate();
+    }
+
+    // 暴露需要提供的方法
+    return {
+        isLeapYear : isLeapYear,
+        weekOfMonth : weekOfMonth,
+        getFullYear : getFullYear,
+        getMonth : getMonth,
+        getDate : getDate
+    }
+
+})();

+ 154 - 0
normal/style/style.css

@@ -0,0 +1,154 @@
+div,ul,li {
+    margin: 0;
+    padding: 0;
+}
+
+ul,li {
+    list-style: none;
+}
+
+a {
+    text-decoration: none;
+    color: #000;
+}
+
+.calendar {
+    width: 100%;
+}
+
+.calendar-header {
+    width: 100%;
+    text-align: center;
+    padding: 13px 0;
+    line-height: 14px;
+    border-bottom: 1px solid #ddd;
+}
+
+.calendar-header>i{
+    display: inline-block;
+    width: 14px;
+    height: 14px;
+    vertical-align: -2px;
+    position: relative;
+}
+
+.calendar-header>span {
+    margin: 0 5%;
+}
+
+.calendar-header>.icon-left:before,
+.calendar-header>.icon-left:after,
+.calendar-header>.icon-right:before,
+.calendar-header>.icon-right:after
+{
+    content: '';
+    position: absolute;
+    top: 0;
+    left: 0;
+}
+
+.calendar-header>.icon-left:before {
+    border: 7px solid red;
+    border-color: transparent #666 transparent transparent;
+    -webkit-transform: translateX(-50%);
+    -moz-transform: translateX(-50%);
+    -ms-transform: translateX(-50%);
+    -o-transform: translateX(-50%);
+    transform: translateX(-50%);
+}
+
+.calendar-header>.icon-left:after {
+    border: 7px solid #fff;
+    border-color: transparent #fff transparent transparent;
+    -webkit-transform: translateX(-40%);
+    -moz-transform: translateX(-40%);
+    -ms-transform: translateX(-40%);
+    -o-transform: translateX(-40%);
+    transform: translateX(-40%);
+}
+
+.calendar-header>.icon-right:before {
+    border: 7px solid red;
+    border-color: transparent transparent transparent #666;
+    -webkit-transform: translateX(50%);
+    -moz-transform: translateX(50%);
+    -ms-transform: translateX(50%);
+    -o-transform: translateX(50%);
+    transform: translateX(50%);
+}
+
+.calendar-header>.icon-right:after {
+    border: 7px solid #fff;
+    border-color: transparent transparent transparent #fff;
+    -webkit-transform: translateX(40%);
+    -moz-transform: translateX(40%);
+    -ms-transform: translateX(40%);
+    -o-transform: translateX(40%);
+    transform: translateX(40%);
+}
+
+.calendar-body>.c-body-head {
+    width: 100%;
+    display: flex;
+}
+
+.calendar-body>.c-body-head>li {
+    flex : 1;
+    text-align: center;
+    padding: 10px 0;
+}
+
+.calendar-body>.c-body-content {
+    width: 100%;
+}
+
+.calendar-body>.c-body-content>ul {
+    display: flex;
+}
+
+.calendar-body>.c-body-content>ul>li {
+    flex: 1;
+    text-align: center;
+    padding: 5px 0;
+}
+
+.content-row>.item-gray>a {
+    color: #ccc;
+}
+
+.content-row>.item-tag>a {
+    position: relative;
+}
+
+.content-row>.item-tag>a:after{
+    content: '';
+    width: 8px;
+    height: 8px;
+    position: absolute;
+    bottom: 0;
+    left: 50%;
+    -webkit-transform: translate3D(-50%, 70%, 0);
+    -moz-transform: translate3D(-50%, 70%, 0);
+    -ms-transform: translate3D(-50%, 70%, 0);
+    -o-transform: translate3D(-50%, 70%, 0);
+    transform: translate3D(-50%, 70%, 0);
+    background-color: #b0ceee;
+    border-radius: 50%;
+}
+
+.content-row>li>a {
+    width: 30px;
+    height: 30px;
+    line-height: 30px;
+    text-align: center;
+    display: inline-block;
+}
+
+.content-row>.item-current {
+    text-align: center;
+}
+.content-row>.item-current>a {
+    background-color: #1a85ff;
+    color: #fff;
+    border-radius: 50%;
+}

+ 3 - 0
react/.babelrc

@@ -0,0 +1,3 @@
+{
+  "presets": ["es2016", "es2015", "react"]
+}

File diff suppressed because it is too large
+ 3 - 0
react/assets/app.bundle.js


File diff suppressed because it is too large
+ 46 - 0
react/assets/vendor.bundle.js


+ 321 - 0
react/components/Calendar.js

@@ -0,0 +1,321 @@
+/**
+ * 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;

+ 27 - 0
react/entry/app.js

@@ -0,0 +1,27 @@
+/**
+ * Created by Ryn on 2016/8/7.
+ * 入口文件
+ */
+
+import '../style/style.css';
+
+import React from 'react';
+import ReactDOM from 'react-dom';
+import Calendar from '../components/Calendar';
+
+const App = React.createClass({
+    selectDate(year, month, day) {
+        alert("当前日期为:" + year + '年' + month + '月' + day + '日' );
+    },
+    render() {
+        return (
+            <Calendar onSelectDate={this.selectDate} year="2016" month="8" day="7" tags={[5, 21]} />
+        );
+    }
+});
+
+ReactDOM.render(
+    <App />,
+    document.getElementById('datePicker')
+);
+

+ 86 - 0
react/helpers/H.js

@@ -0,0 +1,86 @@
+/**
+ * Created by Ryn on 2016/8/6.
+ * 帮助函数
+ */
+
+
+// 一些私有变量
+
+// 一些公有变量、函数
+
+/**
+ *
+ * 判断这一年是闰年还是平年
+ * @param year {String/Number} 年份
+ * @returns {boolean}
+ */
+
+export const isLeapYear = function(year) {
+    if (!typeof +year === 'number') {
+        throw new Error("年份格式不正确");
+    }
+
+    if (+year < 1790) {
+        throw new Error("年份不能低于1790年");
+    }
+
+    // 计算闰年方法
+    // 1.能被4整除而不能被100整除
+    // 2.能被400整除
+
+    return (year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0);
+};
+
+/**
+ * 返回月份中的第一天是星期几
+ * @returns {number}
+ * 1 星期一
+ * 2 星期二
+ * 3 星期三
+ * 4 星期四
+ * 5 星期五
+ * 6 星期六
+ * 0 星期天
+ */
+export const weekOfMonth = function(date) {
+    if (!date) date = new Date();
+    return new Date(getFullYear(date), getMonth(date), 1).getDay();
+};
+
+/**
+ * 获取月份
+ * @param date
+ * @returns {*|number}
+ */
+export const getMonth = function(date) {
+    if (!date) date = new Date();
+    return date.getMonth();
+};
+
+/**
+ * 获取年份
+ * @param date
+ * @returns {number}
+ */
+export const getFullYear = function(date) {
+    if (!date) date = new Date();
+    return date.getFullYear();
+};
+
+/**
+ * 获取一月中的某一天
+ * @param date
+ * @returns {number}
+ */
+export const getDate = function(date) {
+    if (!date) date = new Date();
+    return date.getDate();
+};
+
+export default {
+    isLeapYear,
+    weekOfMonth,
+    getMonth,
+    getFullYear,
+    getDate
+};

+ 22 - 0
react/index.html

@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name ="viewport" content ="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no">
+    <title>React Calendar</title>
+    <style>
+        body{
+            font-family: '黑体';
+            font-size : 14px;
+        }
+        .container {
+            border: 1px solid #ccc;
+        }
+    </style>
+</head>
+<body>
+    <div id="datePicker" class="container"></div>
+    <script src="./assets/vendor.bundle.js"></script>
+    <script src="./assets/app.bundle.js"></script>
+</body>
+</html>

+ 32 - 0
react/package.json

@@ -0,0 +1,32 @@
+{
+  "name": "react-calendar",
+  "version": "1.0.0",
+  "description": "react calendar",
+  "main": "webpack.config.js",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "keywords": [
+    "calendar"
+  ],
+  "author": "ryn",
+  "license": "ISC",
+  "devDependencies": {
+    "babel-core": "^6.13.2",
+    "babel-helper-builder-react-jsx": "^6.9.0",
+    "babel-loader": "^6.2.4",
+    "babel-plugin-transform-flow-strip-types": "^6.8.0",
+    "babel-plugin-transform-react-jsx": "^6.8.0",
+    "babel-preset-es2015": "^6.13.2",
+    "babel-preset-es2016": "^6.11.3",
+    "babel-preset-react": "^6.11.1",
+    "css-loader": "^0.23.1",
+    "style-loader": "^0.13.1",
+    "webpack": "^1.13.1"
+  },
+  "dependencies": {
+    "ms": "^0.7.1",
+    "react": "^15.3.0",
+    "react-dom": "^15.3.0"
+  }
+}

+ 160 - 0
react/style/style.css

@@ -0,0 +1,160 @@
+div,ul,li {
+    margin: 0;
+    padding: 0;
+}
+
+ul,li {
+    list-style: none;
+}
+
+a {
+    text-decoration: none;
+    color: #000;
+}
+
+.calendar {
+    width: 100%;
+}
+
+.calendar-header {
+    width: 100%;
+    text-align: center;
+    padding: 13px 0;
+    line-height: 14px;
+    border-bottom: 1px solid #ddd;
+}
+
+.calendar-header>i{
+    display: inline-block;
+    width: 14px;
+    height: 14px;
+    vertical-align: -2px;
+    position: relative;
+}
+
+.calendar-header>span {
+    margin: 0 5%;
+}
+
+.calendar-header>.icon-left:before,
+.calendar-header>.icon-left:after,
+.calendar-header>.icon-right:before,
+.calendar-header>.icon-right:after
+{
+    content: '';
+    position: absolute;
+    top: 0;
+    left: 0;
+}
+
+.calendar-header>.icon-left:before {
+    border: 7px solid red;
+    border-color: transparent #666 transparent transparent;
+    -webkit-transform: translateX(-50%);
+    -moz-transform: translateX(-50%);
+    -ms-transform: translateX(-50%);
+    -o-transform: translateX(-50%);
+    transform: translateX(-50%);
+}
+
+.calendar-header>.icon-left:after {
+    border: 7px solid #fff;
+    border-color: transparent #fff transparent transparent;
+    -webkit-transform: translateX(-40%);
+    -moz-transform: translateX(-40%);
+    -ms-transform: translateX(-40%);
+    -o-transform: translateX(-40%);
+    transform: translateX(-40%);
+}
+
+.calendar-header>.icon-right:before {
+    border: 7px solid red;
+    border-color: transparent transparent transparent #666;
+    -webkit-transform: translateX(50%);
+    -moz-transform: translateX(50%);
+    -ms-transform: translateX(50%);
+    -o-transform: translateX(50%);
+    transform: translateX(50%);
+}
+
+.calendar-header>.icon-right:after {
+    border: 7px solid #fff;
+    border-color: transparent transparent transparent #fff;
+    -webkit-transform: translateX(40%);
+    -moz-transform: translateX(40%);
+    -ms-transform: translateX(40%);
+    -o-transform: translateX(40%);
+    transform: translateX(40%);
+}
+
+.calendar-body>.c-body-head {
+    width: 100%;
+    display: flex;
+}
+
+.calendar-body>.c-body-head>li {
+    flex : 1;
+    text-align: center;
+    padding: 10px 0;
+}
+
+.calendar-body>.c-body-content {
+    width: 100%;
+}
+
+.calendar-body>.c-body-content>ul {
+    display: flex;
+}
+
+.calendar-body>.c-body-content>ul>li {
+    flex: 1;
+    text-align: center;
+    padding: 5px 0;
+}
+
+.content-row>.item-gray>a {
+    color: #ccc;
+}
+
+.content-row>.item-tag>a {
+    position: relative;
+}
+
+.content-row>.item-tag>a:after{
+    content: '';
+    width: 8px;
+    height: 8px;
+    position: absolute;
+    bottom: 0;
+    left: 50%;
+    -webkit-transform: translate3D(-50%, 70%, 0);
+    -moz-transform: translate3D(-50%, 70%, 0);
+    -ms-transform: translate3D(-50%, 70%, 0);
+    -o-transform: translate3D(-50%, 70%, 0);
+    transform: translate3D(-50%, 70%, 0);
+    background-color: #b0ceee;
+    border-radius: 50%;
+}
+
+.content-row>li>a {
+    width: 30px;
+    height: 30px;
+    line-height: 30px;
+    text-align: center;
+    display: inline-block;
+}
+
+.content-row>.item-current, .content-row>.item-active {
+    text-align: center;
+}
+.content-row>.item-current>a {
+    background-color: #1a85ff;
+    color: #fff;
+    border-radius: 50%;
+}
+
+.content-row>.item-active>a {
+    background-color: #31a0c6;
+    color: #fff;
+    border-radius: 50%;
+}

+ 32 - 0
react/webpack.config.js

@@ -0,0 +1,32 @@
+var path = require('path');
+var webpack = require('webpack');
+
+var nodeModulesDir = path.join(__dirname, 'node_modules');
+
+module.exports = {
+    devtool: 'eval-source-map',
+    entry : {
+        app : './entry/app.js',
+        vendor : ['react', 'react-dom']
+    },
+    output : {
+        path : './assets/',
+        filename : '[name].bundle.js',
+    },
+    module : {
+        loaders : [
+            { test : /\.js$/, loader : 'babel' },
+            { test : /\.css$/, loader : 'style!css' },
+        ]
+    },
+    resolve : {
+        extensions: ["", ".webpack.js", ".web.js", ".js", ".less"]
+    },
+    plugins: [
+        new webpack.optimize.CommonsChunkPlugin(/* chunkName= */"vendor", /* filename= */"vendor.bundle.js"),
+        new webpack.optimize.UglifyJsPlugin({
+            sourceMap: false,
+            mangle: false
+        })
+    ]
+};