js 实现日历的核心算法

*日期的核心算法

  • 1、计算本月有多少天
  • 2、本月的第一天是星期几
  • 3、用本月第一天的星期数W+总共天数T 最后得到一个总数WT
  • 用这个数来做循环控制量,let(let i=0;i<WT,i++)如果在i<W,往数组push[0],如果i>W,则计算号数 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<IDatePickerProps,IDatePickerState>{
    constructor(props: Readonly<IDatePickerProps>){
        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 (
            <div>
            <h2>
                {year}年-{month}月
            </h2>
            <div>
                <button onClick={this.handlePreMonth}>上月</button>
                <button onClick={this.handleNextMonth}>下月</button>
                <button onClick={this.toDayClick}>今天</button>
            </div>
            <table>
                <tbody>
                <tr>
                    {weeks.map(el => (
                        <th key={el}>{el}</th>
                    ))}
                </tr>
                {this.state.twoDimArr.map((el, i) => {
                    return (
                        <tr key={i}>
                            {el.map((dateItem, j) => {
                                const dayNum = dateItem.dayNum;
                                const isSignIn = dateItem.isSignIn;
                                const isShowSignIn = dateItem.isShowSignIn;
                                return (
                                    <td
                                        key={j}
                                        style={{
                                            color: (dayNum === nowadays && month === thisMonth && "red")||undefined,
                                            textAlign: "center",
                                            padding: 8,
                                            border: "1px solid",
                                            borderColor: dateItem.isSignIn ? "red" : "transparent",
                                            opacity: dayNum === 0 ? 0 : 1,
                                        }}
                                        onClick={(e)=>{
                                            e.stopPropagation();
                                            this.handleDateItemClick(dateItem, i, j)}}
                                    >
                                        <div>{dayNum}</div>
                                        {!!isShowSignIn && (
                                            <div
                                                style={{
                                                    whiteSpace: "nowrap",
                                                    fontSize: "12px",
                                                }}
                                            >
                                                {!!isSignIn ? `已签到` : `未签到`}
                                            </div>
                                        )}
                                    </td>
                                );
                            })}
                        </tr>
                    );
                })}
                </tbody>
            </table>
        </div>);
    }


}

export default DatePicker;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值