/**
* Layui 数字输入组件
*
* @author iTanken
* @since 2019-03-29
* @version 2020-01-19:数字键盘纵向定位自适应
* @version 2020-04-02:添加功能按钮悬浮提示开关参数;添加悬浮提示内部键盘按钮样式;修复 number 类型输入框小数输入问题
*/
layui.define(['jquery'], function(exports) {
var $ = layui.$, baseClassName = 'layui-input-number', keyClassName = 'layui-keyboard-number',
style = [''].join('');
$('head link:last')[0] && $('head link:last').after(style) || $('head').append(style);
var numberInput = {
/** 默认配置选项 */
options: {
// 123:123键置顶, 789:789键置顶
topBtns: 123,
// 右侧功能按钮
rightBtns: true,
// 功能按钮提示
showTips: true,
// 监听键盘事件
listening: true,
// 批量配置默认小数精确度,-1 不处理精确度
defaultPrec: -1,
// 初始化回调,无参
initEnd: $.noop,
// 触发显示回调,参数为当前输入框和数字键盘的 jQuery 对象
showEnd: $.noop,
// 隐藏键盘回调,参数为当前输入框的 jQuery 对象
hideEnd: $.noop,
// z-index
zIndex: 19999999
},
/** 初始化 */
init: function(custom) {
var _this = this;
_this.options = $.extend(_this.options, custom);
$('.' + baseClassName).attr({
"readonly": "readonly"
}).on('focus', function(e) {
_this.showKeyboard(_this, $(this));
});
typeof _this.options.initEnd === 'function' && _this.options.initEnd();
},
/** 获取按键悬浮提示 */
getTips: function(tip) {
return this.options.showTips ? (' lay-tips="' + tip + '"') : '';
},
/** 显示数字键盘 */
showKeyboard: function(_this, $input) {
// 2020-04-02:修复小数输入问题,将 number 类型输入框一律设置为 text
$input.prop('type') === 'number' && $input.attr('type', 'text');
var $keyBoard = $input.next('.' + keyClassName);
if (!$keyBoard[0]) {
// 不存在,添加元素
var sizeXS = _this.options.rightBtns ? 'xs3' : 'xs4',
sizeZero = _this.options.rightBtns ? 'xs6' : 'xs4',
// 按钮 123
btn123 = [
'
',
'',
''
].join(''),
// 按钮 789
btn789 = [
'',
'',
''
].join(''),
/* 退格键 */
backspace = [
'',
'
',
'
',
'Backspace'), '>',
'
',
'
',
'
'
].join(''),
/* 增加键 */
add = [
''
].join(''),
/* 减小键 */
reduce = [
''
].join(''),
/* 清空键 */
reset = [
'',
'
',
'
',
'Delete'), '>',
'
',
'
',
'
'
].join('');
$input.after(['',
'
',
_this.options.topBtns == 789 ? btn789 : btn123,
_this.options.rightBtns ? backspace : '',
'
',
'
',
'
',
_this.options.rightBtns ? add : '',
_this.options.topBtns == 789 ? btn123 : btn789,
_this.options.rightBtns ? reduce : '',
_this.options.rightBtns ? '' : backspace,
'
',
'
',
_this.options.rightBtns ? reset : '',
'
',
'
'].join(''));
$keyBoard = $input.next('.' + keyClassName);
$keyBoard.on('touchstart click', '.layui-key-btn', function(e) {
_this.setValue(_this, $input, $(this));
layui.stope(e);
return false;
});
$keyBoard.on('blur', function(e) {
_this.setValueRange(_this, $input, _this.toFixedPrec(_this, $input));
$keyBoard.remove(); // $keyBoard.hide();
typeof _this.options.hideEnd === 'function' && _this.options.hideEnd($input);
});
_this.options.listening && _this.initKeyListening(_this, $input, $keyBoard);
}
_this.display(_this, $input, $keyBoard);
},
/** 设置数字键盘样式并显示 */
display: function(_this, $input, $keyBoard) {
var showTop = $input[0].offsetTop + $input[0].offsetHeight + 4, boardHeight = $keyBoard.height(),
$win = $(window), topOffset = $keyBoard.offset().top + $keyBoard.outerHeight() + 4 - $win.scrollTop();
// 数字键盘纵向定位自适应
if (topOffset + boardHeight > $win.height() && topOffset >= boardHeight) {
showTop = $input[0].offsetTop - boardHeight - 5;
}
$keyBoard.css({
'top': showTop + 'px',
'left': $input[0].offsetLeft + 'px',
'z-index': _this.options.zIndex
});
$keyBoard.show(200, function() {
typeof _this.options.showEnd === 'function' && _this.options.showEnd($input, $keyBoard);
}).focus();
},
/** 初始化键盘监听事件 */
initKeyListening: function(_this, $input, $keyBoard) {
var $key, code;
$keyBoard.on('keydown', function(e) {
code = e.keyCode;
var inputNumber = parseInt($input.val(), 10) || 0;
if (code === 107 || e.shiftKey && code === 187) {
// 加号切换正数
inputNumber < 0 && _this.setValueRange(_this, $input, Math.abs(inputNumber));
} else if (code === 109 || e.shiftKey && code === 189) {
// 减号切换负数
inputNumber > 0 && _this.setValueRange(_this, $input, '-' + inputNumber);
} else {
// 监听数字键盘,退格键(Backspace)/重置键(Delete)
$key = $keyBoard.find('.layui-key-btn[data-keycode~=' + code + ']');
if ($key[0]) {
$key.trigger('click').css("background-color", "#f2f2f2");
$keyBoard.off('keyup').on('keyup', function(e) {
$('.layui-key-btn[data-keycode]').css("background-color", "#ffffff");
});
}
if (code > 36 && code < 41) {
// 上下左右键,防止触发混动条滑动事件
return false;
}
}
return true;
});
},
/** 处理精确度 */
toFixedPrec: function(_this, $input, val1, val2) {
var m, s, rs, prec = $.trim($input.data('prec'));
// 2020-04-02:修复获取小数精确度配置值问题
prec = parseInt(prec === '' || isNaN(prec) ? _this.options.defaultPrec : prec, 10);
val1 = val1 === undefined ? $input.val() : val1;
val1 = val1 == '' ? ($input.attr('min') || 0) : val1;
rs = val1.toString().split('.')[1];
if (prec < 0) {
prec = rs && rs.length || prec;
}
val2 = val2 || 0;
rs = val2.toString().split('.')[1];
prec = Math.max(prec, rs ? rs.length : 0);
m = Math.pow(10, prec);
s = ((val1 * m + val2 * m).toFixed(0) / m).toString();
rs = s.indexOf('.');
if (rs < 0 && prec > 0) {
rs = s.length;
s += '.';
}
while (s.length <= rs + prec) {
s += '0';
}
return s;
},
/** 设置值范围 */
setValueRange: function(_this, $input, value) {
var minVal = $input.attr('min') || Math.pow(-2, 63),
maxVal = $input.attr('max') || Math.pow(2, 63) - 1;
minVal = typeof minVal === 'string' && minVal.indexOf('.') > -1 ? parseFloat(minVal) : parseInt(minVal, 10);
maxVal = typeof maxVal === 'string' && maxVal.indexOf('.') > -1 ? parseFloat(maxVal) : parseInt(maxVal, 10);
if (value < minVal) {
value = _this.toFixedPrec(_this, $input, minVal);
_this.tips($input, '最小值为 ' + minVal + '!');
}
if (value > maxVal) {
value = _this.toFixedPrec(_this, $input, maxVal);
_this.tips($input, '最大值为 ' + maxVal + '!');
}
value = value < minVal ? minVal : (value > maxVal ? maxVal : value);
$input.val(value);
},
/** 设置输入框值 */
setValue: function(_this, $input, $key) {
var inputVal = $.trim($input.val()), keyVal = $.trim($key.text()), changeVal,
prec = $.trim($input.data('prec')), isDecimal = inputVal.indexOf('.') > -1;
// 2020-04-02:修复获取小数精确度配置值问题
prec = parseInt(prec === '' || isNaN(prec) ? _this.options.defaultPrec : prec, 10);
if ($.inArray(keyVal, ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.']) > -1) {
if (keyVal === '.') {
if (inputVal === '' || isDecimal) {
return;
}
if (prec === 0) {
_this.tips($input, '当前字段不允许输入小数!');
return;
}
}
if (keyVal === '0' && inputVal.indexOf('0') === 0 && !isDecimal) {
return;
}
if (inputVal.indexOf('.') > -1 && inputVal.split('.')[1].length >= prec && prec > 0) {
_this.tips($input, '精确度为保留小数点后 ' + prec + ' 位!');
return;
}
changeVal = inputVal = (keyVal !== '.' && inputVal === '0' ? '' : inputVal) + keyVal;
$input.val(inputVal);
} else {
changeVal = inputVal === '' ? 0 : inputVal,
step = $input.attr('step');
if (isDecimal) {
step = parseFloat(step) || 0.1;
changeVal = parseFloat(changeVal);
} else {
step = parseInt(step, 10) || 1;
changeVal = parseInt(changeVal, 10);
}
// right function buttons
switch($key.data('keycode')) {
case '38 39':
// ↑、→ 键增加
changeVal = _this.toFixedPrec(_this, $input, changeVal, step);
break;
case '37 40':
// ↓、← 键减小
changeVal = _this.toFixedPrec(_this, $input, changeVal, -step);
break;
case 8:
// Backspace 键退格
var valLength = inputVal.length;
valLength && $input.val(inputVal.substring(0, valLength - 1));
return;
case 46:
// Delete 键清空
$input.val('');
return;
}
}
$input.val(changeVal);
},
/** 提示 */
tips: function($input, msg) {
return layer.tips(msg, $input, { tips: [1, '#01AAED'], time: 2e3, anim: 6, zIndex: this.options.zIndex });
}
};
exports('numinput', numberInput);
});