Merge branch 'main' into 2.10-dev

This commit is contained in:
贤心 2025-03-10 14:15:12 +08:00 committed by GitHub
commit c112285cb3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 173 additions and 39 deletions

View File

@ -1,6 +1,10 @@
name: 😄 创建议题
description: 此处只受理 Bug 报告、功能请求。若是其他业务相关的问题建议在 Discussions 寻求社区帮助。
body:
- type: markdown
attributes:
value: |
> 💡 请遵循标题格式:**[组件名称] 描述问题的标题** 例如:[table] 表格能否支持自定义请求
- type: checkboxes
attributes:
label: 议题条件

2
dist/css/layui.css vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
dist/layui.js vendored

File diff suppressed because one or more lines are too long

2
dist/layui.js.map vendored

File diff suppressed because one or more lines are too long

View File

@ -12,7 +12,7 @@ toc: true
| className | 描述 |
| --- | --- |
| layui-main | 设置一个固定宽度为 `1160px` 的水平居中块 |
| layui-border-box | 设置元素及其所有子元素均为 `box-sizing: content-box` 模型的容器 |
| layui-border-box | 设置元素及其所有子元素均为 `box-sizing: border-box` 模型的容器 |
| layui-clear | 清除前面的同级元素产生的浮动 |
| layui-clear-space <sup>2.8+</sup> | 清除容器内的空白符 |
| layui-inline | 设置元素为内联块状结构 |

View File

@ -114,7 +114,8 @@ toc: true
数字输入框 <sup>2.8.9+</sup>
</h3>
一般搭配 `<input type="number">` 使用,用于替代原生数字输入框,支持的属性如下:
一般搭配 `<input type="text">` 使用,用于替代原生数字输入框,支持的属性如下:
注:<sup>2.10+</sup> 之前的版本,使用 `type="number"` 类型的输入框。
| 属性 | 描述 |
| --- | --- |
@ -122,6 +123,8 @@ toc: true
| min | 设置数字的最小值 |
| max | 设置数字的最大值 |
| lay-precision <sup>2.8.18+</sup> | 设置数字的小数位精度。注<sup>2.9.8+</sup>:若值为 `0`,则表示取整。 |
| lay-step-strictly <sup>2.10+</sup> | 步长严格模式,只能输入步长的倍数 |
| lay-wheel <sup>2.10+</sup> | 是否启用滚轮或触摸板事件处理 |
### 示例
@ -134,19 +137,22 @@ toc: true
<hr class="ws-space-16">
<div class="layui-row layui-col-space16">
<div class="layui-col-xs6">
<input type="number" lay-affix="number" placeholder="设置 step 为 0.01" step="0.01" class="layui-input">
<input type="text" lay-affix="number" placeholder="设置 step 为 0.01" step="0.01" class="layui-input">
</div>
<div class="layui-col-xs6">
<input type="number" lay-affix="number" placeholder="设置 step,min,max" step="10" min="0" max="100" class="layui-input">
<input type="text" lay-affix="number" placeholder="设置 step,min,max" step="10" min="0" max="100" class="layui-input">
</div>
<div class="layui-col-xs6">
<input type="text" lay-affix="number" placeholder="步长严格模式" lay-step-strictly step="10" min="0" max="100" class="layui-input">
</div>
<div class="layui-col-xs4">
<input type="number" lay-affix="number" placeholder="设置小数位精度为 2" step="0.1" lay-precision="2" class="layui-input">
<input type="text" lay-affix="number" placeholder="设置小数位精度为 2" step="0.1" lay-precision="2" class="layui-input">
</div>
<div class="layui-col-xs4">
<input type="number" lay-affix="number" readonly placeholder="不允许输入状态" class="layui-input">
<input type="text" lay-affix="number" readonly placeholder="不允许输入状态" class="layui-input">
</div>
<div class="layui-col-xs4">
<input type="number" lay-affix="number" disabled placeholder="禁用状态" class="layui-input">
<input type="text" lay-affix="number" disabled placeholder="禁用状态" class="layui-input">
</div>
</div>
</div>

View File

@ -9,10 +9,27 @@ toc: true
<h2 id="2.9.x" lay-toc="{title: '2.9.x'}"></h2>
<h2 id="v2.9.24" class="ws-anchor">
v2.9.24
<span class="layui-badge-rim">2025-03-07</span>
<span class="layui-badge-rim" style="color: #16b777;">稳定版</span>
</h2>
- #### layer
- 改进 打开弹层后设置页面滚动条逻辑 #2537 @Sight-wcg
- #### treeTable
- 修复 treeToFlat 改变根节点 pid 问题 #2526 @Sight-wcg
- 修复 checkbox 重复更新的问题,大约可提升 30% 性能 #2528 @Sight-wcg
- #### code
- 修复 选项卡底边框显示异常问题 #2519 @Sight-wcg
### 下载: [layui-v2.9.24.zip](https://gitee.com/layui/layui/attach_files/2085224/download)
---
<h2 id="v2.9.23" class="ws-anchor">
v2.9.23
<span class="layui-badge-rim">2025-02-19</span>
<span class="layui-badge-rim" style="color: #16b777;">稳定版</span>
</h2>
- #### 基础

View File

@ -1,6 +1,6 @@
{
"name": "layui",
"version": "2.9.23",
"version": "2.9.24",
"description": "Classic modular Front-End UI library",
"keywords": [
"layui",

View File

@ -841,7 +841,8 @@ hr.layui-border-black{border-width: 0 0 1px;}
.layui-input-wrap .layui-input[type="number"]::-webkit-outer-spin-button,
.layui-input-wrap .layui-input[type="number"]::-webkit-inner-spin-button{-webkit-appearance: none !important;}
.layui-input-wrap .layui-input[type="number"]{-moz-appearance: textfield; -webkit-appearance: textfield; appearance: textfield;}
.layui-input-wrap .layui-input[type="number"].layui-input-number-out-of-range{color:#ff5722;}
.layui-input-wrap .layui-input.layui-input-number-out-of-range,
.layui-input-wrap .layui-input.layui-input-number-invalid{color:#ff5722;}

View File

@ -48,8 +48,7 @@ html #layuicss-skincodecss{display: none; position: absolute; width: 1989px;}
.layui-code-preview > .layui-code,
.layui-code-preview > .layui-code-view{margin: 0;}
.layui-code-preview > .layui-tab{position: relative; z-index: 1; margin-bottom: 0;}
.layui-code-preview > .layui-tab > .layui-tab-title{border-width: 0;}
.layui-code-preview .layui-code-item{display: none;}
.layui-code-preview .layui-code-item{display: none; border-top-width: 0;}
.layui-code-preview .layui-code-view > .layui-code-lines > .layui-code-line{}
.layui-code-item-preview{position: relative; padding: 16px;}
.layui-code-item-preview > iframe{position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: none;}
@ -62,7 +61,7 @@ html #layuicss-skincodecss{display: none; position: absolute; width: 1989px;}
/* 全屏风格 */
.layui-code-full{position: fixed; left: 0; top: 0; z-index: 1111111; width: 100%; height: 100%; background-color: #fff;}
.layui-code-full .layui-code-item{width: 100% !important; border-width: 0 !important; border-top-width: 1px !important;}
.layui-code-full .layui-code-item{width: 100% !important; border-width: 0 !important;}
.layui-code-full .layui-code-item,
.layui-code-full .layui-code-view,
.layui-code-full .layui-code-wrap{height: calc(100vh - 51px) !important; box-sizing: border-box;}

View File

@ -16,7 +16,7 @@
};
var Layui = function(){
this.v = '2.9.23'; // Layui 版本号
this.v = '2.9.24'; // Layui 版本号
};
// 识别预先可能定义的指定全局对象

View File

@ -19,6 +19,7 @@ layui.define(['lay', 'layer', 'util'], function(exports){
var HIDE = 'layui-hide';
var DISABLED = 'layui-disabled';
var OUT_OF_RANGE = 'layui-input-number-out-of-range';
var BAD_INPUT = 'layui-input-number-invalid';
var Form = function(){
this.config = {
@ -194,10 +195,15 @@ layui.define(['lay', 'layer', 'util'], function(exports){
var precision = Number(elem.attr('lay-precision'));
var noAction = eventType !== 'click' && rawValue === ''; // 初始渲染和失焦时空值不作处理
var isInit = eventType === 'init';
var isBadInput = isNaN(value);
var isStepStrictly = typeof elem.attr('lay-step-strictly') === 'string';
if(isNaN(value)) return; // 若非数字,则不作处理
elem.toggleClass(BAD_INPUT, isBadInput);
if(isBadInput) return; // 若非数字,则不作处理
if(eventType === 'click'){
// 兼容旧版行为2.10 以前 readonly 不禁用控制按钮
if(elem[0].type === 'text' && typeof elem.attr('readonly') === 'string') return;
var isDecrement = !!$(that).index() // 0: icon-up, 1: icon-down
value = isDecrement ? value - step : value + step;
}
@ -214,6 +220,9 @@ layui.define(['lay', 'layer', 'util'], function(exports){
if (!noAction) {
// 初始渲染时只处理数字精度
if (!isInit) {
if(isStepStrictly){
value = Math.round(value / step) * step;
}
if(value <= min) value = min;
if(value >= max) value = max;
}
@ -223,7 +232,9 @@ layui.define(['lay', 'layer', 'util'], function(exports){
} else if(precision > 0) { // 小数位精度
value = value.toFixed(precision);
}
elem.val(value);
elem.attr('lay-input-mirror', elem.val())
}
// 超出范围的样式
@ -369,6 +380,71 @@ layui.define(['lay', 'layer', 'util'], function(exports){
className: 'layui-input-number',
disabled: othis.is('[disabled]'), // 跟随输入框禁用状态
init: function(elem){
// 旧版浏览器不支持更改 input 元素的 type 属性,需要主动设置 text
if(elem.attr('type') === 'text' || elem[0].type === 'text'){
var ns = '.lay_input_number';
var skipCheck = false;
var isComposition = false;
var isReadonly = typeof elem.attr('readonly') === 'string';
var isMouseWheel = typeof elem.attr('lay-wheel') === 'string';
var btnElem = elem.next('.layui-input-number').children('i');
// 旧版浏览器不支持 beforeInput 事件,需要设置一个 attr 存储输入前的值
elem.attr('lay-input-mirror', elem.val());
elem.off(ns);
// 旧版浏览器不支持 event.inputType 属性,需要用 keydown 事件来判断是否跳过输入检查
elem.on('keydown' + ns, function (e) {
skipCheck = false;
if (e.keyCode === 8 || e.keyCode === 46) { // Backspace || Delete
skipCheck = true;
}
// Up & Down 键盘事件处理
if(!isReadonly && btnElem.length === 2 && (e.keyCode === 38 || e.keyCode === 40)){
e.preventDefault();
btnElem.eq(e.keyCode === 38 ? 0 : 1).click();
}
})
elem.on('input' + ns + ' propertychange' + ns, function (e) {
if (isComposition || (e.type === 'propertychange' && e.originalEvent.propertyName !== 'value')) return;
if (skipCheck || canInputNumber(this.value)) {
elem.attr('lay-input-mirror', this.value);
} else {
// 恢复输入前的值
this.value = elem.attr('lay-input-mirror');
}
elem.toggleClass(BAD_INPUT, isNaN(Number(this.value)));
});
elem.on('compositionstart' + ns, function () {
isComposition = true;
});
elem.on('compositionend' + ns, function () {
isComposition = false;
elem.trigger('input');
})
// 响应鼠标滚轮或触摸板
if(isMouseWheel){
elem.on(['wheel','mousewheel','DOMMouseScroll'].join(ns + ' ') + ns, function (e) {
if(!btnElem.length) return;
if(!$(this).is(':focus')) return;
var direction = 0;
e.preventDefault();
// IE9+chrome 和 firefox 同时添加 'wheel' 和 'mousewheel' 事件时,只执行 'wheel' 事件
if(e.type === 'wheel'){
e.deltaX = e.originalEvent.deltaX;
e.deltaY = e.originalEvent.deltaY;
direction = Math.abs(e.deltaX) >= Math.abs(e.deltaY) ? e.deltaX : e.deltaY;
}else if(e.type === 'mousewheel' ){
direction = -e.originalEvent.wheelDelta;
}else if(e.type === 'DOMMouseScroll'){
direction = e.originalEvent.detail;
}
btnElem.eq(direction > 0 ? 1 : 0).click();
})
}
if(isReadonly){
btnElem.addClass(DISABLED);
}
}
handleInputNumber.call(this, elem, 'init')
},
click: function(elem){
@ -1344,6 +1420,31 @@ layui.define(['lay', 'layer', 'util'], function(exports){
return true;
}
}
// 修改自 https://github.com/Tencent/tdesign-common/blob/53786c58752401e648cc45918f2a4dbb9e8cecfa/js/input-number/number.ts#L209
var specialCode = ['-', '.', 'e', 'E', '+'];
function canInputNumber(number) {
if (number === '') return true;
// 数字最前方不允许出现连续的两个 0
if (number.slice(0, 2) === '00') return false;
// 不能出现空格
if (number.match(/\s/g)) return false;
// 只能出现一个点(.
var tempMatched = number.match(/\./g);
if (tempMatched && tempMatched.length > 1) return false;
// 只能出现一个ee
tempMatched = number.match(/e/g);
if (tempMatched && tempMatched.length > 1) return false;
// 只能出现一个负号(-)或 一个正号(+),并且在第一个位置;但允许 3e+10 这种形式
var tempNumber = number.slice(1);
tempMatched = tempNumber.match(/(\+|-)/g);
if (tempMatched && (!/e(\+|-)/i.test(tempNumber) || tempMatched.length > 1)) return false;
// 允许输入数字字符
var isNumber = !isNaN(Number(number));
if (!isNumber && !(specialCode.indexOf(number.slice(-1)) !== -1)) return false;
if (/e/i.test(number) && (!/\de/i.test(number) || /e\./.test(number))) return false;
return true;
}
var form = new Form();
var $dom = $(document);

View File

@ -1021,16 +1021,20 @@ ready.record = function(layero){
// 设置页面滚动条
ready.setScrollbar = function(index){
doms.html.css('overflow', 'hidden').attr('layer-full', index);
doms.html.css('overflow', 'hidden');
};
// 恢复页面滚动条
ready.restScrollbar = function(index){
if(doms.html.attr('layer-full') == index){
doms.html[0].style[doms.html[0].style.removeProperty
? 'removeProperty'
: 'removeAttribute']('overflow');
doms.html.removeAttr('layer-full');
ready.restScrollbar = function(index) {
// 关闭和大小化, layer-full 处理
var targetEl = $('.'+ doms[0]).filter(function(){
var layero = $(this);
return layero.data('config').scrollbar === false
&& layero.data('maxminStatus') !== 'min'
&& layero.attr('times') !== String(index);
});
if(targetEl.length === 0){
doms.html.css('overflow', '');
}
};
@ -1230,9 +1234,7 @@ layer.full = function(index){
layero.data('maxminStatus', 'max');
ready.record(layero); // 记录当前尺寸、坐标
if(!doms.html.attr('layer-full')){
ready.setScrollbar(index);
}
ready.setScrollbar(index);
setTimeout(function(){
var isfix = layero.css('position') === 'fixed';

View File

@ -1746,6 +1746,9 @@ layui.define(['lay', 'laytpl', 'laypage', 'form', 'util'], function(exports){
var isCheckMult = layui.type(opts.index) === 'array'; // 是否操作多个
var isCheckAllOrMult = isCheckAll || isCheckMult; // 是否全选或多选
// treeTable 内部已处理选中,此处不再处理
if(options.tree && options.tree.view) return;
// 全选或多选时
if (isCheckAllOrMult) {
that.layBox.addClass(DISABLED_TRANSITION); // 减少回流
@ -1839,7 +1842,7 @@ layui.define(['lay', 'laytpl', 'laypage', 'form', 'util'], function(exports){
if(isCheckAllOrMult){
setTimeout(function(){
that.layBox.removeClass(DISABLED_TRANSITION);
},100)
}, 100)
}
};

View File

@ -387,7 +387,8 @@ layui.define(['table'], function (exports) {
layui.each(tableData, function (i1, item1) {
var dataIndex = (parentIndex ? parentIndex + '-' : '') + i1;
var dataNew = $.extend({}, item1);
dataNew[pIdKey] = item1[pIdKey] || parentId;
dataNew[pIdKey] = typeof item1[pIdKey] !== 'undefined' ? item1[pIdKey] : parentId;
flat.push(dataNew);
flat = flat.concat(that.treeToFlat(item1[childrenKey], item1[customName.id], dataIndex));
});
@ -1761,10 +1762,10 @@ layui.define(['table'], function (exports) {
that.setRowCheckedClass(checkboxElem.closest('tr'), checked);
// 设置原始复选框 checked 属性值并渲染
form.render(checkboxElem.prop({
checkboxElem.prop({
checked: checked,
indeterminate: itemP[LAY_CHECKBOX_HALF]
}))
})
})
}
@ -1793,10 +1794,10 @@ layui.define(['table'], function (exports) {
}
isIndeterminate = isIndeterminate && !isAll;
form.render(tableView.find('input[name="layTableCheckbox"][lay-filter="layTableAllChoose"]').prop({
tableView.find('input[name="layTableCheckbox"][lay-filter="layTableAllChoose"]').prop({
'checked': isAll,
indeterminate: isIndeterminate
}));
})
return isAll
}
@ -1908,7 +1909,7 @@ layui.define(['table'], function (exports) {
// 取消当前选中行背景色
that.setRowCheckedClass(radioElem.closest('tr'), false);
form.render(radioElem.prop('checked', false));
radioElem.prop('checked', false);
}
}); // 取消其他的选中状态
trData[checkName] = checked;
@ -1916,7 +1917,7 @@ layui.define(['table'], function (exports) {
that.setRowCheckedClass(trElem, checked); // 标记当前选中行背景色
that.setRowCheckedClass(trElem.siblings(), false); // 取消其他行背景色
form.render(trElem.find('input[type="radio"][lay-type="layTableRadio"]').prop('checked', checked));
trElem.find('input[type="radio"][lay-type="layTableRadio"]').prop('checked', checked);
} else {
// 切换只能用到单条,全选到这一步的时候应该是一个确定的状态
checked = layui.type(checked) === 'boolean' ? checked : !trData[checkName]; // 状态切换,如果遇到不可操作的节点待处理 todo
@ -1935,7 +1936,7 @@ layui.define(['table'], function (exports) {
}).join(','));
that.setRowCheckedClass(checkboxElem.closest('tr'), checked); // 标记当前选中行背景色
form.render(checkboxElem.prop({checked: checked, indeterminate: false}));
checkboxElem.prop({checked: checked, indeterminate: false});
var trDataP;
@ -1944,7 +1945,7 @@ layui.define(['table'], function (exports) {
// 找到父节点,然后判断父节点的子节点是否全部选中
trDataP = that.getNodeDataByIndex(trData[LAY_PARENT_INDEX]);
}
return that.updateCheckStatus(trDataP, checked);
}
}