js 实现日历的核心算法

*日期的核心算法

  • 1、计算本月有多少天
  • 2、本月的第一天是星期几
  • 3、用本月第一天的星期数W+总共天数T 最后得到一个总数WT
  • 用这个数来做循环控制量,let(let i=0;iW,则计算号数 n=i-W+1;数组添加push[n]
  • 4、把这个数组按7,分成二维数组就可以了,这个二维数组就是每个月对应的日历
  • 5、最后如果这个二维数组的最后一个元素不满7,则补0

参考链接:
日历参考链接

import {logClass} from "Components/Decorator/Decorator";

interface IDay {
    dayNum:number;
    isSignIn?: boolean;
    isShowSignIn: boolean;
}
@logClass
class DateItem {
    /**
     * @param  dayNum 日数, 如果和 new Date().getDate() 相等则是今天
     * @param  isSignIn=false 是否签到
     * @param  isShowSignIn=false 是否显示是否签到,大于今日和这个月的日期应该都不显示
     */
    dayNum:number;
    isSignIn?: boolean;
    isShowSignIn: boolean;
    constructor(day:IDay) {
        this.dayNum=day.dayNum;
        this.isSignIn=day.isSignIn;
        this.isShowSignIn=day.isShowSignIn;
    };
}
export default DateItem;
import * as React from 'react';
import DateItem from './DateItem'
import _ from 'lodash'
export interface IDatePickerProps{
    DefaultDate:string[];
}
export interface IDatePickerState {
    year:number;//年份
    month:number;//月份

    date:Date,//当前时间点
    nowadays:number;//当日
    thisMonth:number;//当月

    day:number;//本月第一天是星期数
    days:number,//本月总有多少天

    list:DateItem[],//天数+第一天星期数的数组
    twoDimArr:DateItem[][],//list分割成的二维数组

}

interface IYMD {//年月日接口
    year:number;
    month:number;
    day?:number;

}
const l=console.log;
const weeks = ["日", "一", "二", "三", "四", "五", "六"];
class DatePicker extends React.Component{
    constructor(props: Readonly){
        super(props);
        this.initState=this.initState.bind(this);
        this.handleDateItemClick=this.handleDateItemClick.bind(this);
        this.handleNextMonth=this.handleNextMonth.bind(this);
        this.handlePreMonth=this.handlePreMonth.bind(this);
        this.toDayClick=this.toDayClick.bind(this);
    }

    componentWillMount(): void {
        this.initState({year:0,month:0});
    }

   // @autoBindThis
    initState(ymd:IYMD){
        const date=new Date();
        l(`${ymd}`);
        //获取天数
        const year=ymd.year||date.getFullYear();//年
        const month=ymd.month||date.getMonth()+1;//月
        l(`${year}年${month}月`);

        let date2 = new Date(year, month, 0);
        let days = date2.getDate(); // 本月有多少天
        l(`本月有${days}天.`);

        //本月第一天是星期几
        date2.setDate(1);
        let day = date2.getDay(); // 本月第一天是星期几
        l(`本月第一天是星期${day}.`);


        let list = [];
        const nowadays = date.getDate(); // 本日
        const thisMonth = date.getMonth() + 1; // 本月


        let isShowSignIn:boolean = false; //是否可以点击
        const date2GtDate = date2 > date;//传入的年月是不是在当前日期前
        const isThisMonth = month === thisMonth; // 传入的月month,选择的日期的月份是否是本月

        //得到一个星期数+本月天数的长度数组
        for (let i = 0; i < days + day; i++) {
            const dayNum = i - day + 1;
            //判断是否可以签到
            if (date2GtDate) {
                isShowSignIn = false;
            } else {
                if (isThisMonth && i >= day + nowadays) {
                    isShowSignIn = false;
                } else {
                    isShowSignIn = true;
                }
            }
            //添加日期的编号
            if (i < day) {
                list.push(new DateItem({ dayNum: 0, isShowSignIn }));
            } else {
                list.push(new DateItem({ dayNum, isShowSignIn }));
            }


        }
        let twoDimArr=this.getTwoDimensional(list,isShowSignIn);
        this.setState({
            year,
            month,
            date,

            nowadays,
            thisMonth,

            day,
            days,

            list,
            twoDimArr,


        });
}

    getTwoDimensional(list:DateItem[],isShowSignIn:boolean):DateItem[][]{
       let twoDimensionArr= _.chunk(list,7);
       let twoDimLength=twoDimensionArr.length;
       let to=7-twoDimensionArr[twoDimLength-1].length;

    // 循环尾部补空格
        for (let i = 0; i < to; i++) {
            twoDimensionArr[twoDimLength - 1].push(new DateItem({ dayNum: 0, isShowSignIn }));
        }
       return twoDimensionArr

}

    //上一月
   // @autoBindThis
    handlePreMonth(){
        alert("000");
        let prevMonth = this.state.month + -1;
        let prevYear = this.state.year;
        if (prevMonth < 1) {
            prevMonth = 12;
            prevYear -= 1;
        }
        this.initState({year:prevYear,month:prevMonth});
    }
    //下一个月
    //@autoBindThis
    handleNextMonth(){
        let nextMonth = this.state.month + 1;
        let nextYear = this.state.year;
        if (nextMonth > 12) {
            nextMonth = 1;
            nextYear += 1;
        }
        this.initState({
            year: nextYear,
            month: nextMonth,
        });
    };

    toDayClick(e:any) {
        this.initState({year:0,month:0});
        e.stopPropagation();
    }

    // 点击每个日期
    //@autoBindThis
    handleDateItemClick (dateItem:DateItem, i:number, j:number){
        const { year, month, date, nowadays } = this.state;
        const { isShowSignIn, isSignIn, dayNum } = dateItem;
        if (dayNum === 0) return;
        const selectDate = new Date(`${year}-${month}-${dayNum}`);
        if (nowadays === dayNum) {
            l("签到");
        } else if (selectDate < date) {
            l("补签");
        }

        if (!isShowSignIn || isSignIn)
        // 不能签到的日期和已签到的日期直接返回
            return;

        this.setState(state => {
            const twoDimArr = state.twoDimArr.slice();
            twoDimArr[i][j].isSignIn = true;
            return {twoDimArr};
        });
    };


    render(): React.ReactNode {
        const {year,month,nowadays,twoDimArr,thisMonth}=this.state;
        return (
            

{year}年-{month}月

{weeks.map(el => ( ))} {this.state.twoDimArr.map((el, i) => { return ( {el.map((dateItem, j) => { const dayNum = dateItem.dayNum; const isSignIn = dateItem.isSignIn; const isShowSignIn = dateItem.isShowSignIn; return ( ); })} ); })}
{el}
{ e.stopPropagation(); this.handleDateItemClick(dateItem, i, j)}} >
{dayNum}
{!!isShowSignIn && (
{!!isSignIn ? `已签到` : `未签到`}
)}
); } } export default DatePicker;

你可能感兴趣的:(js 实现日历的核心算法)