WeUI时间选择器实现(年、月、日、时、分、秒)
最近做移动端页面时有这么一个需求,时间选择器需要精确到时、分、秒。但WeUI的时间选择器只能支持年、月、日。
研究了下WeUI的时间选择器部分的代码,其实 datePicker() 时间选择器,是由 picker() 拓展而来,在控件开头的注释就已经提到了。picker() 的数据格式其实就是每个选择项的 children() 属性下加入下一级选项的数据,子选项的children属性继续加入下一级选项的数据,这样一层一层的嵌套下去。
WeUI时间选择器相关代码:
/**
* datePicker 时间选择器,由picker拓展而来,提供年、月、日的选择。
* @param options 配置项
* @param {string=} [options.id=datePicker] 作为picker的唯一标识
* @param {number=|string|Date} [options.start=2000] 起始年份,如果是 `Number` 类型,表示起始年份;如果是 `String` 类型,格式为 'YYYY-MM-DD';如果是 `Date` 类型,就传一个 Date
* @param {number=|string|Date} [options.end=2030] 结束年份,同上
* @param {string=} [options.cron=* * *] cron 表达式,三位,分别是 dayOfMonth[1-31],month[1-12] 和 dayOfWeek[0-6](周日-周六)
* @param {string=} [options.className] 自定义类名
* @param {array=} [options.defaultValue] 默认选项的value数组, 如 [1991, 6, 9]
* @param {function=} [options.onChange] 在picker选中的值发生变化的时候回调
* @param {function=} [options.onConfirm] 在点击"确定"之后的回调。回调返回选中的结果(Array),数组长度依赖于picker的层级。
*
*@example
* // 示例1:
* weui.datePicker({
* start: 1990,
* end: 2000,
* defaultValue: [1991, 6, 9],
* onChange: function(result){
* console.log(result);
* },
* onConfirm: function(result){
* console.log(result);
* },
* id: 'datePicker'
* });
*
* // 示例2:
* weui.datePicker({
* start: new Date(), // 从今天开始
* end: 2030,
* defaultValue: [2020, 6, 9],
* onChange: function(result){
* console.log(result);
* },
* onConfirm: function(result){
* console.log(result);
* },
* id: 'datePicker'
* });
*
* // 示例3:
* weui.datePicker({
* start: new Date(), // 从今天开始
* end: 2030,
* cron: '* * 0,6', // 每逢周日、周六
* onChange: function(result){
* console.log(result);
* },
* onConfirm: function(result){
* console.log(result);
* },
* id: 'datePicker'
* });
*
* // 示例4:
* weui.datePicker({
* start: new Date(), // 从今天开始
* end: 2030,
* cron: '1-10 * *', // 每月1日-10日
* onChange: function(result){
* console.log(result);
* },
* onConfirm: function(result){
* console.log(result);
* },
* id: 'datePicker'
* });
*/
function datePicker(options) {
var nowDate = new Date();
var defaults = _util2.default.extend({
id: 'datePicker',
onChange: _util2.default.noop,
onConfirm: _util2.default.noop,
start: nowDate.getFullYear() - 20,
end: nowDate.getFullYear() + 20,
defaultValue: [nowDate.getFullYear(), nowDate.getMonth() + 1, nowDate.getDate()],
cron: '* * *',
depth: 3
}, options);
// 约束depth
if (defaults.depth > 3) {
console.warn('max depth is 3, but you passed depth = ' + defaults.depth + ', set depth = 3');
defaults.depth = 3;
}
if (defaults.depth < 1) {
console.warn('min depth is 1, but you passed depth = ' + defaults.depth + ', set depth = 1');
defaults.depth = 1;
}
// 兼容原来的 start、end 传 Number 的用法
if (typeof defaults.start === 'number') {
defaults.start = new Date(defaults.start + '/01/01');
} else if (typeof defaults.start === 'string') {
defaults.start = new Date(defaults.start.replace(/-/g, '/'));
}
if (typeof defaults.end === 'number') {
defaults.end = new Date(defaults.end + '/12/31');
} else if (typeof defaults.end === 'string') {
defaults.end = new Date(defaults.end.replace(/-/g, '/'));
}
var findBy = function findBy(array, key, value) {
for (var i = 0, len = array.length; i < len; i++) {
var _obj = array[i];
if (_obj[key] == value) {
return _obj;
}
}
};
var date = [];
var interval = _cron2.default.parse(defaults.cron, defaults.start, defaults.end);
var obj = void 0;
do {
obj = interval.next();
var year = obj.value.getFullYear();
var month = obj.value.getMonth() + 1;
var day = obj.value.getDate();
var Y = findBy(date, 'value', year);
if (!Y) {
Y = {
label: year + '年',
value: year,
children: []
};
date.push(Y);
}
// 如果深度是大于1,加入月份
if (defaults.depth > 1) {
var M = findBy(Y.children, 'value', month);
if (!M) {
M = {
label: month + '月',
value: month,
children: []
};
Y.children.push(M);
}
// 如果深度大于2,加入日期
if (defaults.depth > 2) {
M.children.push({
label: day + '日',
value: day
});
}
}
} while (!obj.done);
return picker(date, defaults);
}
exports.default = {
picker: picker,
datePicker: datePicker
};
module.exports = exports['default'];要改进这个时间选择器,可以显示时、分、秒,其实也很简单。我们只需要在日的选项下加入一个children,把时、分、秒加进去。由于每天24小时、每小时60分、每分钟60秒这些都是固定的,我们就不需要在他那个 do{...}while(...) 循环里进行建立时分秒的数组了,这样会很耗浏览器资源,我们直接在循环的外面定义好时分秒的数据,然后在每日的children里面插入这个数组就可以了。
由于 picker() 不支持多列和级联混用,时分秒的数组也只能用级联的方式来添加。
建立时分秒数组的代码如下:
// 加入时分秒
var dateHours = [],dateMinutes = [],dateSeconds = [];
// 如果深度大于5,加入秒
if(defaults.depth > 5){
if(!(defaults.defaultValue[5] > 0 && defaults.defaultValue[5] < 60) ){
defaults.defaultValue[5] = 0;
}
for (var i = 0 ; i < 60; i++){
dateSeconds.push({
label: i + '秒',
value: i
})
};
};
// 如果深度大于4,加入分
if(defaults.depth > 4){
if(!(defaults.defaultValue[4] > 0 && defaults.defaultValue[4] < 60) ){
defaults.defaultValue[4] = 0;
}
for (var i = 0 ; i < 60; i++){
dateMinutes.push({
label: i + '分',
value: i,
children: dateSeconds
})
};
};
// 如果深度大于3,加入时
if(defaults.depth > 3){
if(!(defaults.defaultValue[3] > 0 && defaults.defaultValue[3] < 24) ){
defaults.defaultValue[3] = 0;
}
for (var i = 0 ; i < 24; i++){
dateHours.push({
label: i + '时',
value: i,
children: dateMinutes
})
};
};然后把 dateHours 加入到每日的children下
// 如果深度大于2,加入日期
if (defaults.depth > 2) {
M.children.push({
label: day + '日',
value: day,
children: dateHours
});
}虽然加上了时分秒的选项,但我们发现时间选择器调用时还是只有年月日,那是因为有个depth深度的约束。这里默认是3层,所以就只有年月日。我们还需要把时间默认值、默认显示层级、约束depth进行修改
默认时间:
defaultValue: [nowDate.getFullYear(), nowDate.getMonth() + 1, nowDate.getDate()], // 改为 defaultValue: [nowDate.getFullYear(), nowDate.getMonth() + 1, nowDate.getDate(), nowDate.getHours(), nowDate.getMinutes(), nowDate.getSeconds()],
默认层级:
depth: 3 // 改为 depth: 6
约束depth:
// 约束depth
if (defaults.depth > 3) {
console.warn('max depth is 3, but you passed depth = ' + defaults.depth + ', set depth = 3');
defaults.depth = 3;
}
// 改为
if (defaults.depth > 6) {
console.warn('max depth is 6, but you passed depth = ' + defaults.depth + ', set depth = 6');
defaults.depth = 6;
}这样WeUI的时间选择器就可以显示到时分秒了
修改后的时间选择器部分代码:
/**
* datePicker 时间选择器,由picker拓展而来,提供年、月、日的选择。
* @param options 配置项
* @param {string=} [options.id=datePicker] 作为picker的唯一标识
* @param {number=|string|Date} [options.start=2000] 起始年份,如果是 `Number` 类型,表示起始年份;如果是 `String` 类型,格式为 'YYYY-MM-DD';如果是 `Date` 类型,就传一个 Date
* @param {number=|string|Date} [options.end=2030] 结束年份,同上
* @param {string=} [options.cron=* * *] cron 表达式,三位,分别是 dayOfMonth[1-31],month[1-12] 和 dayOfWeek[0-6](周日-周六)
* @param {string=} [options.className] 自定义类名
* @param {array=} [options.defaultValue] 默认选项的value数组, 如 [1991, 6, 9]
* @param {function=} [options.onChange] 在picker选中的值发生变化的时候回调
* @param {function=} [options.onConfirm] 在点击"确定"之后的回调。回调返回选中的结果(Array),数组长度依赖于picker的层级。
*
*@example
* // 示例1:
* weui.datePicker({
* start: 1990,
* end: 2000,
* defaultValue: [1991, 6, 9],
* onChange: function(result){
* console.log(result);
* },
* onConfirm: function(result){
* console.log(result);
* },
* id: 'datePicker'
* });
*
* // 示例2:
* weui.datePicker({
* start: new Date(), // 从今天开始
* end: 2030,
* defaultValue: [2020, 6, 9],
* onChange: function(result){
* console.log(result);
* },
* onConfirm: function(result){
* console.log(result);
* },
* id: 'datePicker'
* });
*
* // 示例3:
* weui.datePicker({
* start: new Date(), // 从今天开始
* end: 2030,
* cron: '* * 0,6', // 每逢周日、周六
* onChange: function(result){
* console.log(result);
* },
* onConfirm: function(result){
* console.log(result);
* },
* id: 'datePicker'
* });
*
* // 示例4:
* weui.datePicker({
* start: new Date(), // 从今天开始
* end: 2030,
* cron: '1-10 * *', // 每月1日-10日
* onChange: function(result){
* console.log(result);
* },
* onConfirm: function(result){
* console.log(result);
* },
* id: 'datePicker'
* });
*/
function datePicker(options) {
var nowDate = new Date();
var defaults = _util2.default.extend({
id: 'datePicker',
onChange: _util2.default.noop,
onConfirm: _util2.default.noop,
start: nowDate.getFullYear() - 20,
end: nowDate.getFullYear() + 20,
defaultValue: [nowDate.getFullYear(), nowDate.getMonth() + 1, nowDate.getDate(), nowDate.getHours(), nowDate.getMinutes(), nowDate.getSeconds()],
cron: '* * *',
depth: 6
}, options);
// 约束depth
if (defaults.depth > 6) {
console.warn('max depth is 6, but you passed depth = ' + defaults.depth + ', set depth = 6');
defaults.depth = 6;
}
if (defaults.depth < 1) {
console.warn('min depth is 1, but you passed depth = ' + defaults.depth + ', set depth = 1');
defaults.depth = 1;
}
// 兼容原来的 start、end 传 Number 的用法
if (typeof defaults.start === 'number') {
defaults.start = new Date(defaults.start + '/01/01');
} else if (typeof defaults.start === 'string') {
defaults.start = new Date(defaults.start.replace(/-/g, '/'));
}
if (typeof defaults.end === 'number') {
defaults.end = new Date(defaults.end + '/12/31');
} else if (typeof defaults.end === 'string') {
defaults.end = new Date(defaults.end.replace(/-/g, '/'));
}
var findBy = function findBy(array, key, value) {
for (var i = 0, len = array.length; i < len; i++) {
var _obj = array[i];
if (_obj[key] == value) {
return _obj;
}
}
};
var date = [];
var interval = _cron2.default.parse(defaults.cron, defaults.start, defaults.end);
var obj = void 0;
// 加入时分秒
var dateHours = [],dateMinutes = [],dateSeconds = [];
// 如果深度大于5,加入秒
if(defaults.depth > 5){
if(!(defaults.defaultValue[5] > 0 && defaults.defaultValue[5] < 60) ){
defaults.defaultValue[5] = 0;
}
for (var i = 0 ; i < 60; i++){
dateSeconds.push({
label: i + '秒',
value: i
})
};
};
// 如果深度大于4,加入分
if(defaults.depth > 4){
if(!(defaults.defaultValue[4] > 0 && defaults.defaultValue[4] < 60) ){
defaults.defaultValue[4] = 0;
}
for (var i = 0 ; i < 60; i++){
dateMinutes.push({
label: i + '分',
value: i,
children: dateSeconds
})
};
};
// 如果深度大于3,加入时
if(defaults.depth > 3){
if(!(defaults.defaultValue[3] > 0 && defaults.defaultValue[3] < 24) ){
defaults.defaultValue[3] = 0;
}
for (var i = 0 ; i < 24; i++){
dateHours.push({
label: i + '时',
value: i,
children: dateMinutes
})
};
};
do {
obj = interval.next();
var year = obj.value.getFullYear();
var month = obj.value.getMonth() + 1;
var day = obj.value.getDate();
var Y = findBy(date, 'value', year);
if (!Y) {
Y = {
label: year + '年',
value: year,
children: []
};
date.push(Y);
}
// 如果深度是大于1,加入月份
if (defaults.depth > 1) {
var M = findBy(Y.children, 'value', month);
if (!M) {
M = {
label: month + '月',
value: month,
children: []
};
Y.children.push(M);
}
// 如果深度大于2,加入日期
if (defaults.depth > 2) {
M.children.push({
label: day + '日',
value: day,
children: dateHours
});
}
}
} while (!obj.done);
return picker(date, defaults);
}
exports.default = {
picker: picker,
datePicker: datePicker
};
module.exports = exports['default'];注意:由于 picker() 方法不支持多列级联同时使用,时分秒只能通过级联方式添加到每日的 children 里,年份跨度太大会用性能问题,要严格控制年份的跨度,尽量控制在100年以内。
