由於antd的日期組件都是選擇單個日期或者日期範圍。不符合需求,因此本身就實現了一個。寫的很差的地方你們請指教javascript
測試組件css
<CheckCalendar visible={this.state.showCalendar} onClose={()=>{ this.setState({ showCalendar:false }) }} onConfirm={(isCheck)=>{ console.log(isCheck) this.setState({ showCalendar:false }) }} />
CheckCalendar.jsxjava
import React, { Component, Fragment } from "react"; import { cloneDeep, chunk } from "lodash"; import PropTypes from 'prop-types'; import "animate.css"; import "./index.scss" 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 < 2; i++) { // 取近三個月內的日期 let obj = this.getDateTable(i); temp.push(obj); } 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 }) } } onExit = () => { const { onCancel } = this.props; onCancel && onCancel(); } onConfirm = () => { const { onConfirm } = this.props; onConfirm && onConfirm(this.state.isCheck); } render() { return this.props.visible ? ( <div className="calendar-mask"> <div className="calendar-wrap animated fadeInUp"> <RenderCalendarHeader onExit={this.onExit} /> <RenderChineseWeek /> <RenderDateTemp dateTable={this.state.dateTable} handleItemClick={this.handleItemClick} self={this} /> <div className="fake-area"></div> </div> <RenderConfirm onConfirm={this.onConfirm} /> </div> ) : (<span></span>) } } /** * 渲染表格每一個item * */ const RenderDateItem = (props) => { const { number, active } = props; return 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">{number}</div> <span className="right"></span> </div> ) : ( <div className="date-wrap"> <span className="left"></span> <div className={`item ${active ? 'active' : ''}`} onClick={props.itemClick} > <span>{number}</span> </div> <span className="right"></span> </div> ) } /** * 日曆頂部 * @param props.onExit 退出事件 */ const RenderCalendarHeader = (props) => { const { onExit } = props; return ( <div className="header"> <span>日期多選</span> <div className="exit" onClick={onExit}></div> </div> ) } /** * 渲染中文日期 */ const RenderChineseWeek = () => { const weeks = ["日", "一", "二", "三", "四", "五", "六"]; return ( <div className="week-wrap"> { weeks.map((item, index) => ( <div className="week-item" key={index}>{item}</div> )) } </div> ) } /** * * @param props.dateTable 模板數組 * @param prop.handleItemClick item點擊事件 * @param prop.self 父組件做用域 */ const RenderDateTemp = (props) => { const { dateTable, handleItemClick, self } = props; return ( <Fragment> { dateTable.map((item, index) => { const arr = item.desc.split("-"); return ( <div className="date-table" key={index}> <span className="desc"> {arr[0] + "年" + arr[1] + "月"} </span> { item.list.map((item2, index2) => { return ( <div className="row" key={index2}> { item2.map((item3, index3) => { return ( <RenderDateItem itemClick={handleItemClick.bind(self, item.desc, item3.number, index, index2, index3)} active={item3.isActive} disable={item3.disable ? item3.disable : false} number={item3.number} key={index3} /> ) }) } </div> ) }) } </div> ); }) } </Fragment> ) } const RenderConfirm = (props) => { return ( <div className="confirm-wrap"> <div className="confirm" onClick={props.onConfirm}> 肯定 </div> </div> ) } /** * @param onCancel 關閉事件回調 * @param onConfirm 確認事件回調 * @param visible 組件顯示狀態 */ CheckCalendar.propTypes = { onCancel: PropTypes.func, onConfirm: PropTypes.func, visible: PropTypes.bool } export default CheckCalendar;
checkCalendar.scssreact
.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) } } } }