fix(table): 修复自动行高时固定列高度异常 (#2825)

* fix(select): select 面板大小变化时重新定位

* update

* fix(table): 修复自动行高时固定列高度异常

* feat(table): 新增 syncFixedRowHeight 选项

* update

* update

* update

* update

* update

* fix: 重复 render 时停止 onserveResize

* update

* docs: 更新 table 文档

* fix: 修复一些边缘情况

* fix: 修复边缘情况
main
morning-star 2025-09-25 19:08:02 +08:00 committed by GitHub
parent 182c0bcedd
commit 1ec6c91da6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 150 additions and 2 deletions

View File

@ -254,6 +254,17 @@ height: function(){
</td>
<td>string</td>
<td>-</td>
</tr>
<tr>
<td>syncFixedRowHeight <sup>2.12+ 实验性</sup></td>
<td>
是否强制计算表格主区域的行高度并同步到固定列区域。开启后会对表格性能有一定的影响,仅适用于行高度自适应的场景。
该功能使用 [ResizeObserver](https://developer.mozilla.org/zh-CN/docs/Web/API/ResizeObserver) 实现,如果你的浏览器不支持 `ResizeObserver`,可以尝试添加 [polyfill](https://github.com/que-etc/resize-observer-polyfill) 来解决兼容性问题。
</td>
<td>boolean</td>
<td>-</td>
</tr>
<tr>

View File

@ -151,9 +151,12 @@ layui.define(['lay', 'i18n', 'laytpl', 'laypage', 'form', 'util'], function(expo
var ELEM_TOOL_PANEL = 'layui-table-tool-panel';
var ELEM_EXPAND = 'layui-table-expanded';
var DISABLED_TRANSITION = 'layui-table-disabled-transition';
var FIXED_HEIGHT_PATCH = 'layui-table-fixed-height-patch';
var DATA_MOVE_NAME = 'LAY_TABLE_MOVE_DICT';
var resizeObserver = lay._createResizeObserver(MOD_NAME);
// thead 区域模板
var TPL_HEADER = function(options){
var rowCols = '{{#var colspan = layui.type(item2.colspan2) === \'number\' ? item2.colspan2 : item2.colspan; if(colspan){}} colspan="{{=colspan}}"{{#} if(item2.rowspan){}} rowspan="{{=item2.rowspan}}"{{#}}}';
@ -288,6 +291,7 @@ layui.define(['lay', 'i18n', 'laytpl', 'laypage', 'form', 'util'], function(expo
var that = this;
that.index = ++table.index;
that.config = $.extend({}, that.config, table.config, options);
that.unobserveResize = $.noop;
that.render();
};
@ -318,6 +322,11 @@ layui.define(['lay', 'i18n', 'laytpl', 'laypage', 'form', 'util'], function(expo
options.elem.attr('id') || that.index
);
// 重复 render 时清理旧实例
if(thisTable.that[id] && thisTable.that[id] !== that){
thisTable.that[id].dispose();
}
thisTable.that[id] = that; // 记录当前实例对象
thisTable.config[id] = options; // 记录当前实例配置项
@ -460,6 +469,7 @@ layui.define(['lay', 'i18n', 'laytpl', 'laypage', 'form', 'util'], function(expo
that.pullData(that.page); // 请求数据
that.events(); // 事件
that.observeResize(); // 观察尺寸变化
};
// 根据列类型,定制化参数
@ -622,6 +632,7 @@ layui.define(['lay', 'i18n', 'laytpl', 'laypage', 'form', 'util'], function(expo
// 自定义 css 属性
if (options.css) text.push(options.css);
text.push('.' + FIXED_HEIGHT_PATCH + '{height:auto;}');
// 生成 style
lay.style({
@ -1521,6 +1532,11 @@ layui.define(['lay', 'i18n', 'laytpl', 'laypage', 'form', 'util'], function(expo
}, 50);
that.haveInit = true;
// reloadData 或 renderData 时tbody 高度可能不变,需要主动同步
if(that.needSyncFixedRowHeight){
that.calcFixedRowHeight();
}
layer.close(that.tipsIndex);
};
@ -2530,12 +2546,20 @@ layui.define(['lay', 'i18n', 'laytpl', 'laypage', 'form', 'util'], function(expo
var othis = $(this);
var index = othis.index();
if(othis.data('off')) return; // 不触发事件
that.layBody.find('tr:eq('+ index +')').addClass(ELEM_HOVER)
var trsElem = that.layBody.find('tr:eq('+ index +')');
trsElem.addClass(ELEM_HOVER);
if(that.needSyncFixedRowHeight){
that.fixedRowHeightPatchOnHover(this, trsElem, true);
}
}).on('mouseleave', 'tr', function(){ // 鼠标移出行
var othis = $(this);
var index = othis.index();
if(othis.data('off')) return; // 不触发事件
that.layBody.find('tr:eq('+ index +')').removeClass(ELEM_HOVER)
var trsElem = that.layBody.find('tr:eq('+ index +')');
trsElem.removeClass(ELEM_HOVER);
if(that.needSyncFixedRowHeight){
that.fixedRowHeightPatchOnHover(this, trsElem, false);
}
}).on('click', 'tr', function(e){ // 单击行
setRowEvent.call(this, 'row', e);
}).on('dblclick', 'tr', function(e){ // 双击行
@ -2922,6 +2946,119 @@ layui.define(['lay', 'i18n', 'laytpl', 'laypage', 'form', 'util'], function(expo
}
};
Class.prototype.dispose = function(){
var that = this;
that.unobserveResize();
for (const propName in that) {
if(lay.hasOwn(that, propName) && propName !== 'config'){
that[propName] = null;
}
}
}
Class.prototype.calcFixedRowHeight = function(){
var that = this;
var tableElem = that.layMain.children('table');
var leftTrs = that.layFixLeft.find('>.layui-table-body>table>tbody>tr');
var rightTrs = that.layFixRight.find('>.layui-table-body>table>tbody>tr');
var mainTrs = tableElem.find('>tbody>tr');
// 批量获取主表格行高,设置高度以优化性能
var heights = [];
mainTrs.each(function() {
heights.push(that.getElementSize(this).height);
});
if (leftTrs.length) {
leftTrs.each(function(i) {
if (heights[i]) {
this.style.height = heights[i] + 'px';
}
});
}
if (rightTrs.length) {
rightTrs.each(function(i) {
if (heights[i]) {
this.style.height = heights[i] + 'px';
}
});
}
}
// 鼠标悬停于某一列纵向移动时,若单元格会出现横向滚动条,此时 tbody 高度不变,需要修复行高
Class.prototype.fixedRowHeightPatchOnHover = function (targetEl, trsElem, isEnter) {
var that = this;
var style = that.elem.children('style')[0];
var selector = '.' + FIXED_HEIGHT_PATCH;
trsElem.toggleClass(FIXED_HEIGHT_PATCH, isEnter);
// 将当前鼠标悬停行的高度同步到 FIXED_HEIGHT_PATCH (所有区域)类名的样式中
if (isEnter) {
lay.getStyleRules(style, function (item) {
if (item.selectorText === selector) {
item.style.setProperty('height', that.getElementSize(targetEl).height + 'px', 'important');
}
})
} else {
// 将当前鼠标悬停行主区域的行高同步到固定列行
var height;
lay.getStyleRules(style, function (item) {
if (item.selectorText === selector) {
item.style.setProperty('height', 'auto');
}
})
trsElem = trsElem.filter(function () {
var tr = $(this);
var isFixed = tr.closest(ELEM_FIXED, that.layBox).length > 0;
if (!isFixed) {
height = that.getElementSize(tr[0]).height;
}
return isFixed;
})
trsElem.css('height', height);
}
}
Class.prototype.observeResize = function(){
var that = this;
if(!resizeObserver) return;
that.unobserveResize();
var el = that.elem[0];
var tableEl = that.layMain.children('table')[0];
// 显示或隐藏时重置列宽
resizeObserver.observe(el, $.proxy(that.resize, that));
// 同步固定列表格和主表格高度
var lineStyle = that.config.lineStyle;
var isAutoHeight = lineStyle && /\bheight\s*:\s*auto\b/g.test(lineStyle);
// 只重载数据时需要主动同步高度,因为 tbody 大小可能不变
var needSyncFixedRowHeight = that.needSyncFixedRowHeight = (that.layBody.length > 1) && (that.config.syncFixedRowHeight || (that.config.syncFixedRowHeight !== false && isAutoHeight));
if(needSyncFixedRowHeight){
resizeObserver.observe(
tableEl,
$.proxy(that.calcFixedRowHeight, that)
);
}
that.unobserveResize = function(){
resizeObserver.unobserve(el);
if(needSyncFixedRowHeight){
resizeObserver.unobserve(tableEl);
}
that.unobserveResize = $.noop;
}
};
// 全局事件
(function(){
// 自适应尺寸