Ext3.x版本的锁定列和多表头都是通过插件实现,但是这两个插件不能一起工作,而实际上这样的业务也是存在的,即在多表头的情况下也需要锁定列。

在Ext的论坛上有很多这样的需求,都没有一个解决方案,除了一个需要收费的(150没有),而且联系起来也麻烦。后面决定自己做一个。

效果如下图:

文件:

/*!* Ext JS Library 3.3.0* Copyright(c) 2006-2010 Ext JS, Inc.* licensing@extjs.com* http://www.extjs.com/license*/
Ext.ns('Ext.ux.grid');Ext.ux.grid.LockingHeaderGroupView = Ext.extend(Ext.grid.GridView, {lockText: '锁定',unlockText: '解锁',rowBorderWidth: 1,lockedBorderWidth: 1,//先支持只有两层的多表头情况//从外部传入//grows : [[{},{},{},{"align":"center","colspan":2,"header":"合并后名称"},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}]],/** This option ensures that height between the rows is synchronized* between the locked and unlocked sides. This option only needs to be used* when the row heights aren't predictable.*/syncHeights: false,initTemplates: function () {var ts = this.templates || {};if (!ts.masterTpl) {ts.masterTpl = new Ext.Template('<div class="x-grid3" hidefocus="true">','<div class="x-grid3-locked">','<div class="x-grid3-header"><div class="x-grid3-header-inner"><div class="x-grid3-header-offset" style="{lstyle}">{lockedHeader}</div></div><div class="x-clear"></div></div>','<div class="x-grid3-scroller"><div class="x-grid3-body" style="{lstyle}">{lockedBody}</div><div class="x-grid3-scroll-spacer"></div></div>','</div>','<div class="x-grid3-viewport x-grid3-unlocked">','<div class="x-grid3-header"><div class="x-grid3-header-inner"><div class="x-grid3-header-offset" style="{ostyle}">{header}</div></div><div class="x-clear"></div></div>','<div class="x-grid3-scroller"><div class="x-grid3-body" style="{bstyle}">{body}</div><a href="#" class="x-grid3-focus" tabIndex="-1"></a></div>','</div>','<div class="x-grid3-resize-marker"> </div>','<div class="x-grid3-resize-proxy"> </div>','</div>');}if (!ts.gcell) {ts.gcell = new Ext.XTemplate('<td class="x-grid3-hd x-grid3-gcell x-grid3-td-{id} ux-grid-hd-group-row-{row} {cls}" style="{style}">', '<div {tooltip} class="x-grid3-hd-inner x-grid3-hd-{id}" unselectable="on" style="{istyle}">', this.grid.enableHdMenu ? '<a class="x-grid3-hd-btn" href="#"></a>' : '', '{value}</div></td>');}this.hrowRe = new RegExp("ux-grid-hd-group-row-(\\d+)", "");this.templates = ts;Ext.ux.grid.LockingHeaderGroupView.superclass.initTemplates.call(this);},getEditorParent: function (ed) {return this.el.dom;},initElements: function () {var el = Ext.get(this.grid.getGridEl().dom.firstChild),lockedWrap = el.child('div.x-grid3-locked'),lockedHd = lockedWrap.child('div.x-grid3-header'),lockedScroller = lockedWrap.child('div.x-grid3-scroller'),mainWrap = el.child('div.x-grid3-viewport'),mainHd = mainWrap.child('div.x-grid3-header'),scroller = mainWrap.child('div.x-grid3-scroller');if (this.grid.hideHeaders) {lockedHd.setDisplayed(false);mainHd.setDisplayed(false);}if (this.forceFit) {scroller.setStyle('overflow-x', 'hidden');}Ext.apply(this, {el: el,mainWrap: mainWrap,mainHd: mainHd,innerHd: mainHd.dom.firstChild,scroller: scroller,mainBody: scroller.child('div.x-grid3-body'),focusEl: scroller.child('a'),resizeMarker: el.child('div.x-grid3-resize-marker'),resizeProxy: el.child('div.x-grid3-resize-proxy'),lockedWrap: lockedWrap,lockedHd: lockedHd,lockedScroller: lockedScroller,lockedBody: lockedScroller.child('div.x-grid3-body'),lockedInnerHd: lockedHd.child('div.x-grid3-header-inner', true)});this.focusEl.swallowEvent('click', true);},getLockedRows: function () {return this.hasRows() ? this.lockedBody.dom.childNodes : [];},getLockedRow: function (row) {return this.getLockedRows()[row];},getCell: function (row, col) {var lockedLen = this.cm.getLockedCount();if (col < lockedLen) {return this.getLockedRow(row).getElementsByTagName('td')[col];}return Ext.ux.grid.LockingHeaderGroupView.superclass.getCell.call(this, row, col - lockedLen);},getHeaderCell: function (index) {var lockedLen = this.cm.getLockedCount();if (index < lockedLen) {return this.lockedHd.dom.getElementsByTagName('td')[index];}return Ext.ux.grid.LockingHeaderGroupView.superclass.getHeaderCell.call(this, index - lockedLen);},addRowClass: function (row, cls) {var lockedRow = this.getLockedRow(row);if (lockedRow) {this.fly(lockedRow).addClass(cls);}Ext.ux.grid.LockingHeaderGroupView.superclass.addRowClass.call(this, row, cls);},removeRowClass: function (row, cls) {var lockedRow = this.getLockedRow(row);if (lockedRow) {this.fly(lockedRow).removeClass(cls);}Ext.ux.grid.LockingHeaderGroupView.superclass.removeRowClass.call(this, row, cls);},removeRow: function (row) {Ext.removeNode(this.getLockedRow(row));Ext.ux.grid.LockingHeaderGroupView.superclass.removeRow.call(this, row);},removeRows: function (firstRow, lastRow) {var lockedBody = this.lockedBody.dom,rowIndex = firstRow;for (; rowIndex <= lastRow; rowIndex++) {Ext.removeNode(lockedBody.childNodes[firstRow]);}Ext.ux.grid.LockingHeaderGroupView.superclass.removeRows.call(this, firstRow, lastRow);},syncScroll: function (e) {this.lockedScroller.dom.scrollTop = this.scroller.dom.scrollTop;Ext.ux.grid.LockingHeaderGroupView.superclass.syncScroll.call(this, e);},updateSortIcon: function (col, dir) {var sortClasses = this.sortClasses,lockedHeaders = this.lockedHd.select('td').removeClass(sortClasses),headers = this.mainHd.select('td').removeClass(sortClasses),lockedLen = this.cm.getLockedCount(),cls = sortClasses[dir == 'DESC' ? 1 : 0];if (col < lockedLen) {lockedHeaders.item(col).addClass(cls);} else {headers.item(col - lockedLen).addClass(cls);}},updateAllColumnWidths: function () {var tw = this.getTotalWidth(),clen = this.cm.getColumnCount(),lw = this.getLockedWidth(),llen = this.cm.getLockedCount(),ws = [], len, i;this.updateLockedWidth();for (i = 0; i < clen; i++) {ws[i] = this.getColumnWidth(i);var hd = this.getHeaderCell(i);hd.style.width = ws[i];}var lns = this.getLockedRows(), ns = this.getRows(), row, trow, j;for (i = 0, len = ns.length; i < len; i++) {row = lns[i];row.style.width = lw;if (row.firstChild) {row.firstChild.style.width = lw;trow = row.firstChild.rows[0];for (j = 0; j < llen; j++) {trow.childNodes[j].style.width = ws[j];}}row = ns[i];row.style.width = tw;if (row.firstChild) {row.firstChild.style.width = tw;trow = row.firstChild.rows[0];for (j = llen; j < clen; j++) {trow.childNodes[j - llen].style.width = ws[j];}}}this.onAllColumnWidthsUpdated(ws, tw);this.syncHeaderHeight();},updateColumnWidth: function (col, width) {var w = this.getColumnWidth(col),llen = this.cm.getLockedCount(),ns, rw, c, row;this.updateLockedWidth();if (col < llen) {ns = this.getLockedRows();rw = this.getLockedWidth();c = col;} else {ns = this.getRows();rw = this.getTotalWidth();c = col - llen;}var hd = this.getHeaderCell(col);hd.style.width = w;for (var i = 0, len = ns.length; i < len; i++) {row = ns[i];row.style.width = rw;if (row.firstChild) {row.firstChild.style.width = rw;row.firstChild.rows[0].childNodes[c].style.width = w;}}this.onColumnWidthUpdated(col, w, this.getTotalWidth());this.syncHeaderHeight();},updateColumnHidden: function (col, hidden) {var llen = this.cm.getLockedCount(),ns, rw, c, row,display = hidden ? 'none' : '';this.updateLockedWidth();if (col < llen) {ns = this.getLockedRows();rw = this.getLockedWidth();c = col;} else {ns = this.getRows();rw = this.getTotalWidth();c = col - llen;}var hd = this.getHeaderCell(col);hd.style.display = display;for (var i = 0, len = ns.length; i < len; i++) {row = ns[i];row.style.width = rw;if (row.firstChild) {row.firstChild.style.width = rw;row.firstChild.rows[0].childNodes[c].style.display = display;}}this.onColumnHiddenUpdated(col, hidden, this.getTotalWidth());delete this.lastViewWidth;this.layout();},doRender: function (cs, rs, ds, startRow, colCount, stripe) {var ts = this.templates, ct = ts.cell, rt = ts.row, last = colCount - 1,tstyle = 'width:' + this.getTotalWidth() + ';',lstyle = 'width:' + this.getLockedWidth() + ';',buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r;for (var j = 0, len = rs.length; j < len; j++) {r = rs[j]; cb = []; lcb = [];var rowIndex = (j + startRow);for (var i = 0; i < colCount; i++) {c = cs[i];p.id = c.id;p.css = (i === 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '')) +(this.cm.config[i].cellCls ? ' ' + this.cm.config[i].cellCls : '');p.attr = p.cellAttr = '';p.style = c.style;//根据配置加入渲染前事件if (c.scope && c.scope.beforeRenderer) {c.scope.beforeRenderer.call(c.scope, r.data[c.name], p, r, j, i);}p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);if (Ext.isEmpty(p.value)) {p.value = ' ';}if (this.markDirty && r.dirty && Ext.isDefined(r.modified[c.name])) {p.css += ' x-grid3-dirty-cell';}if (c.locked) {lcb[lcb.length] = ct.apply(p);} else {cb[cb.length] = ct.apply(p);}}var alt = [];if (stripe && ((rowIndex + 1) % 2 === 0)) {alt[0] = 'x-grid3-row-alt';}if (r.dirty) {alt[1] = ' x-grid3-dirty-row';}rp.cols = colCount;if (this.getRowClass) {alt[2] = this.getRowClass(r, rowIndex, rp, ds);}rp.alt = alt.join(' ');rp.cells = cb.join('');rp.tstyle = tstyle;buf[buf.length] = rt.apply(rp);rp.cells = lcb.join('');rp.tstyle = lstyle;lbuf[lbuf.length] = rt.apply(rp);}return [buf.join(''), lbuf.join('')];},processRows: function (startRow, skipStripe) {if (!this.ds || this.ds.getCount() < 1) {return;}var rows = this.getRows(),lrows = this.getLockedRows(),row, lrow;skipStripe = skipStripe || !this.grid.stripeRows;startRow = startRow || 0;for (var i = 0, len = rows.length; i < len; ++i) {row = rows[i];lrow = lrows[i];row.rowIndex = i;lrow.rowIndex = i;if (!skipStripe) {row.className = row.className.replace(this.rowClsRe, ' ');lrow.className = lrow.className.replace(this.rowClsRe, ' ');if ((i + 1) % 2 === 0) {row.className += ' x-grid3-row-alt';lrow.className += ' x-grid3-row-alt';}}this.syncRowHeights(row, lrow);}if (startRow === 0) {Ext.fly(rows[0]).addClass(this.firstRowCls);Ext.fly(lrows[0]).addClass(this.firstRowCls);}Ext.fly(rows[rows.length - 1]).addClass(this.lastRowCls);Ext.fly(lrows[lrows.length - 1]).addClass(this.lastRowCls);},syncRowHeights: function (row1, row2) {if (this.syncHeights) {var el1 = Ext.get(row1),el2 = Ext.get(row2),h1 = el1.getHeight(),h2 = el2.getHeight();if (h1 > h2) {el2.setHeight(h1);} else if (h2 > h1) {el1.setHeight(h2);}}},afterRender: function () {if (!this.ds || !this.cm) {return;}var bd = this.renderRows() || [' ', ' '];this.mainBody.dom.innerHTML = bd[0];this.lockedBody.dom.innerHTML = bd[1];this.processRows(0, true);if (this.deferEmptyText !== true) {this.applyEmptyText();}this.grid.fireEvent('viewready', this.grid);},renderUI: function () {var templates = this.templates,header = this.renderHeaders(),body = templates.body.apply({ rows: ' ' });return templates.masterTpl.apply({body: body,header: header[0],ostyle: 'width:' + this.getOffsetWidth() + ';',bstyle: 'width:' + this.getTotalWidth() + ';',lockedBody: body,lockedHeader: header[1],lstyle: 'width:' + this.getLockedWidth() + ';'});},afterRenderUI: function () {var g = this.grid;this.initElements();Ext.fly(this.innerHd).on('click', this.handleHdDown, this);Ext.fly(this.lockedInnerHd).on('click', this.handleHdDown, this);this.mainHd.on({scope: this,mouseover: this.handleHdOver,mouseout: this.handleHdOut,mousemove: this.handleHdMove});this.lockedHd.on({scope: this,mouseover: this.handleHdOver,mouseout: this.handleHdOut,mousemove: this.handleHdMove});this.scroller.on('scroll', this.syncScroll, this);if (g.enableColumnResize !== false) {this.splitZone = new Ext.grid.GridView.SplitDragZone(g, this.mainHd.dom);this.splitZone.setOuterHandleElId(Ext.id(this.lockedHd.dom));this.splitZone.setOuterHandleElId(Ext.id(this.mainHd.dom));}if (g.enableColumnMove) {this.columnDrag = new Ext.grid.GridView.ColumnDragZone(g, this.innerHd);this.columnDrag.setOuterHandleElId(Ext.id(this.lockedInnerHd));this.columnDrag.setOuterHandleElId(Ext.id(this.innerHd));this.columnDrop = new Ext.grid.HeaderDropZone(g, this.mainHd.dom);}if (g.enableHdMenu !== false) {this.hmenu = new Ext.menu.Menu({ id: g.id + '-hctx' });this.hmenu.add({ itemId: 'asc', text: this.sortAscText, cls: 'xg-hmenu-sort-asc' },{ itemId: 'desc', text: this.sortDescText, cls: 'xg-hmenu-sort-desc' });if (this.grid.enableColLock !== false) {this.hmenu.add('-',{ itemId: 'lock', text: this.lockText, cls: 'xg-hmenu-lock' },{ itemId: 'unlock', text: this.unlockText, cls: 'xg-hmenu-unlock' });}if (g.enableColumnHide !== false) {this.colMenu = new Ext.menu.Menu({ id: g.id + '-hcols-menu' });this.colMenu.on({scope: this,beforeshow: this.beforeColMenuShow,itemclick: this.handleHdMenuClick});this.hmenu.add('-', {itemId: 'columns',hideOnClick: false,text: this.columnsText,menu: this.colMenu,iconCls: 'x-cols-icon'});}this.hmenu.on('itemclick', this.handleHdMenuClick, this);}if (g.trackMouseOver) {this.mainBody.on({scope: this,mouseover: this.onRowOver,mouseout: this.onRowOut});this.lockedBody.on({scope: this,mouseover: this.onRowOver,mouseout: this.onRowOut});}if (g.enableDragDrop || g.enableDrag) {this.dragZone = new Ext.grid.GridDragZone(g, {ddGroup: g.ddGroup || 'GridDD'});}this.updateHeaderSortState();},layout: function () {if (!this.mainBody) {return;}var g = this.grid;var c = g.getGridEl();var csize = c.getSize(true);var vw = csize.width;if (!g.hideHeaders && (vw < 20 || csize.height < 20)) {return;}this.syncHeaderHeight();if (g.autoHeight) {this.scroller.dom.style.overflow = 'visible';this.lockedScroller.dom.style.overflow = 'visible';if (Ext.isWebKit) {this.scroller.dom.style.position = 'static';this.lockedScroller.dom.style.position = 'static';}} else {this.el.setSize(csize.width, csize.height);var hdHeight = this.mainHd.getHeight();var vh = csize.height - (hdHeight);}this.updateLockedWidth();if (this.forceFit) {if (this.lastViewWidth != vw) {this.fitColumns(false, false);this.lastViewWidth = vw;}} else {this.autoExpand();this.syncHeaderScroll();}this.onLayout(vw, vh);},getOffsetWidth: function () {return (this.cm.getTotalWidth() - this.cm.getTotalLockedWidth() + this.getScrollOffset()) + 'px';},//GROUP 方法getGroupStyle: function (group, gcol) {var width = 0, hidden = true;for (var i = gcol, len = gcol + group.colspan; i < len; i++) {if (!this.cm.isHidden(i)) {var cw = this.cm.getColumnWidth(i);if (typeof cw == 'number') {width += cw;}hidden = false;}}if (group.colspan > 1) {if (Ext.isWebKit) {//Chrome浏览器下,取消注释会出现表头对不齐的问题//width += (group.colspan - 1);} else if (Ext.isIE7) {width = width - 2;} else {width--;}} else {if (Ext.isGecko) {if (gcol == 1 || gcol == 3) {width++;}}}return {width: (Ext.isBorderBox || (Ext.isWebKit && !Ext.isSafari2) ? width : Math.max(width - this.borderWidth, 0)) + 'px',hidden: hidden};},updateGroupStyles: function (col) {var tables = this.mainHd.query('.x-grid3-header-offset > table'), tw = this.getTotalWidth(), grows = this.grows;for (var row = 0; row < tables.length; row++) {tables[row].style.width = tw;if (row < grows.length) {var cells = tables[row].firstChild.firstChild.childNodes;for (var i = 0, gcol = 0; i < cells.length; i++) {var group = grows[row][i];if ((typeof col != 'number') || (col >= gcol && col < gcol + group.colspan)) {var gs = Ext.ux.grid.ColumnHeaderGroup.prototype.getGroupStyle.call(this, group, gcol);cells[i].style.width = gs.width;cells[i].style.display = gs.hidden ? 'none' : '';}gcol += group.colspan;}}}},getGroupRowIndex: function (el) {if (el) {var m = el.className.match(this.hrowRe);if (m && m[1]) {return parseInt(m[1], 10);}}return this.grows.length;},getGroupSpan: function (row, col) {if (row < 0) {return {col: 0,colspan: this.cm.getColumnCount()};}var r = this.grows[row];if (r) {for (var i = 0, gcol = 0, len = r.length; i < len; i++) {var group = r[i];if (col >= gcol && col < gcol + group.colspan) {return {col: gcol,colspan: group.colspan};}gcol += group.colspan;}return {col: gcol,colspan: 0};}return {col: col,colspan: 1};},renderHeaders: function () {var ts = this.templates, headers = [], cm = this.cm, grows = this.grows;var len = cm.getColumnCount();if (!grows || grows.length == 0) {//没有多表头,return this.getLockingHeaders();}var lockIndex = -1, lockGrows = [], unlockGrows = [];for (var i = 0; i < len; i++) {if (cm.isLocked(i)) {//这个字段是锁定字段,根据这个字段所处的位置,将多表头定义的rows分割成两部分//注意不能break,需要找到最后的一个locked字段lockIndex = i;}}grows = grows[0];//FIXME 定义分组情况的数组,只支持两级分组//lockIndex = 5;//[{},{},{},{"align":"center","colspan":2,"header":"合并后名称"},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}]if (lockIndex >= 0) {var index = 0; var colspan = 0;//原判断错误,修复了右边unLockedGrid只有一列的时候,不显示表头的问题for (var i = 0; i < grows.length; i++) {var group = grows[i];colspan += group.colspan || 1;index = colspan - 1;if (lockIndex >= index) {lockGrows.push(group);} else {unlockGrows.push(group);}}}var lastLockCol = 0;for (var i = 0; i < lockGrows.length; i++) {var g = lockGrows[i];lastLockCol += (g.colspan || 1);}//分组表头,包括锁定的分组和未锁定的分组var lockGroupHeader = this.getGroupHeader(lockGrows, true, 0);var unlockGroupHeader = this.getGroupHeader(unlockGrows, false, lastLockCol);//实际的表头,包括锁定和未锁定var lockingHeaders = this.getLockingHeaders();var s1 = [unlockGroupHeader, lockingHeaders[0]].join('');var s2 = [lockGroupHeader, lockingHeaders[1]].join('');return [s1, s2];},/*** 返回多表头的header部分* @param {} grows 列的集合,可能是锁定部分和未锁定部分* @param {} lockflag 是否锁定的标识* @param {} firstIndex 比如对于未锁定部分,表示的就是:lockGrows的第一个元素在整个cm的位置* @return {}*/getGroupHeader: function (grows, lockflag, lastLockCol) {var ts = this.templates, cm = this.cm, cells = [];for (var i = 0, len = grows.length; i < len; i++) {var group = grows[i];group.colspan = group.colspan || 1;//FIXME 如果没有分组的列,那么需要加入一个style,参见GridView.getColumnStylevar colIndex = group.dataIndex ? cm.findColumnIndex(group.dataIndex) : lastLockCol;//最底下的一层分组(不计算表格的header这一组)if (group.colspan == 1) {cm.config[colIndex].marginTop = true;}var id = this.getColumnId(colIndex), gs = this.getGroupStyle.call(this, group, lastLockCol);cells[i] = ts.gcell.apply({cls: (group.header && group.header != '') ? 'ux-grid-hd-group-cell' : 'ux-grid-hd-nogroup-cell',id: id,row: 0,style: 'width:' + gs.width + ';' + (gs.hidden ? 'display:none;' : '') + (group.align ? 'text-align:' + group.align + ';' : ''),tooltip: group.tooltip ? (Ext.QuickTips.isEnabled() ? 'ext:qtip' : 'title') + '="' + group.tooltip + '"' : '',istyle: group.align == 'right' ? 'padding-right:16px' : '',btn: this.grid.enableHdMenu && group.header,value: group.header || '&nbsp;'});lastLockCol += group.colspan;}var tstyle = 'width:' + this.getLockedWidth() + ';';if (!lockflag) {tstyle = 'width:' + this.getTotalWidth() + ';';}//加入合并的表头return ts.header.apply({cells: cells.join(''),tstyle: tstyle});},/*** 返回锁定列的表头部分* @return {}*/getLockingHeaders: function () {var cm = this.cm,ts = this.templates,ct = ts.hcell,cb = [], lcb = [],p = {},len = cm.getColumnCount(),last = len - 1;for (var i = 0; i < len; i++) {p.id = cm.getColumnId(i);p.value = cm.getColumnHeader(i) || '';p.style = this.getColumnStyle(i, true);p.tooltip = this.getColumnTooltip(i);p.css = (i === 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '')) +(cm.config[i].headerCls ? ' ' + cm.config[i].headerCls : '');if (cm.config[i].align == 'right') {p.istyle = 'padding-right:16px';} else {delete p.istyle;}//FIXME 如果是多表头的情况下,需要设置文字上下居中,这里只针对适合2组表头的情况.marginTop在ColumnHeaderGroup.js中设置了if (!p.istyle) {p.istyle = '';}if (cm.config[i].marginTop && !cm.config[i].hidden) {p.istyle += ';margin-top:-20px;height:40px;line-height:40px;';}if (cm.isLocked(i)) {lcb[lcb.length] = ct.apply(p);} else {cb[cb.length] = ct.apply(p);}}return [ts.header.apply({ cells: cb.join(''), tstyle: 'width:' + this.getTotalWidth() + ';' }),ts.header.apply({ cells: lcb.join(''), tstyle: 'width:' + this.getLockedWidth() + ';' })];},updateHeaders: function () {var hd = this.renderHeaders();this.innerHd.firstChild.innerHTML = hd[0];this.innerHd.firstChild.style.width = this.getOffsetWidth();this.innerHd.firstChild.firstChild.style.width = this.getTotalWidth();this.lockedInnerHd.firstChild.innerHTML = hd[1];var lw = this.getLockedWidth();this.lockedInnerHd.firstChild.style.width = lw;this.lockedInnerHd.firstChild.firstChild.style.width = lw;},getResolvedXY: function (resolved) {if (!resolved) {return null;}var c = resolved.cell, r = resolved.row;return c ? Ext.fly(c).getXY() : [this.scroller.getX(), Ext.fly(r).getY()];},syncFocusEl: function (row, col, hscroll) {Ext.ux.grid.LockingHeaderGroupView.superclass.syncFocusEl.call(this, row, col, col < this.cm.getLockedCount() ? false : hscroll);},ensureVisible: function (row, col, hscroll) {return Ext.ux.grid.LockingHeaderGroupView.superclass.ensureVisible.call(this, row, col, col < this.cm.getLockedCount() ? false : hscroll);},insertRows: function (dm, firstRow, lastRow, isUpdate) {var last = dm.getCount() - 1;if (!isUpdate && firstRow === 0 && lastRow >= last) {this.refresh();} else {if (!isUpdate) {this.fireEvent('beforerowsinserted', this, firstRow, lastRow);}var html = this.renderRows(firstRow, lastRow),before = this.getRow(firstRow);if (before) {if (firstRow === 0) {this.removeRowClass(0, this.firstRowCls);}Ext.DomHelper.insertHtml('beforeBegin', before, html[0]);before = this.getLockedRow(firstRow);Ext.DomHelper.insertHtml('beforeBegin', before, html[1]);} else {this.removeRowClass(last - 1, this.lastRowCls);Ext.DomHelper.insertHtml('beforeEnd', this.mainBody.dom, html[0]);Ext.DomHelper.insertHtml('beforeEnd', this.lockedBody.dom, html[1]);}if (!isUpdate) {this.fireEvent('rowsinserted', this, firstRow, lastRow);this.processRows(firstRow);} else if (firstRow === 0 || firstRow >= last) {this.addRowClass(firstRow, firstRow === 0 ? this.firstRowCls : this.lastRowCls);}}this.syncFocusEl(firstRow);},getColumnStyle: function (col, isHeader) {var style = !isHeader ? this.cm.config[col].cellStyle || this.cm.config[col].css || '' : this.cm.config[col].headerStyle || '';style += 'width:' + this.getColumnWidth(col) + ';';if (this.cm.isHidden(col)) {style += 'display:none;';}var align = this.cm.config[col].align;if (align) {style += 'text-align:' + align + ';';}return style;},getLockedWidth: function () {return (this.cm.getTotalLockedWidth() + 1) + 'px';},getTotalWidth: function () {return (this.cm.getTotalWidth() - this.cm.getTotalLockedWidth()) + 'px';},getColumnData: function () {var cs = [], cm = this.cm, colCount = cm.getColumnCount();for (var i = 0; i < colCount; i++) {var name = cm.getDataIndex(i);cs[i] = {name: (!Ext.isDefined(name) ? this.ds.fields.get(i).name : name),renderer: cm.getRenderer(i),scope: cm.getRendererScope(i),id: cm.getColumnId(i),style: this.getColumnStyle(i),locked: cm.isLocked(i)};}return cs;},renderBody: function () {var markup = this.renderRows() || [' ', ' '];return [this.templates.body.apply({ rows: markup[0] }), this.templates.body.apply({ rows: markup[1] })];},refreshRow: function (record) {var store = this.ds,colCount = this.cm.getColumnCount(),columns = this.getColumnData(),last = colCount - 1,cls = ['x-grid3-row'],rowParams = {tstyle: String.format("width: {0};", this.getTotalWidth())},lockedRowParams = {tstyle: String.format("width: {0};", this.getLockedWidth())},colBuffer = [],lockedColBuffer = [],cellTpl = this.templates.cell,rowIndex,row,lockedRow,column,meta,css,i;if (Ext.isNumber(record)) {rowIndex = record;record = store.getAt(rowIndex);} else {rowIndex = store.indexOf(record);}if (!record || rowIndex < 0) {return;}for (i = 0; i < colCount; i++) {column = columns[i];if (i == 0) {css = 'x-grid3-cell-first';} else {css = (i == last) ? 'x-grid3-cell-last ' : '';}meta = {id: column.id,style: column.style,css: css,attr: "",cellAttr: ""};if (column.scope && column.scope.beforeRenderer) {column.scope.beforeRenderer.call(column.scope, record.data[column.name], meta, record, rowIndex, i, store);}meta.value = column.renderer.call(column.scope, record.data[column.name], meta, record, rowIndex, i, store);if (Ext.isEmpty(meta.value)) {meta.value = ' ';}if (this.markDirty && record.dirty && typeof record.modified[column.name] != 'undefined') {meta.css += ' x-grid3-dirty-cell';}if (column.locked) {lockedColBuffer[i] = cellTpl.apply(meta);} else {colBuffer[i] = cellTpl.apply(meta);}}row = this.getRow(rowIndex);row.className = '';lockedRow = this.getLockedRow(rowIndex);lockedRow.className = '';if (this.grid.stripeRows && ((rowIndex + 1) % 2 === 0)) {cls.push('x-grid3-row-alt');}if (this.getRowClass) {rowParams.cols = colCount;cls.push(this.getRowClass(record, rowIndex, rowParams, store));}// Unlocked rowsthis.fly(row).addClass(cls).setStyle(rowParams.tstyle);rowParams.cells = colBuffer.join("");row.innerHTML = this.templates.rowInner.apply(rowParams);// Locked rowsthis.fly(lockedRow).addClass(cls).setStyle(lockedRowParams.tstyle);lockedRowParams.cells = lockedColBuffer.join("");lockedRow.innerHTML = this.templates.rowInner.apply(lockedRowParams);lockedRow.rowIndex = rowIndex;this.syncRowHeights(row, lockedRow);this.fireEvent('rowupdated', this, rowIndex, record);},refresh: function (headersToo) {this.fireEvent('beforerefresh', this);this.grid.stopEditing(true);var result = this.renderBody();this.mainBody.update(result[0]).setWidth(this.getTotalWidth());this.lockedBody.update(result[1]).setWidth(this.getLockedWidth());if (headersToo === true) {this.updateHeaders();this.updateHeaderSortState();}this.processRows(0, true);this.layout();this.applyEmptyText();this.fireEvent('refresh', this);//修复了左右两边lockedGrid|unLockedGrid行高不一致的问题$(".x-grid3-row").height(20);},onDenyColumnLock: function () {},initData: function (ds, cm) {if (this.cm) {this.cm.un('columnlockchange', this.onColumnLock, this);}Ext.ux.grid.LockingHeaderGroupView.superclass.initData.call(this, ds, cm);if (this.cm) {this.cm.on('columnlockchange', this.onColumnLock, this);}},onColumnLock: function () {this.refresh(true);},handleHdMenuClick: function (item) {var index = this.hdCtxIndex,cm = this.cm,id = item.getItemId(),llen = cm.getLockedCount();switch (id) {case 'lock':if (cm.getColumnCount(true) <= llen + 1) {this.onDenyColumnLock();return undefined;}cm.setLocked(index, true);if (llen != index) {cm.moveColumn(index, llen);this.grid.fireEvent('columnmove', index, llen);}break;case 'unlock':if (llen - 1 != index) {cm.setLocked(index, false, true);cm.moveColumn(index, llen - 1);this.grid.fireEvent('columnmove', index, llen - 1);} else {cm.setLocked(index, false);}break;default:return Ext.ux.grid.LockingHeaderGroupView.superclass.handleHdMenuClick.call(this, item);}return true;},handleHdDown: function (e, t) {Ext.ux.grid.LockingHeaderGroupView.superclass.handleHdDown.call(this, e, t);if (this.grid.enableColLock !== false) {if (Ext.fly(t).hasClass('x-grid3-hd-btn')) {var hd = this.findHeaderCell(t),index = this.getCellIndex(hd),ms = this.hmenu.items, cm = this.cm;ms.get('lock').setDisabled(cm.isLocked(index));ms.get('unlock').setDisabled(!cm.isLocked(index));}}},syncHeaderHeight: function () {var hrow = Ext.fly(this.innerHd).child('tr', true),lhrow = Ext.fly(this.lockedInnerHd).child('tr', true);if (!hrow || !lhrow) {return;}hrow.style.height = 'auto';lhrow.style.height = 'auto';var hd = hrow.offsetHeight,lhd = lhrow.offsetHeight,height = Math.max(lhd, hd) + 'px';hrow.style.height = height;lhrow.style.height = height;},updateLockedWidth: function () {var lw = this.cm.getTotalLockedWidth(),tw = this.cm.getTotalWidth() - lw,csize = this.grid.getGridEl().getSize(true),lp = Ext.isBorderBox ? 0 : this.lockedBorderWidth,rp = Ext.isBorderBox ? 0 : this.rowBorderWidth,vw = (csize.width - lw - lp - rp) + 'px',so = this.getScrollOffset();if (!this.grid.autoHeight) {//多页签时,隐藏的页签高度不能为0,否则会导致表体不见if (csize.height - this.mainHd.getHeight() == 0) {var vh = (this.grid.getGridEl().dom.style.height - this.mainHd.getHeight()) + 'px';this.lockedScroller.dom.style.height = vh;this.scroller.dom.style.height = vh;} else {var vh = (csize.height - this.mainHd.getHeight()) + 'px';this.lockedScroller.dom.style.height = vh;this.scroller.dom.style.height = vh;}}this.lockedWrap.dom.style.width = (lw + rp) + 'px';this.scroller.dom.style.width = vw;this.mainWrap.dom.style.left = (lw + lp + rp) + 'px';if (this.innerHd) {this.lockedInnerHd.firstChild.style.width = lw + 'px';this.lockedInnerHd.firstChild.firstChild.style.width = lw + 'px';this.innerHd.style.width = vw;this.innerHd.firstChild.style.width = (tw + rp + so) + 'px';this.innerHd.firstChild.firstChild.style.width = tw + 'px';}if (this.mainBody) {this.lockedBody.dom.style.width = (lw + rp) + 'px';this.mainBody.dom.style.width = (tw + rp) + 'px';}}
});

这个文件现在在项目(Ext3.4)上使用,需要注意,这里只提供js文件,需要导入ux.css样式表;

下面给出示例:

//同步获取节点数据
var ssnAry = eval("(" + $.ajax({url: "PdmP029.csx?tag=GetSsnJsonList",async: false,data: {ssnId: '41'}}).responseText + ")");
//使用RowSelectionModel
var sm = new Ext.grid.RowSelectionModel();
//定义ColumnModel
var columns = [{header: "内码",dataIndex: "MSI_IDS",hidden: true,locked: true}, {header: "船号",dataIndex: "PROJ_NO",width: sm_width,renderer: projStore.toValue,locked: true}, {header: "作业对象类型",dataIndex: "OBJ_TYPE",width: md_width,locked: true}, {header: "作业对象编码",dataIndex: "OBJ_CODE",width: md_width,locked: true}, {header: "搭载开始时间",dataIndex: "C_BEGIN_DATE",width: md_width,xtype: 'datecolumn',format: Date.patterns.ISO8601Short,locked: true}, {header: "差距",dataIndex: "C_CYC",width: md_width,renderer: function (v) {if (v == '-0') {return '0';}return v;},locked: true}
];
//定义fields
var fields = ['PROJ_NO', 'OBJ_TYPE', 'OBJ_CODE', 'MSI_IDS', 'C_BEGIN_DATE', 'C_CYC', 'FLAG'];
//定义多表头
var hgroup = [[{header: '基本信息',align: 'center',rowspan: 1,colspan: 6}]];
//节点循环,动态生成字段fields,列模型columns
for (var i = 0; i < ssnAry.data.length; i++) {columns.push({header: '内码',dataIndex: "MSI_ID_" + ssnAry.data[i].id,hidden: true});columns.push({header: '中日程计划开始',dataIndex: "OBEGIN_DATE_" + ssnAry.data[i].id,xtype: 'datecolumn',format: Date.patterns.ISO8601Short,width: xs_width,hidden: true});columns.push({header: '中日程计划结束',dataIndex: "OEND_DATE_" + ssnAry.data[i].id,xtype: 'datecolumn',format: Date.patterns.ISO8601Short,width: xs_width,hidden: true});columns.push({header: '计划开始',dataIndex: "BEGIN_DATE_" + ssnAry.data[i].id,xtype: 'datecolumn',format: Date.patterns.ISO8601Short,width: md_width,editor: new ef.DateField({format: Date.patterns.ISO8601Short})});columns.push({header: '计划结束',dataIndex: "END_DATE_" + ssnAry.data[i].id,xtype: 'datecolumn',format: Date.patterns.ISO8601Short,width: md_width,editor: new ef.DateField({format: Date.patterns.ISO8601Short})});fields.push("MSI_ID_" + ssnAry.data[i].id);fields.push("CYC_" + ssnAry.data[i].id);fields.push("STATE_" + ssnAry.data[i].id);fields.push("FLOAT_CYC_" + ssnAry.data[i].id);fields.push("OBEGIN_DATE_" + ssnAry.data[i].id);fields.push("OEND_DATE_" + ssnAry.data[i].id);fields.push({name: "ACTUAL_BEGIN_" + ssnAry.data[i].id,type: "date",dateFormat: Date.patterns.ISO8601Short});fields.push("ACTUAL_END_" + ssnAry.data[i].id);fields.push({name: "BEGIN_DATE_" + ssnAry.data[i].id,type: "date",dateFormat: Date.patterns.ISO8601Short});fields.push({name: "END_DATE_" + ssnAry.data[i].id,type: "date",dateFormat: Date.patterns.ISO8601Short});hgroup[0].push({header: ssnAry.data[i].cvalue,align: 'center',rowspan: 2,colspan: 5});
}
//定义数据集
var store = new Ext.data.JsonStore({url: "MON15.csx?tag=GetJsonList",fields: fields,pruneModifiedRecords: true,root: "data",id: '',totalProperty: "totalCount",listeners: {beforeload: function (v) {if (versionid_cmb.getValue() == '') {prityTip("提示", '请先选择计划!');return false;}var p = {proj_no: proj_cmb.getValue(),obj_code: obj_code_txt.getValue(),versionid: versionid_cmb.getValue(),limit: pagebar.pageSize,page: "1",show_act: show_act.getValue()};Ext.apply(Ext.version.startsWith('3') ? v.baseParams : v.proxy.extraParams, p);},load: function (store, records) {SetCellBg();}}});
//定义LockingColumnModel
var cm = new Ext.ux.grid.LockingColumnModel({columns: columns,isCellEditable: function (colIndex, rowIndex) {//如果本记录字段FLAG==实际,那么本行记录不允许编辑var record = store.getAt(rowIndex);if (record.get('FLAG') == '实际') {return false;}return Ext.grid.ColumnModel.prototype.isCellEditable.call(this, colIndex, rowIndex);},listeners: {hiddenchange: function () {grid.getView().updateHeaders();}}});//分页工具栏
var pagebar = new prj.util.PagingToolbar(store, 500, true);
//grid定义
var grid = new Ext.RSEGrid({id: 'MON15_grid',loadMask: true,store: store,bbar: pagebar,sm: sm,cm: cm,region: 'center',margins: '3 3 3 3',border: 1,split: true,tbar: tbar,listeners: {afteredit: function (e) {},sortchange: function () {}},view: new Ext.ux.grid.LockingHeaderGroupView({grows: hgroup,getRowClass: function (record, rowIndex, rowParams, store) {if (record.data.FLAG == '实际') {return 'row-bg-lemonchiffon';}}})});

效果图:

写在后面的话: 左右对不齐的BUG修复方法,使用jQuery暴力修改,因此需要引入jQuery.js.

从CSDN找到了多表头锁定控件,到修改BUG,再到生产中实际运用.关键还是源码源码源码.

这个控件还剩下一个小尾巴BUG,就是暂时还不支持checkboxSelectionModel.

本篇博客将会继续更新.

Ext3.x版本锁定列和多表头的插件,BUG修复版相关推荐

  1. Ext3.x版本锁定列和多表头的插件

    Ext3.x版本的锁定列和多表头都是通过插件实现,但是这两个插件不能一起工作,而实际上这样的业务也是存在的,即在多表头的情况下也需要锁定列. 在Ext的论坛上有很多这样的需求,都没有一个解决方案,除了 ...

  2. fastjson android 版本,fastjson 1.1.58.android 发布,Bug 修复版本

    fastjson 1.1.58.android 发布,Bug 修复版本 这里有新鲜出炉的精品教程,程序狗速度看过来! fastjson JSON 解析器 fastjson 是一个性能很好的 Java ...

  3. easyExcel导入导出(列锁定单元格、表头合并、导出类型限制、锁定单元格增加底色、设置密码、隐藏列等)

    easyexcel官网文档:https://www.yuque.com/easyexcel/doc/easyexcel easyexcel {maven 版本} GitHub网址:https://gi ...

  4. antd 设置表头属性_纯css实现固定表头和锁定列

    ### table 表格是一个网站很常用的元素,用以展示大量的数据.在处理表格时,通常会加入许多功能,如斑马线.选中高亮.固定表头.锁定列等等,本篇文章主要介绍如何单纯的使用css实现固定行或列的功能 ...

  5. Ext实现表格列头冻结效果,锁定列头

    这段时间我们的项目中采用了Coolite来做富客户端,Coolite目前的版本是0.8.1,它所采用的Ext版本是2.2.1,因此下面主要是说明在这个版本下所实现的锁定列和多行表头,需要说明的是Ext ...

  6. UWP Composition API - 锁定列的FlexGrid

    原文:UWP Composition API - 锁定列的FlexGrid 需求是第一列锁定,那么怎么让锁定列不跟着滚动条向做移动呢? 其实很简单,让锁定列跟scrollviewer的滚动做反方向移动 ...

  7. Vue目录结构与Yarn.lock 的版本锁定

    参考:Vue目录结构与Yarn.lock 的版本锁定 - 知乎 Vue目录结构与Yarn.lock 的版本锁定 一.Vue3.0目录结构图总览 刚看到vue的目录,一开始不知道该目录是什么含义,目录里 ...

  8. bootstrap Table实现锁定列不滚动功能

    当表格中的数据列比较多,各列的数据宽度大于页面的可用显示宽度时,就会出现横向滚动条,向右滚动时,左侧的数据列会隐藏,有时左侧列数据标识数据,隐藏了不方便数据查看,所以这时可以指定左侧的特定几列不进行滚 ...

  9. yarn降版本_npm依赖版本锁定和管理

    前几天测试的时候遇到个问题,测试的时候出现依赖升级问题,由于测试同学是重新换了个机子,重装了环境,导致下载过程中依赖升级.npm带来便利的时候也带一些问题.如果您觉得比较啰嗦,直接看结果. 我们的vu ...

最新文章

  1. hive工作记录-20180513
  2. oracle如何获取异常,Oracle专题13之异常错误处理
  3. 93后阿里P7晒出工资单:原来是狠补了这个~真香
  4. express-generator——Express应用生成器贼快!
  5. 宿主机进程挂载到容器内_迄今为止最严重的容器逃逸漏洞:Docker cp命令漏洞分析(CVE201914271)...
  6. 【leetcode】41. First Missing Positive
  7. sql group by 取每组符合条件_SPL 简化 SQL 案例详解:计算各组前 N 行
  8. 你可能没听过的 Java 8 中的 10 个特性
  9. 我的Oracle 9i学习日志(11)--重做日志文件及练习
  10. 阿里云服务器Java+Mysql+Tomcat环境搭建(Mac OS下配置)
  11. Linux有问必答:如何查看Linux上程序或进程用到的库
  12. 携程基于云的软呼叫中心及客服平台架构实践\n
  13. jmeter压力测试的辅助插件和实操技巧
  14. 基于matlab的2ASK调制解调仿真
  15. 阿里软件测试工程师推荐|自动化测试——HTTP之URL
  16. php seekdir,readdir()
  17. 【程序人生】底层程序员,出局
  18. 宏观分析(美债利率、美元、黄金、石油、标普500的相关性)
  19. MacOS使用minicom工具配置华为交换机
  20. 公钥密码学中的三大难解数学问题

热门文章

  1. mov和mp4格式哪个好_在线视频格式转换器哪个好?求一个能转换多种格式的工具...
  2. oracle 数据泵导入导出 expdp/impdp
  3. 从零开始学badusb(2)--badusb调用命令行
  4. 自定义控件 - 收藏集 - 掘金
  5. VMware15 虚拟机下Win7 桥接模式、NAT模式无法联网处理
  6. 模拟数字时钟走字60次
  7. hid python uhf_简单的读写Python中的USB HID设备?
  8. pyecharts+Vue在显示图表
  9. Win10下解决Wireshark“没有找到接口”的问题
  10. axis在matlab中是什么意思_【日语学习】这些很日本风的词汇是什么意思,你都知道吗?...