共计 5908 个字符,预计需要花费 15 分钟才能阅读完成。
业务需求
- 多选近三个月的日期。
- 不能选择当日之前的日期。
因为 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)
}
}
}
}
正文完
发表至: javascript
2019-09-07