关于FL组件库日期组件制作思路


title: 关于FL组件库日期组件制作思路
date: 2022-09-03 20:42:05
tags:

  • 组件库
  • 组件开发
  • React
    categories:
  • 组件开发
  • React

关于FL组件库日期组件制作思路

关于日期组件开发思路,首天的计算极为重要,要想知道一个月中第一天也就是1号在日历的第几个位置,就要计算当月1号为周几。得到之后就方便很多了😋

单页日历的数据

获取当前月第一天

getDate获取日期

假设我们要获取2022-07月的日期数据

  1. 先获取当月的1日是周几—date.getDay()得到周5
    • 所以开始遍历的起始为第5个,依次往后遍历累加直到当月最后一天
  2. 填充42个格子
    • 长度为42的数组,如果当月1日不为周一则前半部分为当月总日从后往前依次递减排列
  3. 一个月的日历涵盖了三部分,上月剩余日期,当月全部日期,下个月初始日期
    • 上月剩余日期可以通过当月计算上月有几天,从开始的星期开始依次递减往前填充
    • 当月日期只需要直到当月有几天从1到28/29/30/31填充
    • 因为一个月的展示固定为42个格子,前两部分算出来了剩下的只需要减一下从1开始累加到结束就好了
  4. 其实最重要的就只需要知道年份,月份,就可以获取到该日历页面的数据
  5. 剩下的就是其他因素了,比如:如果当月为1号上个月的日期数据该怎么办,如果是12号呢?还有就是点击日期的切换等等…
    useEffect(()=>{
        // 监测月份和日期的变化
        // 1.前本部分为上月最后几天
        let frontArr = [];
        // 2.中间为当月日期
        let centerArr = [];
        // 3.若长度不大于42,后半部分1-开始累加
        let afterArr = [];
        let day = new Date(`${year}-${math}`);
        // console.log(day);
        // console.log('当月第一天为周',day.getDay());//获取当月第一天周几
        // console.log(day.getMonth());//获取上个月的月份(不需要加一)
        // 上个月有dayMath[day.getMonth()-1]
        let dayMath = [31,28,31,30,31,30,31,31,30,31,30,31];//一年每月的天数
        // console.log(day.getFullYear());//年份 
        if (day.getFullYear()%4 === 0 && day.getFullYear()%100 !== 0) {
            dayMath[1] = 29
        }
        // console.log('上个月有',dayMath[day.getMonth()-1]);
        let forOne = dayMath[day.getMonth()-1]
        if (day.getMonth()+1===1) {
            forOne = 31
        }
        // console.log(forOne);//上月的天数
        let shangMatch = day.getDay() - 1
        if (day.getDay()===0) {
            shangMatch = 6
        }

        // 如果这个月为1月,则上个月按照12月的31计算
        while (frontArr.length < shangMatch) {
            frontArr.unshift(forOne--)
        }
        // console.log('上个月的数组',frontArr);//上月的数组
        // console.log('这个月',day.getMonth()+1);
        let index = 1;
        // while(centerArr.length < dayMath[day.getMonth()-1]){
        //     centerArr.push(index++)
        // }
        // console.log(dayMath[day.getMonth()]);
        for (let i = 0; i < dayMath[day.getMonth()]; i++) {
            // console.log(28);
            if (index>31) {
                break
            }
            centerArr.push(index++)
        }
        // console.log('这个月的数组',centerArr);//这个月的数组

        let endFor = 42 - frontArr.length - centerArr.length;
        // console.log(endFor);
        for (let i = 1; i < endFor+1; i++) {
            afterArr.push(i)
        }
        // console.log('最后数组',afterArr);
        // 汇总数组 ---将一维数组转换为二维数组方便遍历数据渲染---有待改进算法
        let countArr = [...frontArr,...centerArr,...afterArr];
        // console.log('数组汇总',countArr);
        let newArr = [];
        newArr.push(countArr.slice(0,7))
        newArr.push(countArr.slice(7,14))
        newArr.push(countArr.slice(14,21))
        newArr.push(countArr.slice(21,28))
        newArr.push(countArr.slice(28,35))
        newArr.push(countArr.slice(35,42))
        // console.log(newArr);
        setArr(newArr)
    },[year,math])

页面渲染

image-20220903205537882

这里我是用的是<table>标签,所以我才需要将得到的数据重构成二维数组,方便渲染

日期的切换

点击下个月和上个月的交互其实很简单,因为上文我们页面的渲染有useEffect这个钩子监测。

所以当我们改变传入的初始日期就可以自动渲染页面

    // 事件
    function nextMath() {
        // 点击下一月份
        setMath(title=>{
            return title+1
        })
        if (math >= 12) {
            setyear(title=>{
                return title+1
            })
            setMath(title=>{
                return 1
            })
        }
        // console.log(myRefipt);
        // myRefipt.current.focus();
    }
    function upMath() {
        // 点击上一月份
        setMath(title=>{
            return title-1
        })
        if (math <= 1) {
            setyear(title=>{
                return title-1
            })
            setMath(title=>{
                return 12
            })
        }
    }

    // 点击下一年
    function nextYear() {
        setyear(title=>{
            return title+1
        })
    }
    // 点击上一年
    function upYear() {
        setyear(title=>{
            return title - 1
        })
    }
    // 点击今天跳转
    function teday(){
        let data = new Date();
        setyear(data.getFullYear());
        setMath(data.getMonth()+1)
        setDatas(data.getDate())
    }

源码

// index.jsx
import React,{useEffect, useRef, useState} from 'react'
import style from './index.module.scss'

function DatePicker() {
    // 获取焦点后的样式
    let [focus,setFocus] = useState(false);
    // main主体数据
    let [show,setShow] = useState(false)
    // 1. 初始日期
    // let [day,setday] = useState(new Date());
    // 2. 42格子遍历数据
    let [Arr,setArr] = useState([]);
    // 3. 年份 // 控制这俩就可以控制日历
    let [year,setyear] = useState(2022);
    // 4. 月份
    let [math,setMath] = useState(1);
    // 5. 日期
    let [datas,setDatas] = useState(0);
    // 6. 输入框
    let myRefipt = useRef();
    let mySection = useRef();
    // 7. 输入框内容
    let [iptValue,setiptValue] = useState("");

    // 获取当前月第一天
    // getDate获取日期
    // 假设我们要获取2022-07月的日期数据
    // 1. 先获取当月的1日是周几---date.getDay()得到周5
    //  - 所以开始遍历的起始为第5个,依次往后遍历累加直到当月最后一天
    // 2. 填充42个格子
    //  - 长度为42的数组,如果当月1日不为周一则前半部分为当月总日从后往前依次递减排列
    useEffect(()=>{
        // 监测月份和日期的变化
        // 1.前本部分为上月最后几天
        let frontArr = [];
        // 2.中间为当月日期
        let centerArr = [];
        // 3.若长度不大于42,后半部分1-开始累加
        let afterArr = [];
        let day = new Date(`${year}-${math}`);
        // console.log(day);
        // console.log('当月第一天为周',day.getDay());//获取当月第一天周几
        // console.log(day.getMonth());//获取上个月的月份(不需要加一)
        // 上个月有dayMath[day.getMonth()-1]
        let dayMath = [31,28,31,30,31,30,31,31,30,31,30,31];//一年每月的天数
        // console.log(day.getFullYear());//年份 
        if (day.getFullYear()%4 === 0 && day.getFullYear()%100 !== 0) {
            dayMath[1] = 29
        }
        // console.log('上个月有',dayMath[day.getMonth()-1]);
        let forOne = dayMath[day.getMonth()-1]
        if (day.getMonth()+1===1) {
            forOne = 31
        }
        // console.log(forOne);//上月的天数
        let shangMatch = day.getDay() - 1
        if (day.getDay()===0) {
            shangMatch = 6
        }

        // 如果这个月为1月,则上个月按照12月的31计算
        while (frontArr.length < shangMatch) {
            frontArr.unshift(forOne--)
        }
        // console.log('上个月的数组',frontArr);//上月的数组
        // console.log('这个月',day.getMonth()+1);
        let index = 1;
        // while(centerArr.length < dayMath[day.getMonth()-1]){
        //     centerArr.push(index++)
        // }
        // console.log(dayMath[day.getMonth()]);
        for (let i = 0; i < dayMath[day.getMonth()]; i++) {
            // console.log(28);
            if (index>31) {
                break
            }
            centerArr.push(index++)
        }
        // console.log('这个月的数组',centerArr);//这个月的数组

        let endFor = 42 - frontArr.length - centerArr.length;
        // console.log(endFor);
        for (let i = 1; i < endFor+1; i++) {
            afterArr.push(i)
        }
        // console.log('最后数组',afterArr);

        // 汇总数组
        let countArr = [...frontArr,...centerArr,...afterArr];
        // console.log('数组汇总',countArr);
        let newArr = [];
        newArr.push(countArr.slice(0,7))
        newArr.push(countArr.slice(7,14))
        newArr.push(countArr.slice(14,21))
        newArr.push(countArr.slice(21,28))
        newArr.push(countArr.slice(28,35))
        newArr.push(countArr.slice(35,42))
        // console.log(newArr);
        setArr(newArr)


    },[year,math])

    // 第一次进入页面当前日期
    useEffect(()=>{
        let data = new Date();
        // console.log(data.getFullYear());
        // console.log(data.getMonth());
        setyear(data.getFullYear());
        setMath(data.getMonth()+1)
        // console.log(data.getDate());
        setDatas(data.getDate())
    },[])

    useEffect(()=>{
        // 点击除去日历以外的部分隐藏日历
        document.onclick = ()=>{
            setShow(false)
            setFocus(false)
        }
    },[])

    // div获取焦点
    function divFocus(e) {
        e.nativeEvent.stopImmediatePropagation();
        // console.log('div获取了焦点');
        setFocus(true)
        setShow(true)
    }

    // 事件
    function nextMath() {
        // 点击下一月份
        setMath(title=>{
            return title+1
        })
        if (math >= 12) {
            setyear(title=>{
                return title+1
            })
            setMath(title=>{
                return 1
            })
        }
        // console.log(myRefipt);
        // myRefipt.current.focus();
    }
    function upMath() {
        // 点击上一月份
        setMath(title=>{
            return title-1
        })
        if (math <= 1) {
            setyear(title=>{
                return title-1
            })
            setMath(title=>{
                return 12
            })
        }
    }

    // 点击下一年
    function nextYear() {
        setyear(title=>{
            return title+1
        })
    }
    // 点击上一年
    function upYear() {
        setyear(title=>{
            return title - 1
        })
    }
    // 点击今天跳转
    function teday(){
        let data = new Date();
        setyear(data.getFullYear());
        setMath(data.getMonth()+1)
        setDatas(data.getDate())
    }

    // 鼠标移入改变输入框内容
    function mouseOver(tit,index) {
        // 获取到内容
        myRefipt.current.value = year+'-'+math+'-'+tit;
        if (index === 0) {
            if (tit > 7) {
                if (math > 1) {
                    myRefipt.current.value = year+'-'+(math-1)+'-'+tit;
                }else{
                    myRefipt.current.value = (year-1)+'-'+ 12 +'-'+tit;
                }
            }
        }else if(index === 4){
            if (tit < 7) {
               if (math < 12) {
                    myRefipt.current.value = year+'-'+(math+1)+'-'+tit;
               }else{
                    myRefipt.current.value = (year+1)+'-'+1+'-'+tit;
               }
            }
        }else if(index === 5){
            if (tit <= 14) {
                if (math < 12) {
                    myRefipt.current.value = year+'-'+(math+1)+'-'+tit;
                }else{
                    myRefipt.current.value = (year+1)+'-'+1+'-'+tit;
                }
            }
        }

    }

    // 鼠标点击改变ipt值,并且隐藏日历
    function clickIpt(tit,index) {
        if (index === 0) {
            if (tit > 7) {
                if (math > 1) {
                    iptValue = year+'-'+(math-1)+'-'+tit;
                    setiptValue(year+'-'+(math-1)+'-'+tit)
                }else{
                    iptValue = (year-1)+'-'+12+'-'+tit;
                    setiptValue((year-1)+'-'+12+'-'+tit)
                }
            }else{
                iptValue = year+'-'+math+'-'+tit;
                setiptValue(year+'-'+math+'-'+tit)
            }
        }else if(index === 4){
            if (tit < 7) {
                if (math<12) {
                    iptValue = year+'-'+(math+1)+'-'+tit;
                    setiptValue(year+'-'+(math+1)+'-'+tit)
                }else{
                    iptValue = (year+1)+'-'+1+'-'+tit;
                    setiptValue((year+1)+'-'+1+'-'+tit)
                }
            }else{
                iptValue = year+'-'+math+'-'+tit;
                setiptValue(year+'-'+math+'-'+tit)
            }
        }else if(index === 5){
            if (tit <= 14) {
                if (math<12) {
                    iptValue = year+'-'+(math+1)+'-'+tit;
                    setiptValue(year+'-'+(math+1)+'-'+tit)
                }else{
                    iptValue = (year+1)+'-'+1+'-'+tit;
                    setiptValue((year+1)+'-'+1+'-'+tit)
                }
            }else{
                iptValue = year+'-'+math+'-'+tit;
                setiptValue(year+'-'+math+'-'+tit)
            }
        }else{
            iptValue = year+'-'+math+'-'+tit;
            setiptValue(year+'-'+math+'-'+tit)
        }
        // console.log(iptValue);
        // console.log(iptValue);
        myRefipt.current.value = iptValue
        // console.log(myRefipt.current.value);
        setTimeout(() => {
            setShow(false)
        }, 0);
    }

    // 没有点击鼠标移出清除input
    function mouseOut() {  
        myRefipt.current.value = iptValue;
    }


  return (
    <div className={style.Box}
    onClick={(ev)=>divFocus(ev)}
    // onBlur={divBlur}
    
    >
        <div 
        className={focus?[style.iptBox,style.iptBoxFocu].join(' '):style.iptBox}>
            <input type="text"
                placeholder="请选择日期"
                ref={myRefipt}
            />
            <span>icon</span>
        </div>

        <section
        ref={mySection}
        tabIndex="-1"
        className={show?`${style.dropChangeUp}`:`${style.dropChangeDown}`}
        >
            <div className={style.sectionTop}>
                <header>
                    <button 
                    className={style.leftBtnOne}
                    onClick={upYear}
                    >
                        <span></span>
                    </button>
                    <button 
                    className={style.leftBtnTwo}
                    onClick={upMath}
                    >
                        <span></span>
                    </button>
                    <div>
                        <button 
                        className={style.yearBtn}
                        style={{marginRight:5}}
                        >
                            {year}</button>
                        <button className={style.monthBth}>
                            {math}</button>
                    </div>
                    <button 
                    className={style.rightBtnTwo}
                    onClick={nextMath}
                    >
                        <span></span>
                    </button>
                    <button 
                    className={style.rightBtnOne}
                    onClick={nextYear}
                    >
                        <span></span>
                    </button>
                </header>
                <main>
                    <table>
                        <thead>
                            <tr>
                                <th></th>
                                <th></th>
                                <th></th>
                                <th></th>
                                <th></th>
                                <th></th>
                                <th></th>
                            </tr>
                        </thead>
                        <tbody
                        onMouseOut={mouseOut}
                        >
                            {
                                Arr.map((title,index)=>{
                                    return (
                                        <tr key={index}>
                                           {
                                               title.map((tit,ind)=>{
                                                return (
                                                    <td 
                                                    key={ind}
                                                    className={
                                                        [index === 0 ? (tit > 7 ? `${style.lightColour}` : ""):
                                                        index === 5 ? (tit<=20 ? `${style.lightColour}` : ""):
                                                        index === 4 ? (tit<=7 ? `${style.lightColour}` : ""):
                                                        "",
                                                        tit === datas&&year===new Date().getFullYear()&&math===new Date().getMonth()+1 ? `${style.choice}`:""
                                                    ].join('')
                                                    }
                                                    onMouseOver={()=>mouseOver(tit,index)}
                                                    
                                                    onClick={()=>clickIpt(tit,index)}
                                                    >{tit}</td>
                                                )
                                            })
                                           }
                                        </tr>
                                    )
                                })
                            }
                        </tbody>
                    </table>
                </main>
            </div>
            <footer className={style.footer}>
                <a href="javascript:void(0)" onClick={teday}>今天</a>
            </footer>
        </section>
    </div>
  )
}

export default DatePicker

scss


$allClor:#64abfb;

a{
    text-decoration: none;
}
.iptBox{
    width: 170px;
    height: 30px;
    border: 1px solid #afafaf;
    display: flex;
    justify-content: space-between;
    align-items: center;
    transition: all 0.3s ease;
    border-radius: 2px;

    &:hover{
        border: 1px solid $allClor;
    }

    input{
        width: 80%;
        border: none;
        box-sizing: border-box;
        outline: none;
        height: 100%;
        padding-left: 8px;
        font-size: 16px;
    }
    span{
        display: inline-block;
        flex: 1;
        height: 100%;
        line-height: 30px;
        user-select: none;
    }
}

.Box{
    position: relative;
}


section{
    display: flex;
    flex-direction: column;
    width: 280px;
    height: 308px;
    // border: 1px solid #afafaf;
    box-shadow: 0px 0px 20px -5px #afafaf;
    overflow: hidden;
    position: absolute;
    background-color: #fff;
    z-index: 100;

    .sectionTop{
        display:flex;
        flex: 1;
        flex-direction: column;
        height: 32px;

        header{
            display: flex;
            justify-content: center;
            padding: 0px 20px;
            align-items: center;
            height: 40px;
            border-bottom: 1px solid #f0f0f0;
            div{
                height: 100%;
                flex: 1;
                display: flex;
                justify-content: center;
                button{
                    height: 100%;
                    text-align: center;
                    color: #808080;
                    font-weight: 600;
                    transition: all 0.3s ease;
                    cursor: pointer;
                    &:hover{
                        color: $allClor;
                    }
                }
            }
            button{
                height: 100%;
                border: none;
                background-color: #fff;
            }

            .rightBtnOne span,
            .rightBtnTwo span,
            .leftBtnTwo span,
            .leftBtnOne span{
                width: 7px;
                height: 7px;
                cursor: pointer;
                position: relative;
            }


            .leftBtnOne span::after{
                top: 3px;
                left: -4px;
                position: absolute;
                content: "";
                display: inline-block;
                width: 7px;
                height: 7px;
                border-top: 2px solid #afafaf;
                border-left: 2px solid #afafaf;
                transform: rotate(-45deg);
                margin-left: -2px;
                transition: all 0.3s ease;
            }
            .rightBtnOne span::after{
                left: -4px;
                top: 3px;
                position: absolute;
                content: "";
                display: inline-block;
                width: 7px;
                height: 7px;
                border-top: 2px solid #afafaf;
                border-left: 2px solid #afafaf;
                transform: rotate(135deg);
                margin-left: -2px;
                transition: all 0.3s ease;
            }
            .leftBtnOne span::before{
                content: "";
                display: inline-block;
                width: 7px;
                height: 7px;
                border-top: 2px solid #afafaf;
                border-left: 2px solid #afafaf;
                transform: rotate(-45deg);
                transition: all 0.3s ease;
            }
            .rightBtnOne span::before{
                content: "";
                display: inline-block;
                width: 7px;
                height: 7px;
                border-top: 2px solid #afafaf;
                border-left: 2px solid #afafaf;
                transform: rotate(135deg);
                transition: all 0.3s ease;
            }

            // 鼠标移入
            .leftBtnTwo{
                width: 30px;
                // 鼠标移入
                &:hover span::after{
                    border-top: 2px solid #797979;
                    border-left: 2px solid #797979;
                }
            }
            .rightBtnTwo{
                width: 30px;
                // 鼠标移入
                &:hover span::after{
                    border-top: 2px solid #797979;
                    border-left: 2px solid #797979;
                }
            }
            .rightBtnOne{
                width: 20px;
                // 鼠标移入
                &:hover span::after,
                &:hover span::before{
                    border-top: 2px solid #797979;
                    border-left: 2px solid #797979;
                }
            }
            .leftBtnOne{
                width: 20px;
                // 鼠标移入
                &:hover span::after,
                &:hover span::before{
                    border-top: 2px solid #797979;
                    border-left: 2px solid #797979;
                }
            }

            .leftBtnTwo span::after{
                content: "";
                display: inline-block;
                width: 7px;
                height: 7px;
                border-top: 2px solid #afafaf;
                border-left: 2px solid #afafaf;
                transform: rotate(-45deg);
                margin-left: -2px;
                transition: all 0.3s ease;
            }
            .rightBtnTwo span::after{
                content: "";
                display: inline-block;
                width: 7px;
                height: 7px;
                border-top: 2px solid #afafaf;
                border-left: 2px solid #afafaf;
                transform: rotate(135deg);
                margin-left: -2px;
                transition: all 0.3s ease;
            }



        }
        main{
            flex: 1;
        }
    }
    .footer{
        display: flex;
        height: 35px;
        border-top: 1px solid #f0f0f0;
        a{
            margin: auto;
            color: $allClor;
        }
    }


    main{
        padding: 10px;

        table{
            height: 100%;
            display: flex;
            flex-direction: column;
            width: 100%;
            color: #363636;
            thead{
                height: 30px;
                tr{
                    display: flex;
                    th{
                        flex: 1;
                    }
                }
            }
            tbody{
                display: flex;
                flex-direction: column;
                flex: 1;

                tr{
                    display: flex;
                    flex: 1;
                    td{
                        flex: 1;
                        text-align: center;
                        line-height: 29px;
                        cursor: pointer;
                        font-weight: 400;
                        transition: all 0.3s  ease;
                        user-select: none;
                        &:hover{
                            background-color: #f0f0f0;
                        }
                    }
                }

                .lightColour{
                    color: #afafaf;
                }

                .choice{
                    background-color: #64abfb;
                    color: #fff;
                }

            }
        }
    }

}


.iptBoxFocu{
    border: 1px solid $allClor;
    box-shadow: 0px 0px 3px 0.5px $allClor;
}



.dropChangeUp{
    animation: changeUp 0.2s ease 1 normal forwards 
}
.dropChangeDown{
    animation: changeDown 0.2s ease 1 normal forwards 
}

/* 动画函数 */
@keyframes changeUp {
    /*以一种写法*/
    0% {
        top: 0px;
        opacity: 0;
        height: 0px;
    }
    100%{
        top: 40px;
        opacity: 1;
        height: 308px;
    }
}
@keyframes changeDown {
    /*以一种写法*/
    from {
        top: 40px;
        opacity: 1;
        height: 308px;
    }
    to {
        top: 0px;
        opacity: 0;
        height: 0px;
    }
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值