幾乎以前我經手的全部項目,都有用到日期選擇器或者日曆的控件,datetimepicker、calendar.js,都是基於JQ的控件。javascript
奈何產品爸爸總提一些UI和交互上的需求,改這些控件的源碼很差改啊!碰巧看了一篇用原生js擼日曆的文章,傳送連接,寫的很是棒啊,挺受啓迪的我,就在現有的react項目裏擼了個輪子,大佬們多多指點那,沒有太寫樣式,主要實現功能來着,效果以下。java
this.state = {
dates: [
[{}, {}, {}, {}, {}, {}, {}],
[...],
[...],
[...],
[...],
[...],
],
};複製代碼
1.經過最上面form中的下拉菜單選擇當前月份,轉化成moment對象react
2.使用moment的startOf('month')方法,找到當月的第一天算法
3.使用moment的.isoWeekday()方法,找到當月的第一天是星期幾數組
4.經過這兩個值,便可計算出日曆上的當月顯示的第一天,算法以下antd
let now = moment();
//當月第一天
let firstDayOfMonth = now.clone().startOf('month');
//當月第一天是星期幾
let weekOfFirstDay = firstDayOfMonth.isoWeekday();
//日曆上的當月顯示的第一天
let firstDayOfMonthDisplay = firstDayOfMonth.clone().subtract(weekOfFirstDay - 1, 'days');複製代碼
5.有了第一天的moment對象,遍歷6周和每週7天,就能夠組裝出上面提到的數據結構了
數據結構
let dates = [];
//單個日曆,顯示6周
const weeksNum = 6;
//每週顯示7天
const weekDaysNum = 7;
for (let i=0; i<weeksNum; i++){
let weekList = [];
for (let j=0; j<weekDaysNum; j++){
...
weekList.push(item);
currentDate.add(1, 'days');
}
dates.push(weekList);
}
複製代碼
1.假期的話,組裝這樣一個{日期: 假期}的映射,在上面步驟中遍歷天的時候,判斷一下是否存在於這個map中,若是有則把假期名稱置到item中。app
const holidayMap = {
"2019-05-01": "勞動節1",
"2019-05-02": "勞動節2",
"2019-05-03": "勞動節3",
"2019-05-04": "五四青年節",
...
};
複製代碼
2.相似的還有,非當前月日期的樣式,週末樣式,公休加班的樣式,都是在遍歷當天的時候去作判斷,把須要的樣式push到一個數組裏,而後.join(" ")組裝成當天完整的樣式便可。less
import React, { Component } from 'react';
import { Menu, Icon, Form, Select, Row, Col, Button } from 'antd';
import moment from 'moment';
import '@/static/less/le_calendar.less';
const { Option } = Select;
const FormItem = Form.Item;
// console.log(moment);
const DATE_FORMAT = "YYYY-MM-DD";
const holidayMap = {
"2019-05-01": "勞動節1",
"2019-05-02": "勞動節2",
"2019-05-03": "勞動節3",
"2019-05-04": "五四青年節",
"2019-06-07": "端午節1",
"2019-06-08": "端午節2",
"2019-06-09": "端午節3",
};
class queryForm extends React.Component {
constructor(props) {
super(props);
this.state = {
options: {
years: [],
months: [],
},
};
};
state = {
};
componentDidMount = function () {
let mySelf = this;
mySelf.init();
};
init = () => {
let mySelf = this;
mySelf.initOptions();
mySelf.setDefaultOption();
};
initOptions = function(){
let mySelf = this;
let options = mySelf.state.options;
let now = moment();
const yearOfNow = now.year();
let yearsOptions = [];
let monthsOptions = [];
const startYear = yearOfNow - 20;
const endYear = yearOfNow + 20;
const startMonth = 1;
const endMonth = 12;
for (let i=startYear; i<endYear; i++){
let item = {
key: i,
label: i,
}
yearsOptions.push(item);
}
for (let i=startMonth; i<=endMonth; i++){
let item = {
key: i,
label: i,
}
monthsOptions.push(item);
}
// console.log(platformOptions);
options.years = yearsOptions;
options.months = monthsOptions;
mySelf.setState({
options: options
});
};
setDefaultOption = () => {
let mySelf = this;
let form = mySelf.props.form;
// let options = mySelf.state.options;
let now = moment();
const yearOfNow = now.year();
const monthOfNow = now.month() + 1;
// console.log(options);
let defaultYearOption = {key: yearOfNow, label: yearOfNow};
let defaultMonthOption = {key: monthOfNow, label: monthOfNow};
// console.log(defaultOption)
let formData = {
year: defaultYearOption,
month: defaultMonthOption,
};
form.setFieldsValue(formData);
};
changeMonth = (pars) => {
let mySelf = this;
let form = mySelf.props.form;
let res = form.getFieldsValue();
let month = Number(res.month.key) + Number(pars);
let monthOption = {key: month, label: month};
// console.log(monthOption);
let formData = {
month: monthOption,
};
form.setFieldsValue(formData);
mySelf.props.changeCalendar();
};
render() {
let mySelf = this;
// eslint-disable-next-line
let { getFieldDecorator, getFieldValue } = mySelf.props.form;
let { options } = mySelf.state;
// console.log(options);
return (
<div> <Row className="margin-bottom-5"> <Col span={24}> <Form layout="inline" // wrappedComponentRef={(inst) => this.queryForm = inst} > <FormItem label="年" > {getFieldDecorator('year', { })( <Select labelInValue size={'default'} placeholder="year" style={{ width: 150 }} onChange={ () => setTimeout(() => { mySelf.props.changeCalendar() },0) } > {options.years.map(function(item,index){ return <Option key={item.key}>{item.label}</Option>; })} </Select> )} </FormItem> <FormItem label="月" > {getFieldDecorator('month', { })( <Select labelInValue size={'default'} placeholder="month" style={{ width: 150 }} onChange={ () => setTimeout(() => { mySelf.props.changeCalendar() },0) } > {options.months.map(function(item,index){ return <Option key={item.key}>{item.label}</Option>; })} </Select> )} </FormItem> <FormItem> <Button type="primary" size="small" onClick={() => mySelf.changeMonth(-1)}>pre</Button> <Button type="primary" size="small" onClick={() => mySelf.changeMonth(1)} className="margin-left-5">next</Button> </FormItem> </Form> </Col> </Row> </div>
);
}
};
let QueryFormComponent = Form.create()(queryForm);
class LeCalendar extends Component {
constructor(props) {
super(props);
this.state = {
//渲染日曆過程當中,當前顯示月的moment對象
monthDisplay: "",
dates: [
[],
[],
],
};
};
componentDidMount = () => {
let mySelf = this;
mySelf.init();
// mySelf.generateDates();
};
init = () => {
let mySelf = this;
mySelf.changeCalendar();
};
generateDates = () => {
let mySelf = this;
let dates = [];
//單個日曆,顯示6周
const weeksNum = 6;
//每週顯示7天
const weekDaysNum = 7;
//日曆中的今天
const todayDate = moment().format(DATE_FORMAT);
//渲染日曆過程當中,當前顯示月的moment對象
let now = mySelf.state.monthDisplay;
//當前顯示的月份
let nowMonth = now.month();
// console.log(nowMonth);
//當月第一天
let firstDayOfMonth = now.clone().startOf('month');
//當月第一天是星期幾
let weekOfFirstDay = firstDayOfMonth.isoWeekday();
//日曆上的當月顯示的第一天
let firstDayOfMonthDisplay = firstDayOfMonth.clone().subtract(weekOfFirstDay - 1, 'days');
//渲染日曆過程當中,使用的當前moment對象
let currentDate = firstDayOfMonthDisplay.clone();
// console.log(firstDayOfMonthDisplay.format(DATE_FORMAT));
const otherMonthClass = "other-month";
const dayOffClass = "day-off";
const holidayClass = "holiday";
const todayClass = "today";
for (let i=0; i<weeksNum; i++){
let weekList = [];
for (let j=0; j<weekDaysNum; j++){
let date = currentDate.format(DATE_FORMAT);
let dateDisplay = currentDate.date();
let currentMonth = currentDate.month();
let currentWeekDay = currentDate.isoWeekday();
let classNameList = [];
let classNameStr = "";
let content = "";
// console.log(currentMonth);
// console.log(currentDate.isoWeekday());
//今天
if (todayDate === date){
classNameList.push(todayClass);
}
//非當前月
if (currentMonth !== nowMonth){
classNameList.push(otherMonthClass);
}
//週末
if ([6, 7].includes(currentWeekDay)){
classNameList.push(dayOffClass);
}
//節假日
if (holidayMap[date]){
classNameList.push(holidayClass);
content = holidayMap[date];
}
classNameStr = classNameList.join(" ");
let item = {
momentObj: currentDate,
date: date,
dateDisplay: dateDisplay,
classNameStr: classNameStr,
content: content
};
weekList.push(item);
currentDate.add(1, 'days');
}
dates.push(weekList);
}
mySelf.setState({
dates
});
// console.log(dates);
};
changeCalendar = () => {
let mySelf = this;
let form = mySelf.queryForm.props.form;
let res = form.getFieldsValue();
let year = Number(res.year.key);
let month = Number(res.month.key) -1;
let now = moment();
now.year(year).month(month);
mySelf.state.monthDisplay = now;
mySelf.generateDates();
// console.log(firstDayOfMonthDisplay.format(DATE_FORMAT));
console.log(now.format(DATE_FORMAT));
};
render() {
// let { } = this.props;
let mySelf = this;
let { dates } = mySelf.state;
let renderWeekDays = (days) => {
let dom = null;
// console.log(days);
dom = (
<React.Fragment>
{days.map((item,index) => {
let tdDom = (
<td key={index}>
<div className={item.classNameStr + ' day-cell'}>
<div className="header">{ item.date } {item.dateDisplay}</div>
<div>{ item.content }</div>
</div>
</td>
);
return tdDom;
})}
</React.Fragment>
);
return dom
};
let renderWeeks = (dates) => {
let dom = null;
// console.log(dates);
dom = (
<React.Fragment>
{dates.map((item,index) => {
let trDom = (
<tr key={index}>
{ renderWeekDays(item) }
</tr>
);
return trDom;
})}
</React.Fragment>
);
return dom
};
return (
<div>
<QueryFormComponent
wrappedComponentRef={(inst) => mySelf.queryForm = inst}
changeCalendar={() => mySelf.changeCalendar()}
/>
<table border="1" className="le-calendar-container">
<thead>
<tr>
<th>一</th>
<th>二</th>
<th>三</th>
<th>四</th>
<th>五</th>
<th>六</th>
<th>日</th>
</tr>
</thead>
<tbody>
{ renderWeeks(dates) }
</tbody>
<tfoot>
<tr>
<td>foot1</td>
<td>foot2</td>
</tr>
</tfoot>
</table>
</div>
);
}
}
export default LeCalendar;
複製代碼