美文网首页
模仿antd手写一个多选日期日历组件

模仿antd手写一个多选日期日历组件

作者: pppercywang | 来源:发表于2019-11-12 17:33 被阅读0次

    业务需求

    1. 多选近三个月的日期。
    2. 不能选择当日之前的日期。

    因为antd的日期组件都是选择单个日期或者日期范围。不符合需求,所以自己就实现了一个。写的不好的地方大家请指教

    效果展示

    在这里插入图片描述
    测试组件
    <CheckCalendar
          visible={this.state.showCalendar}
           onClose={()=>{
                this.setState({
                     showCalendar:false
                })
           }}
           onConfirm={(isCheck)=>{
                console.log(isCheck)
                this.setState({
                     showCalendar:false
                })
           }}
           />
    

    CheckCalendar.jsx

    import React, { Component } from "react";
    import { cloneDeep, chunk } from "lodash";
    import "animate.css";
    import "./checkCalendar.scss"
    const weeks = ["日", "一", "二", "三", "四", "五", "六"];
    class CheckCalendar extends Component {
      constructor(props) {
        super(props)
        this.state = {
          dateTable: [],
          isCheck: [],
        }
        this.calendar = React.createRef();
        this.mask = React.createRef();
      }
      componentWillMount() {
        this.initDateTable()
      }
      initDateTable() {
        let temp = []
        for (let i = 0; i < 3; i++) {  // 取近三个月内的日期
          let obj = this.getDateTable(i);
          temp.push(obj);
        }
        console.log(temp);
        this.setState({
          dateTable: temp
        })
      }
      getDateTable(plus) {
        let curDate = new Date()  //现在时间
        let curYear = curDate.getFullYear();
        let curMonth = curDate.getMonth() + 1;
        let curDay = curDate.getDate();
        if (curMonth + plus > 12) {
          curYear++
          curMonth = curMonth + plus - 12
        } else {
          curMonth = curMonth + plus
        }
        let date = new Date(curYear, curMonth, 0);
        let year = date.getFullYear(); // 当前年
        let month = date.getMonth() + 1; // 当前月
        // console.log(`${year}年${month}月.`);
    
        let date2 = new Date(year, month, 0);
        let days = date2.getDate(); // 当月有多少天
        // console.log(`当月有${days}天.`);
    
        date2.setDate(1);
        let day = date2.getDay(); // 当月第一天是星期几
        // console.log(`当月第一天是星期${day}.`);
    
        let list = [];
    
        for (let i = 0; i < days + day; i++) {
          if (i < day) {  // 头部补零
            list.push({
              isActive: false,
              number: 0
            });
          } else {
            if (plus === 0) {
              if ((i - day + 1) < curDay) {
                list.push({
                  disable: true,
                  isActive: false,
                  number: i - day + 1
                });
              } else {
                list.push({
                  isActive: false,
                  number: i - day + 1
                });
              }
            } else {
              list.push({
                isActive: false,
                number: i - day + 1
              });
            }
          }
        }
        let hlist = chunk(list, 7); // 转换为二维数组
        let len = hlist.length;
        let to = 7 - hlist[len - 1].length;
    
        // 循环尾部补0
        for (let i = 0; i < to; i++) {
          hlist[len - 1].push({
            isActive: false,
            number: 0
          });
        }
        if (month < 10) {
          month = "0" + month
        }
        const str = `${year}-${month}`
        return {
          "list": hlist,
          "desc": str
        }
      }
      handleItemClick(desc, number, index, index1, index2) {
        let temp = cloneDeep(this.state.dateTable)
        const flag = !temp[index].list[index1][index2].isActive
        temp[index].list[index1][index2].isActive = flag
        this.setState({
          dateTable: temp,
        })
        const arr = desc.split("-");
        if (number < 10) {
          number = "0" + number
        }
        if (flag) {
          let temp = cloneDeep(this.state.isCheck);
          temp.push(arr[0] + "-" + arr[1] + "-" + number)
          this.setState({
            isCheck: temp
          })
        } else {
          let temp = cloneDeep(this.state.isCheck);
          let filted = temp.filter((item) => {
            return item !== arr[0] + "-" + arr[1] + "-" + number
          })
          this.setState({
            isCheck: filted
          })
        }
      }
      render() {
        return this.props.visible ? (
          <div ref={this.mask} className="calendar-mask">
            <div ref={this.calendar} className="calendar-wrap animated fadeInUp">
              <div className="header">日期多选<div className="exit" onClick={() => {
                this.calendar.current.classList.remove("fadeInUp")
                this.calendar.current.classList.add("fadeOutDown")
                this.mask.current.classList.add("animated", "fadeOut")
                setTimeout(() => {
                  this.props.onCancel()
                }, 150)
              }}></div></div>
              <div className="week-wrap">
                {
                  weeks.map((item, index) => (
                    <div className="week-item" key={index}>{item}</div>
                  ))
                }
              </div>
              {this.state.dateTable.map((item, index) => {
                const arr = item.desc.split("-");
                return (
                  <div className="date-table" key={index}>
                    <div className="desc">{arr[0] + "年" + arr[1] + "月"}</div>
                    {item.list.map((item2, index2) => {
                      return (<div className="row" key={index2}>
                        {item2.map((item3, index3) => {
                          return (<DateItem itemClick={this.handleItemClick.bind(this, item.desc, item3.number, index, index2, index3)} active={item3.isActive} disable={item3.disable ? item3.disable : false} number={item3.number} key={index3}></DateItem>)
                        })}
                      </div>)
                    })}
                  </div>
                );
              })}
              <div className="fake-area"></div>
            </div>
            <div className="confirm-wrap">
              <div className="confirm" onClick={() => {
                this.calendar.current.classList.remove("fadeInUp")
                this.calendar.current.classList.add("fadeOutDown")
                this.mask.current.classList.add("animated", "fadeOut")
                setTimeout(() => {
                  this.props.onConfirm(this.state.isCheck)
                }, 150)
              }}>
                确定
              </div>
            </div>
          </div>
        ) : (<span></span>)
      }
    }
    function DateItem(props) {
      return props.number === 0 ? (<div className="date-wrap">
        <span className="left"></span><div className="item"></div><span className="right"></span>
      </div>) : props.disable ? (<div className="date-wrap">
        <span className="left"></span><div className="item disable">{props.number}</div><span className="right"></span>
      </div>) : (<div className="date-wrap">
        <span className="left"></span><div className={`item ${props.active ? 'active' : ''}`} onClick={props.itemClick} >{props.number}</div><span className="right"></span>
      </div>)
    }
    export default CheckCalendar;
    

    checkCalendar.scss

    .calendar-mask {
        position: fixed;
        width: 100%;
        height: 100%;
        left: 0;
        top: 0;
        background-color: rgba(0, 0, 0, 0.5);
    
        .calendar-wrap {
            width: 100%;
            height: 100%;
            background-color: #ffffff;
            overflow: auto;
            animation-duration: .3s;
    
            .header {
                color: black;
                font-size: 17px;
                font-weight: bold;
                height: 30px;
                line-height: 30px;
    
                .exit {
                    width: 20px;
                    height: 20px;
                    position: relative;
                    float: left;
                    left: 20px;
                    top: 5px;
                }
    
                .exit::before,
                .exit::after {
                    content: "";
                    position: absolute;
                    height: 20px;
                    width: 1.5px;
                    left: 8.5px;
                    background: #098fef;
                }
    
                .exit::before {
                    transform: rotate(45deg);
                }
    
                .exit::after {
                    transform: rotate(-45deg);
                }
    
            }
    
            .week-wrap {
                display: flex;
                font-size: 16px;
                border-bottom: 1px solid rgb(221, 221, 221);
    
                .week-item {
                    height: 30px;
                    line-height: 30px;
                    width: 14.28571429%;
                }
            }
    
            .date-table {
                margin-top: 20px;
    
                .desc {
                    text-align: left;
                    text-indent: 12px;
                    font-size: 18px;
                }
    
                .row {
                    display: flex;
                    margin: 8px 0px;
    
                    .date-wrap {
                        height: 35px;
                        width: 14.28571429%;
                        line-height: 30px;
    
                        .left {
                            width: 100%;
                        }
    
                        .item {
                            display: inline-block;
                            width: 35px;
                            height: 35px;
                            font-size: 15px;
                            font-weight: bold;
                            line-height: 35px;
                            border-radius: 50%;
                        }
    
                        .disable {
                            background-color: rgb(238, 238, 238);
                            color: rgb(187, 187, 187);
                        }
    
                        .active {
                            background-color: #108ee9;
                            color: #ffffff;
                        }
    
                        .right {
                            width: 100%;
                        }
                    }
                }
            }
    
            .fake-area {
                height: 53px;
                width: 100%;
            }
        }
    
        .confirm-wrap {
            position: fixed;
            bottom: 0;
            height: 54px;
            width: 100%;
            box-sizing: border-box;
            border-top: 1px solid rgb(221, 221, 221);
            background-color: rgb(247, 247, 247);
            display: flex;
            align-items: center;
            justify-content: center;
    
            .confirm {
                border-radius: 5px;
                width: 90%;
                background-color: #108ee9;
                font-size: 18px;
                color: #ffffff;
                padding: 8px 0;
    
                &:active {
                    background-color: rgb(14, 128, 210);
                    color: rgb(85, 166, 223)
                }
            }
        }
    }
    

    相关文章

      网友评论

          本文标题:模仿antd手写一个多选日期日历组件

          本文链接:https://www.haomeiwen.com/subject/zbfyyctx.html