🔥 fix(table): 修复表格列宽计算问题 (#2187)

* fix(table): 修复表格列宽计算问题

rebase

* update

* update

* update

* update
This commit is contained in:
morning-star 2024-09-23 22:47:01 +08:00 committed by GitHub
parent 045ac836ba
commit 8d14ef50e7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -15,6 +15,7 @@ layui.define(['lay', 'laytpl', 'laypage', 'form', 'util'], function(exports){
var util = layui.util; var util = layui.util;
var hint = layui.hint(); var hint = layui.hint();
var device = layui.device(); var device = layui.device();
var isChrome = layui.device('chrome').chrome;
// api // api
var table = { var table = {
@ -483,9 +484,21 @@ layui.define(['lay', 'laytpl', 'laypage', 'form', 'util'], function(exports){
options.clientWidth = options.width || function(){ //获取容器宽度 options.clientWidth = options.width || function(){ //获取容器宽度
//如果父元素宽度为0一般为隐藏元素则继续查找上层元素直到找到真实宽度为止 //如果父元素宽度为0一般为隐藏元素则继续查找上层元素直到找到真实宽度为止
var getWidth = function(parent){ var getWidth = function(parent){
var width, isNone; var width;
parent = parent || options.elem.parent() var isNone;
width = parent.width(); parent = parent || options.elem.parent();
if(!window.getComputedStyle){
// IE 中的 `currentStyle` 获取未显式设置的宽高时会得到 'auto'jQuery 中有一些 hack 方法获取准确值
width = parent.width();
}else{
var size = that.getElementSize(parent[0]);
// IE BUG
// border-box: getComputedStyle 得到的 width/height 是按照 content-box 计算出来的
width = size.boxSizing === 'border-box' && !lay.ie
? size.width - size.paddingLeft - size.paddingRight - size.borderLeftWidth - size.borderRightWidth
: size.width
}
try { try {
isNone = parent.css('display') === 'none'; isNone = parent.css('display') === 'none';
} catch(e){} } catch(e){}
@ -902,16 +915,23 @@ layui.define(['lay', 'laytpl', 'laypage', 'form', 'util'], function(exports){
var autoWidth = 0; // 自动列分配的宽度 var autoWidth = 0; // 自动列分配的宽度
var countWidth = 0; // 所有列总宽度和 var countWidth = 0; // 所有列总宽度和
var cntrWidth = that.setInit('width'); var cntrWidth = that.setInit('width');
var borderWidth = parseFloat(layui.getStyle(that.elem[0], 'border-right-width'));
var lastSpreadCol;
// 统计列个数 // 统计列个数和最后一个分配宽度的列
that.eachCols(function(i, item){ that.eachCols(function(i, item){
item.hide || colNums++; if(!item.hide){
colNums++;
if(!(item.width || item.type !== 'normal')){
lastSpreadCol = item;
}
}
}); });
// 减去边框差和滚动条宽 // 减去边框差和滚动条宽
cntrWidth = cntrWidth - function(){ cntrWidth = cntrWidth - function(){
return (options.skin === 'line' || options.skin === 'nob') ? 2 : colNums + 1; return (options.skin === 'line' || options.skin === 'nob') ? 2 : colNums + 1;
}() - that.getScrollWidth(that.layMain[0]) - 1; }() * borderWidth - that.getScrollWidth(that.layMain[0]);
// 计算自动分配的宽度 // 计算自动分配的宽度
var getAutoWidth = function(back){ var getAutoWidth = function(back){
@ -932,7 +952,7 @@ layui.define(['lay', 'laytpl', 'laypage', 'form', 'util'], function(exports){
if(!back){ if(!back){
width = item2.width || 0; width = item2.width || 0;
if(/\d+%$/.test(width)){ // 列宽为百分比 if(/\d+%$/.test(width)){ // 列宽为百分比
width = Math.floor((parseFloat(width) / 100) * cntrWidth); width = (parseFloat(width) / 100) * cntrWidth;
width < minWidth && (width = minWidth); width < minWidth && (width = minWidth);
width > maxWidth && (width = maxWidth); width > maxWidth && (width = maxWidth);
} else if(!width){ // 列宽未填写 } else if(!width){ // 列宽未填写
@ -969,31 +989,34 @@ layui.define(['lay', 'laytpl', 'laypage', 'form', 'util'], function(exports){
// 记录自动列数 // 记录自动列数
that.autoColNums = autoColNums = autoColNums > 0 ? autoColNums : 0; that.autoColNums = autoColNums = autoColNums > 0 ? autoColNums : 0;
// 设置列宽 var pixelsForLastCol = cntrWidth;
that.eachCols(function(i3, item3){ that.eachCols(function(i3, item3){
var minWidth = item3.minWidth || options.cellMinWidth; var minWidth = item3.minWidth || options.cellMinWidth;
var maxWidth = item3.maxWidth || options.cellMaxWidth; var maxWidth = item3.maxWidth || options.cellMaxWidth;
if(item3.colGroup || item3.hide) return; if(item3.colGroup || item3.hide || (lastSpreadCol && lastSpreadCol.key === item3.key)) return;
// 给未分配宽的列平均分配宽 // 给未分配宽的列平均分配宽
if(item3.width === 0){ if(item3.width === 0){
that.cssRules(item3.key, function(item){ that.cssRules(item3.key, function(item){
item.style.width = Math.floor(function(){ var newWidth = Math.round(function(){
if(autoWidth < minWidth) return minWidth; if(autoWidth < minWidth) return minWidth;
if(autoWidth > maxWidth) return maxWidth; if(autoWidth > maxWidth) return maxWidth;
return autoWidth; return autoWidth;
}()) + 'px'; }());
item.style.width = newWidth + 'px';
pixelsForLastCol = pixelsForLastCol - newWidth;
}); });
} }
// 给设定百分比的列分配列宽 // 给设定百分比的列分配列宽
else if(/\d+%$/.test(item3.width)){ else if(/\d+%$/.test(item3.width)){
that.cssRules(item3.key, function(item){ that.cssRules(item3.key, function(item){
var width = Math.floor((parseFloat(item3.width) / 100) * cntrWidth); var width = Math.round((parseFloat(item3.width) / 100) * cntrWidth);
width < minWidth && (width = minWidth); width < minWidth && (width = minWidth);
width > maxWidth && (width = maxWidth); width > maxWidth && (width = maxWidth);
item.style.width = width + 'px'; item.style.width = width + 'px';
pixelsForLastCol = pixelsForLastCol - width;
}); });
} }
@ -1001,33 +1024,20 @@ layui.define(['lay', 'laytpl', 'laypage', 'form', 'util'], function(exports){
else { else {
that.cssRules(item3.key, function(item){ that.cssRules(item3.key, function(item){
item.style.width = item3.width + 'px'; item.style.width = item3.width + 'px';
pixelsForLastCol = pixelsForLastCol - item3.width;
}); });
} }
}); });
// 最后一列获取剩余的空间,避免舍入导致的布局问题
// 填补 Math.floor 造成的数差 if(lastSpreadCol){
var patchNums = that.layMain.width() - that.getScrollWidth(that.layMain[0]) that.cssRules(lastSpreadCol.key, function(item){
- that.layMain.children('table').outerWidth(); var minWidth = lastSpreadCol.minWidth || options.cellMinWidth;
var maxWidth = lastSpreadCol.maxWidth || options.cellMaxWidth;
if(that.autoColNums > 0 && patchNums >= -colNums && patchNums <= colNums){ var newWidth = Math.max(Math.min(pixelsForLastCol, maxWidth), minWidth);
var getEndTh = function(th){ item.style.width = newWidth + 'px';
var field;
th = th || that.layHeader.eq(0).find('thead > tr:first-child > th:last-child')
field = th.data('field');
if(!field && th.prev()[0]){
return getEndTh(th.prev())
}
return th;
};
var th = getEndTh();
var key = th.data('key');
that.cssRules(key, function(item){
var width = item.style.width || th.outerWidth();
item.style.width = (parseFloat(width) + patchNums) + 'px';
// 二次校验,如果仍然出现横向滚动条(通常是 1px 的误差导致) // 二次校验,如果仍然出现横向滚动条(通常是 1px 的误差导致)
if(that.layMain.height() - that.layMain.prop('clientHeight') > 0){ if(isChrome && that.layMain.prop('offsetHeight') > that.layMain.prop('clientHeight')){
item.style.width = (parseFloat(item.style.width) - 1) + 'px'; item.style.width = (parseFloat(item.style.width) - 1) + 'px';
} }
}); });
@ -1043,7 +1053,6 @@ layui.define(['lay', 'laytpl', 'laypage', 'form', 'util'], function(exports){
} else { } else {
that.layMain.find('table').width('auto'); that.layMain.find('table').width('auto');
} }
}; };
// 重置表格尺寸/结构 // 重置表格尺寸/结构
@ -2789,6 +2798,33 @@ layui.define(['lay', 'laytpl', 'laypage', 'form', 'util'], function(exports){
that.layMain.scrollTop(scrollTop + (delta > 0 ? -step : step)); that.layMain.scrollTop(scrollTop + (delta > 0 ? -step : step));
}); });
}
/**
* 获取元素的大小
* @param {HTMLElement} elem - HTML 元素
*/
Class.prototype.getElementSize = function(elem){
if(!window.getComputedStyle) return;
var style = window.getComputedStyle(elem, null);
return {
height: parseFloat(style.height || '0'),
width: parseFloat(style.width || '0'),
borderTopWidth: parseFloat(style.borderTopWidth || '0'),
borderRightWidth: parseFloat(style.borderRightWidth || '0'),
borderBottomWidth: parseFloat(style.borderBottomWidth || '0'),
borderLeftWidth: parseFloat(style.borderLeftWidth || '0'),
paddingTop: parseFloat(style.paddingTop || '0'),
paddingRight: parseFloat(style.paddingRight || '0'),
paddingBottom: parseFloat(style.paddingBottom || '0'),
paddingLeft: parseFloat(style.paddingLeft || '0'),
marginTop: parseFloat(style.marginTop || '0'),
marginRight: parseFloat(style.marginRight || '0'),
marginBottom: parseFloat(style.marginBottom || '0'),
marginLeft: parseFloat(style.marginLeft || '0'),
boxSizing: style.boxSizing
}
}; };
// 全局事件 // 全局事件