您的位置: 首页 >日志>前端技术>详细内容

前端技术

WeUI时间选择器实现(年、月、日、时、分、秒)

来源:本站原创 发布时间:2024-10-09 10:43:20 浏览次数: 【字体:

最近做移动端页面时有这么一个需求,时间选择器需要精确到时、分、秒。但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的时间选择器就可以显示到时分秒了

02


修改后的时间选择器部分代码:

/**
 * 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年以内。

×

用户登录