首页 > 代码库 > Ext3.x版本锁定列和多表头的插件

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

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

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

效果如下图:

  技术分享

 

使用方法:

this.view = new Ext.ux.grid.LockingHeaderGroupView({grows : [[{},{},{},{"align":"center","colspan":2,"header":"合并后名称"},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}]]});

文件:

/*! * 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 class="x-grid3-scroller"><div class="x-grid3-body" style="{lstyle}">{lockedBody}</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="http://www.mamicode.com/#" class="x-grid3-focus" tabIndex="-1"></a></div>‘,                    ‘</div>‘,                    ‘<div class="x-grid3-resize-marker">&#160;</div>‘,                    ‘<div class="x-grid3-resize-proxy">&#160;</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="http://www.mamicode.com/#"></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 = ‘&#160;‘;                }                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() || [‘&#160;‘, ‘&#160;‘];        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:‘&#160;‘});        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){                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;            for(var i=0;i<grows.length;i++){                var group = grows[i];                var colspan = group.colspan || 1;                index = i+colspan;                if(lockIndex >= (index-1)){                    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 || ‘&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() || [‘&#160;‘, ‘&#160;‘];        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);    },    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‘;        }    }});Ext.ux.grid.LockingColumnModel = Ext.extend(Ext.grid.ColumnModel, {    /**     * Returns true if the given column index is currently locked     * @param {Number} colIndex The column index     * @return {Boolean} True if the column is locked     */    isLocked : function(colIndex){        return this.config[colIndex].locked === true;    },    /**     * Locks or unlocks a given column     * @param {Number} colIndex The column index     * @param {Boolean} value True to lock, false to unlock     * @param {Boolean} suppressEvent Pass false to cause the columnlockchange event not to fire     */    setLocked : function(colIndex, value, suppressEvent){        if (this.isLocked(colIndex) == value) {            return;        }        this.config[colIndex].locked = value;        if (!suppressEvent) {            this.fireEvent(‘columnlockchange‘, this, colIndex, value);        }    },    /**     * Returns the total width of all locked columns     * @return {Number} The width of all locked columns     */    getTotalLockedWidth : function(){        var totalWidth = 0;        for (var i = 0, len = this.config.length; i < len; i++) {            if (this.isLocked(i) && !this.isHidden(i)) {                totalWidth += this.getColumnWidth(i);            }        }        return totalWidth;    },    /**     * Returns the total number of locked columns     * @return {Number} The number of locked columns     */    getLockedCount : function() {        var len = this.config.length;        for (var i = 0; i < len; i++) {            if (!this.isLocked(i)) {                return i;            }        }        //if we get to this point all of the columns are locked so we return the total        return len;    },    /**     * Moves a column from one position to another     * @param {Number} oldIndex The current column index     * @param {Number} newIndex The destination column index     */    moveColumn : function(oldIndex, newIndex){        var oldLocked = this.isLocked(oldIndex),            newLocked = this.isLocked(newIndex);        if (oldIndex < newIndex && oldLocked && !newLocked) {            this.setLocked(oldIndex, false, true);        } else if (oldIndex > newIndex && !oldLocked && newLocked) {            this.setLocked(oldIndex, true, true);        }        Ext.ux.grid.LockingColumnModel.superclass.moveColumn.apply(this, arguments);    }});

 

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