日期选择组件的设计
日期选择组件是一个很常见、又挺复杂的组件,很值得好好研究一番。
一、日期的组成
2017-09-28 22:08:06
年、月、日、时、分、秒
二、日期的格式
三、基本日期选择组件的构成
主要由两部分构成:
日历表格(核心)
头部(切换月份和年份)
四、日历模式
日模式(基本模式)
月模式
年模式
世纪模式
五、衍生日期选择组件
选择日期区间
六、交互
基本日期选择组件
日历表格:选中某个日期、当前月份改变
头部:上一月/下一月/上一年/下一年、切换日历模式
日期区间选择组件
- 开始时间和结束时间的选择在一个流程中,两次选择为一个流程,第一次选的是开始时间还是结束时间,取决于第二次选择的时间比其早还是晚。第二次选择完则时间区间确定,选择流程结束。继续选择进入下一个流程。
- 左侧的日历表格选开始时间,右侧的表格选结束时间,可反复选择,选好点击确定按钮结束选择流程,确定时间区间。
七、日期选择限制
总体可选范围限制
只能选最近一年的数据(包括今天):[-365, 0]
只能选未来一个月的数据(包括今天):[0, 30]
可选时间段限制
只能选三个月的数据:90
日期区间选择组件中选一天(小时数据)的范围限制
只能选最近三个月(包括今天)的小时数据:[-90, 0]
开始日期和结束日期限制的规则
开始时间小于或等于结束时间
八、日期选择组件的实现
日历表格的展示(核心模块)
- DateTable
- DateTHead
- DateTBody
获取日历数组的算法(核心算法):
输入:一个具体的日期
输出:6X7的二维日历数组(月模式)
例如:
输入:2017-09-28
输出:
[
[28, 29, 30, 31, 1, 2, 3],
[4, 5, 6, 7, 8, 9, 10],
[11, 12, 13, 14, 15, 16, 17],
[18, 19, 20, 21, 22, 23, 24],
[25, 26, 27, 28, 29, 30, 1],
[2, 3, 4, 5, 6, 7, 8]
]
在实际的实现中,数组的值一般是Date格式或者Moment格式(Moment.js库:http://momentjs.com/),方便取各种格式的日期。
具体实现如下:
import moment from 'moment';
const DATE_ROW_COUNT = 6;
/**
* 获取某一周的周数组
* @param {某个日期} date
* @param {第几周} num
* @param {数组的值的格式} format
*/
export function getWeekArr(date, num, format) {
let weekArr = [];
let dateMoment = date;
if(!moment.isMoment(date)){
dateMoment = moment(date);
}
let index = dateMoment.format('d');
let firstDay = -index+num*7;
let lastDay = 7-index+num*7;
for(let day=firstDay;day<lastDay;day++){
let weekItem = moment(dateMoment).add(day, 'days');
let formatWeekItem = weekItem;
if(format){
formatWeekItem = weekItem.format(format);
}
weekArr.push(formatWeekItem);
}
return weekArr;
}
/**
* 获取一个月的第一天
* @param {某个日期} date
*/
export function getFirstDayOfMonth(date, monthNum, yearNum) {
let dateMoment = date;
if(!moment.isMoment(date)){
dateMoment = moment(date);
}
let year = moment(dateMoment).add(yearNum, 'years').add(monthNum, 'months').format('YYYY');
let month = moment(dateMoment).add(yearNum, 'years').add(monthNum, 'months').format('MM');
// let month = dateMoment.format('MM');
let day = '01';
let firstDayOfMonth = year+'-'+month+'-'+day;
return firstDayOfMonth;
}
/**
* 获取日历数组
* @param {某个日期} date
*/
export function getCalendarArr(date, monthNum, yearNum) {
let calendarArr = [];
monthNum = monthNum || 0;
yearNum = yearNum || 0;
let firstDayOfMonth = getFirstDayOfMonth(date, monthNum, yearNum);
for(let row=0;row<DATE_ROW_COUNT;row++){
let rowArr = getWeekArr(firstDayOfMonth, row);
calendarArr.push(rowArr);
}
return calendarArr;
}
还有头部操作区、日期的选择、月份/年份的加减、日历模式的切换等模块的实现,可以参考另一篇文章: