业务需求

  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)            }        }    }}