From 4cbd1e5c0d2afe8e9740212cc122b9cfd62d4caf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B4=A4=E5=BF=83?= <3277200+sentsim@users.noreply.github.com> Date: Thu, 25 Aug 2022 16:53:35 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=B9=E8=BF=9B=20table=20=E5=A4=A7=E9=87=8F?= =?UTF-8?q?=E6=A0=B8=E5=BF=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/table.js | 395 ++++++++++++++++++++++++++----------------- 1 file changed, 239 insertions(+), 156 deletions(-) diff --git a/src/modules/table.js b/src/modules/table.js index 06aa8492..2027c8d8 100644 --- a/src/modules/table.js +++ b/src/modules/table.js @@ -3,10 +3,11 @@ * 表格组件 */ -layui.define(['laytpl', 'laypage', 'form', 'util'], function(exports){ +layui.define(['lay', 'laytpl', 'laypage', 'form', 'util'], function(exports){ "use strict"; var $ = layui.$; + var lay = layui.lay; var laytpl = layui.laytpl; var laypage = layui.laypage; var layer = layui.layer; @@ -19,7 +20,8 @@ layui.define(['laytpl', 'laypage', 'form', 'util'], function(exports){ var table = { config: { // 全局配置项 checkName: 'LAY_CHECKED' // 是否选中状态的字段名 - ,indexName: 'LAY_TABLE_INDEX' // 初始下标索引名,用于恢复排序 + ,indexName: 'LAY_TABLE_INDEX' // 初始下标索引名,用于恢复当前页表格排序 + ,numbersName: 'LAY_INDEX' //序号 ,disabledName: 'LAY_DISABLED' } ,cache: {} // 数据缓存 @@ -127,6 +129,7 @@ layui.define(['laytpl', 'laypage', 'form', 'util'], function(exports){ ,ELEM_SORT = '.layui-table-sort' ,ELEM_EDIT = 'layui-table-edit' ,ELEM_HOVER = 'layui-table-hover' + ,ELEM_GROUP = 'laytable-cell-group' ,ELEM_COL_SPECIAL = 'layui-table-col-special' ,DATA_MOVE_NAME = 'LAY_TABLE_MOVE_DICT' @@ -154,7 +157,7 @@ layui.define(['laytpl', 'laypage', 'form', 'util'], function(exports){ return ''; }() ,'{{# var isSort = !(item2.colGroup) && item2.sort; }}' - ,'' + ,'' ,'
= 1 && childIndex == (item2.colspan || 1))) return; item22.HAS_PARENT = true; - item22.parentKey = i1 + '-' + i2; + item22.parentKey = [options.index, i1, i2].join('-') // i1 + '-' + i2; childIndex = childIndex + parseInt(item22.colspan > 1 ? item22.colspan : 1); initChildCols(indexChild, options.cols[indexChild], i22, item22); }); @@ -606,12 +620,12 @@ layui.define(['laytpl', 'laypage', 'form', 'util'], function(exports){ var that = this ,options = that.config - ,parentTh = that.layHeader.find('th[data-key="'+ options.index +'-'+ parentKey +'"]') //获取父列元素 + ,parentTh = that.layHeader.find('th[data-key="'+ parentKey +'"]') //获取父列元素 ,parentColspan = parseInt(parentTh.attr('colspan')) || 0; if(parentTh[0]){ var arrParentKey = parentKey.split('-') - ,getThisCol = options.cols[arrParentKey[0]][arrParentKey[1]]; + ,getThisCol = options.cols[arrParentKey[1]][arrParentKey[2]]; hide ? parentColspan-- : parentColspan++; @@ -627,12 +641,12 @@ layui.define(['laytpl', 'laypage', 'form', 'util'], function(exports){ } }; - //多级表头补丁 + // 多级表头补丁 Class.prototype.setColsPatch = function(){ - var that = this - ,options = that.config + var that = this; + var options = that.config; - //同步表头父列的相关值 + // 同步表头父列的相关值 layui.each(options.cols, function(i1, item1){ layui.each(item1, function(i2, item2){ if(item2.hide){ @@ -642,35 +656,41 @@ layui.define(['laytpl', 'laypage', 'form', 'util'], function(exports){ }); }; - // 设置合并表头的宽度 - Class.prototype.setGroupWidth = function (thElem) { + // 设置组合表头的最大宽度 + Class.prototype.setGroupWidth = function(th){ var that = this; var options = that.config; - var parentKey; - if (options.cols.length > 1) { - for (var i = thElem ? thElem.closest('tr').index() - 1 : options.cols.length - 1; i >= 0; i--) { - // 自下向上处理合并表头的宽度 - parentKey = thElem ? thElem.attr('data-parentkey') : ''; - layui.each(that.layHeader.first().find('tr').eq(i).find('>th' + (parentKey && '[data-key="' + that.index + '-' + parentKey + '"]') + '>div.laytable-cell-group'), function (i1, item1) { - item1 = $(item1); - var width = 0; - var key = item1.parent().attr('data-key'); - layui.each(that.layHeader.first().find('th[data-parentkey="' + key.substr(key.indexOf('-') + 1) + '"]'), function (i2, item2) { - item2 = $(item2); - if (item2.hasClass(HIDE)) { - return; - } - width += item2.children('div.layui-table-cell').outerWidth(); - }); - that.layHeader.find('th[data-key="'+key+'"]').children('div.layui-table-cell').outerWidth(width); - thElem && (thElem = item1.parent()); - }) + + if(options.cols.length <= 1) return; + + // 获取表头组合 + var groups = that.layHeader.find(( + // 根据当前活动的表头 parentkey 属性查找其组合表头 + th ? ('th[data-key='+ th.data('parentkey') +']>') : '' + ) + '.' + ELEM_GROUP).get().reverse(); // 若无指向当前活动表头,则自下而上获取所有组合表头 + + layui.each(groups, function(){ + var othis = $(this); + var key = othis.parent().data('key'); + var maxWidth = 0; + + that.layHeader.eq(0).find('th[data-parentkey='+ key +']').width(function(i, width){ + var oTh = $(this); + if(oTh.hasClass(HIDE)) return; + width > 0 && (maxWidth += width); + }); + + // 给组合表头赋值最大宽度 + if(maxWidth) othis.css('max-width', maxWidth); + + // 若当前活动的组合表头仍存在上级,则继续向上设置 + if(th && othis.parent().data('parentkey')){ + that.setGroupWidth(othis.parent()); } + }); + }; - } - } - - //动态分配列宽 + // 动态分配列宽 Class.prototype.setColsWidth = function(){ var that = this; var options = that.config; @@ -692,11 +712,12 @@ layui.define(['laytpl', 'laypage', 'form', 'util'], function(exports){ // 计算自动分配的宽度 var getAutoWidth = function(back){ - //遍历所有列 + // 遍历所有列 layui.each(options.cols, function(i1, item1){ layui.each(item1, function(i2, item2){ - var width = 0 - ,minWidth = item2.minWidth || options.cellMinWidth; //最小宽度 + var width = 0; + var minWidth = item2.minWidth || options.cellMinWidth; // 最小宽度 + var maxWidth = item2.maxWidth || options.cellMaxWidth; // 最大宽度 if(!item2){ item1.splice(i2, 1); @@ -710,16 +731,22 @@ layui.define(['laytpl', 'laypage', 'form', 'util'], function(exports){ if(/\d+%$/.test(width)){ // 列宽为百分比 width = Math.floor((parseFloat(width) / 100) * cntrWidth); width < minWidth && (width = minWidth); + width > maxWidth && (width = maxWidth); } else if(!width){ // 列宽未填写 item2.width = width = 0; autoColNums++; - } else { - // 设置了宽度校验是否小于最小宽度,这里是否要判断 - item2.type === 'normal' && width < minWidth && (item2.width = width = minWidth); + } else if(item2.type === 'normal'){ + // 若 width 小于 minWidth, 则将 width 值自动设为 minWidth 的值 + width < minWidth && (item2.width = width = minWidth); + // 若 width 大于 maxWidth, 则将 width 值自动设为 maxWidth 的值 + width > maxWidth && (item2.width = width = maxWidth); } } else if(autoWidth && autoWidth < minWidth){ autoColNums--; width = minWidth; + } else if(autoWidth && autoWidth > maxWidth){ + autoColNums--; + width = maxWidth; } if(item2.hide) width = 0; @@ -728,7 +755,7 @@ layui.define(['laytpl', 'laypage', 'form', 'util'], function(exports){ }); // 如果未填充满,则将剩余宽度平分 - (cntrWidth > countWidth && autoColNums) && ( + (cntrWidth > countWidth && autoColNums > 0) && ( autoWidth = (cntrWidth - countWidth) / autoColNums ); } @@ -737,30 +764,36 @@ layui.define(['laytpl', 'laypage', 'form', 'util'], function(exports){ getAutoWidth(true); // 重新检测分配的宽度是否低于最小列宽 // 记录自动列数 - that.autoColNums = autoColNums; + that.autoColNums = autoColNums = autoColNums > 0 ? autoColNums : 0; // 设置列宽 that.eachCols(function(i3, item3){ var minWidth = item3.minWidth || options.cellMinWidth; + var maxWidth = item3.maxWidth || options.cellMaxWidth; + if(item3.colGroup || item3.hide) return; // 给未分配宽的列平均分配宽 if(item3.width === 0){ - that.getCssRule(options.index +'-'+ item3.key, function(item){ - item.style.width = Math.floor(autoWidth >= minWidth ? autoWidth : minWidth) + 'px'; + that.getCssRule(item3.key, function(item){ + item.style.width = Math.floor(function(){ + if(autoWidth < minWidth) return minWidth; + if(autoWidth > maxWidth) return maxWidth; + return autoWidth; + }()) + 'px'; }); } // 给设定百分比的列分配列宽 else if(/\d+%$/.test(item3.width)){ - that.getCssRule(options.index +'-'+ item3.key, function(item){ + that.getCssRule(item3.key, function(item){ item.style.width = Math.floor((parseFloat(item3.width) / 100) * cntrWidth) + 'px'; }); } - // 因为有可能设置的width小于minWidth被调整过,这里重新设置一遍确保最新 + // 给拥有普通 width 值的列分配最新列宽 else { - that.getCssRule(options.index +'-'+ item3.key, function(item){ + that.getCssRule(item3.key, function(item){ item.style.width = item3.width + 'px'; }); } @@ -770,7 +803,7 @@ layui.define(['laytpl', 'laypage', 'form', 'util'], function(exports){ var patchNums = that.layMain.width() - that.getScrollWidth(that.layMain[0]) - that.layMain.children('table').outerWidth(); - if(that.autoColNums && patchNums >= -colNums && patchNums <= colNums){ + if(that.autoColNums > 0 && patchNums >= -colNums && patchNums <= colNums){ var getEndTh = function(th){ var field; th = th || that.layHeader.eq(0).find('thead th:last-child') @@ -787,7 +820,7 @@ layui.define(['laytpl', 'laypage', 'form', 'util'], function(exports){ 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){ item.style.width = (parseFloat(item.style.width) - 1) + 'px'; } @@ -977,7 +1010,7 @@ layui.define(['laytpl', 'laypage', 'form', 'util'], function(exports){ Class.prototype.col = function(key){ try { key = key.split('-'); - return this.config.cols[key[1]][key[2]]; + return this.config.cols[key[1]][key[2]] || {}; } catch(e){ hint.error(e); return {}; @@ -1013,20 +1046,29 @@ layui.define(['laytpl', 'laypage', 'form', 'util'], function(exports){ } layui.each(data, function(i1, item1){ var tds = [], tds_fixed = [], tds_fixed_r = [] - ,numbers = i1 + options.limit*(curr - 1) + 1; //序号 + ,numbers = i1 + options.limit*(curr - 1) + 1; // 序号 + + // 数组值是否为 object,如果不是,则自动转为 object + if(typeof item1 !== 'object'){ + data[i1] = item1 = {LAY_KEY: item1}; + try { + table.cache[that.key][i1] = item1; + } catch(e) {} + } //若数据项为空数组,则不往下执行(因为删除数据时,会将原有数据设置为 []) if(layui.type(item1) === 'array' && item1.length === 0) return; - //记录下标索引,用于恢复排序 - if(!sort){ - item1[table.config.indexName] = i1; - } + // 加入序号保留字段 + item1[table.config.numbersName] = numbers; + + // 记录下标索引,用于恢复排序 + if(!sort) item1[table.config.indexName] = i1; //遍历表头 that.eachCols(function(i3, item3){ var field = item3.field || i3; - var key = options.index + '-' + item3.key; + var key = item3.key; var content = item1[field]; if(content === undefined || content === null) content = ''; @@ -1040,6 +1082,7 @@ layui.define(['laytpl', 'laypage', 'form', 'util'], function(exports){ if(item3.toolbar) attr.push('data-off="true"'); //行工具列关闭单元格事件 if(item3.event) attr.push('lay-event="'+ item3.event +'"'); //自定义事件 if(item3.minWidth) attr.push('data-minwidth="'+ item3.minWidth +'"'); //单元格最小宽度 + if(item3.maxWidth) attr.push('data-maxwidth="'+ item3.maxWidth +'"'); //单元格最大宽度 return attr.join(' '); }() +' class="'+ function(){ //追加样式 var classNames = []; @@ -1059,8 +1102,7 @@ layui.define(['laytpl', 'laypage', 'form', 'util'], function(exports){ }() +'>' + function(){ var tplData = $.extend(true, { - LAY_INDEX: numbers - ,LAY_COL: item3 + LAY_COL: item3 }, item1) ,checkName = table.config.checkName ,disabledName = table.config.disabledName; @@ -1265,10 +1307,11 @@ layui.define(['laytpl', 'laypage', 'form', 'util'], function(exports){ }(); // td 容器 - var td = ['' ,'
tr>th.'+ ELEM_COL_SPECIAL).filter(function(i, thElem){ - return !$(thElem).children('.laytable-cell-group').length; // 父级表头除外 + return !$(thElem).children('.'+ ELEM_GROUP).length; // 父级表头除外 }).remove(); html.find('tbody>tr>td.'+ ELEM_COL_SPECIAL).remove(); // 移除表体特殊列 @@ -1757,6 +1806,7 @@ layui.define(['laytpl', 'laypage', 'form', 'util'], function(exports){ dict.rule = item; dict.ruleWidth = parseFloat(width); dict.minWidth = othis.data('minwidth') || options.cellMinWidth; + dict.maxWidth = othis.data('maxwidth') || options.cellMaxWidth; }); // 临时记录当前拖拽信息 @@ -1779,7 +1829,11 @@ layui.define(['laytpl', 'laypage', 'form', 'util'], function(exports){ var id = thisTable.eventMoveElem.closest('.' + ELEM_VIEW).attr('lay-id'); var thatTable = thisTable.that[id]; + if(!thatTable) return; + if(setWidth < dict.minWidth) setWidth = dict.minWidth; + if(setWidth > dict.maxWidth) setWidth = dict.maxWidth; + dict.rule.style.width = setWidth + 'px'; thatTable.setGroupWidth(thisTable.eventMoveElem); layer.close(that.tipsIndex); @@ -1787,16 +1841,32 @@ layui.define(['laytpl', 'laypage', 'form', 'util'], function(exports){ } }).on('mouseup', function(e){ if(thisTable.eventMoveElem){ - var id = thisTable.eventMoveElem.closest('.' + ELEM_VIEW).attr('lay-id'); + var th = thisTable.eventMoveElem; // 当前触发拖拽的 th 元素 + var id = th.closest('.' + ELEM_VIEW).attr('lay-id'); var thatTable = thisTable.that[id]; + if(!thatTable) return; + + var key = th.data('key'); + var col = thatTable.col(key); + + // 重置过度信息 dict = {}; _BODY.css('cursor', ''); thatTable.scrollPatch(); // 清除当前拖拽信息 - thisTable.eventMoveElem.removeData(DATA_MOVE_NAME); + th.removeData(DATA_MOVE_NAME); delete thisTable.eventMoveElem; + + // 列拖拽宽度后的事件 + thatTable.getCssRule(key, function(item){ + col.width = parseFloat(item.style.width); + layui.event.call(th[0], MOD_NAME, 'colResized('+ filter +')', { + col: col, + config: thatTable.config + }); + }); } }); } @@ -1849,6 +1919,7 @@ layui.define(['laytpl', 'laypage', 'form', 'util'], function(exports){ return $.extend({ tr: tr //行元素 + ,config: options ,data: table.clearCacheKey(data) //当前行数据 ,del: function(){ //删除行数据 table.cache[that.key][index] = []; @@ -1977,41 +2048,9 @@ layui.define(['laytpl', 'laypage', 'form', 'util'], function(exports){ ); }; - // 单元格编辑 - that.layBody.on('change', '.'+ELEM_EDIT, function(){ - var othis = $(this); - var value = this.value; - var field = othis.parent().data('field'); - var index = othis.parents('tr').eq(0).data('index'); - var data = table.cache[that.key][index]; - var oldValue = data[field]; - data[field] = value; // 更新缓存中的值 - layui.event.call(this, MOD_NAME, 'edit('+ filter +')', commonMember.call(this, { - value: value - ,field: field - ,oldValue: oldValue - })); - }).on('blur', '.'+ELEM_EDIT, function(){ - var othis = $(this); - var td = othis.parent(); - var key = td.data('key'); - var index = othis.closest('tr').data('index'); - var data = table.cache[that.key][index]; - - othis.siblings(ELEM_CELL).html(function(value){ - return parseTempData.call(that, { - item3: that.col(key) - ,content: value - ,tplData: data - }); - }(othis[0].value)); - td.data('content', othis[0].value); - othis.remove(); - }); - - // 单元格事件 - that.layBody.on(options.editTrigger, 'td', function(e){ - var othis = $(this); + // 渲染单元格编辑状态 + var renderGridEdit = function(othis, e){ + othis = $(othis); if(othis.data('off')) return; // 不触发事件 @@ -2041,9 +2080,52 @@ layui.define(['laytpl', 'laypage', 'form', 'util'], function(exports){ input[0].value = othis.data('content') || data[field] || elemCell.text(); othis.find('.'+ELEM_EDIT)[0] || othis.append(input); input.focus(); - layui.stope(e); - return; + e && layui.stope(e); } + }; + + // 单元格编辑 - 输入框内容被改变的事件 + that.layBody.on('change', '.'+ ELEM_EDIT, function(){ + var othis = $(this); + var td = othis.parent(); + var value = this.value; + var field = othis.parent().data('field'); + var index = othis.closest('tr').data('index'); + var data = table.cache[that.key][index]; + + //事件回调的参数对象 + var params = commonMember.call(td[0], { + value: value + ,field: field + ,oldValue: data[field] // 编辑前的值 + ,td: td + ,reedit: function(){ // 重新编辑 + setTimeout(function(){ + // 重新渲染为编辑状态 + renderGridEdit(params.td); + + // 将字段缓存的值恢复到编辑之前的值 + var obj = {}; + obj[field] = params.oldValue; + params.update(obj); + }); + } + }); + + // 更新缓存中的值 + var obj = {}; //变更的键值 + obj[field] = value; + params.update(obj); + + // 执行 API 编辑事件 + layui.event.call(td[0], MOD_NAME, 'edit('+ filter +')', params); + }).on('blur', '.'+ ELEM_EDIT, function(){ // 单元格编辑 - 恢复非编辑状态事件 + $(this).remove(); // 移除编辑状态 + }); + + // 单元格触发编辑的事件 + that.layBody.on(options.editTrigger, 'td', function(e){ + renderGridEdit(this, e) }).on('mouseenter', 'td', function(){ gridExpand.call(this) }).on('mouseleave', 'td', function(){ @@ -2165,20 +2247,21 @@ layui.define(['laytpl', 'laypage', 'form', 'util'], function(exports){ //初始化 table.init = function(filter, settings){ settings = settings || {}; - var that = this - ,inst = null - ,elemTable = filter ? $('table[lay-filter="'+ filter +'"]') : $(ELEM + '[lay-data]') - ,errorTips = 'Table element property lay-data configuration item has a syntax error: '; + var that = this; + var inst = null; + var elemTable = filter + ? $('table[lay-filter="'+ filter +'"]') + : $(ELEM + '[lay-data],'+ ELEM + '[lay-options]'); + var errorTips = 'Table element property lay-data configuration item has a syntax error: '; //遍历数据表格 elemTable.each(function(){ - var othis = $(this), tableData = othis.attr('lay-data'); - - try { - tableData = new Function('return '+ tableData)(); - } catch(e) { - hint.error(errorTips + tableData, 'error') - } + var othis = $(this); + var attrData = othis.attr('lay-data'); + var tableData = lay.options(this, { + attr: attrData ? 'lay-data' : null, + errorText: errorTips + (attrData || othis.attr('lay-options')) + }); var cols = [], options = $.extend({ elem: this @@ -2195,13 +2278,12 @@ layui.define(['laytpl', 'laypage', 'form', 'util'], function(exports){ othis.find('thead>tr').each(function(i){ options.cols[i] = []; $(this).children().each(function(ii){ - var th = $(this), itemData = th.attr('lay-data'); - - try{ - itemData = new Function('return '+ itemData)(); - } catch(e){ - return hint.error(errorTips + itemData) - } + var th = $(this); + var attrData = th.attr('lay-data'); + var itemData = lay.options(this, { + attr: attrData ? 'lay-data' : null, + errorText: errorTips + (attrData || th.attr('lay-options')) + }); var row = $.extend({ title: th.text() @@ -2483,6 +2565,7 @@ layui.define(['laytpl', 'laypage', 'form', 'util'], function(exports){ data = $.extend({}, data); delete data[table.config.checkName]; delete data[table.config.indexName]; + delete data[table.config.numbersName]; delete data[table.config.disabledName]; return data; };