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

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

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

效果如下图:

Ext3.x版本锁定列和多表头的插件,BUG修复版_第1张图片

文件:

/*!
 * Ext JS Library 3.3.0
 * Copyright(c) 2006-2010 Ext JS, Inc.
 * [email protected]
 * 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(
                '
', '
', '
{lockedHeader}
', '
{lockedBody}
', '
', '
', '
{header}
', '
{body}
', '
', '
 
', '
 
', '
' ); } if (!ts.gcell) { ts.gcell = new Ext.XTemplate('', '
', this.grid.enableHdMenu ? '' : '', '{value}
'); } 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.getColumnStyle var 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 || ' ' }); 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 rows this.fly(row).addClass(cls).setStyle(rowParams.tstyle); rowParams.cells = colBuffer.join(""); row.innerHTML = this.templates.rowInner.apply(rowParams); // Locked rows this.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';
				}
			}
		})
	});

效果图:

Ext3.x版本锁定列和多表头的插件,BUG修复版_第2张图片

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

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

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

本篇博客将会继续更新.

你可能感兴趣的:(extjs)