<link type="text/css" rel="stylesheet" href="../extjs4/resources/css/ext-all-neptune.css" target="_blank" rel="external nofollow" />
<link type="text/css" rel="stylesheet" href="../extjs4/resources/theme/ext-theme-neptune-all.css" target="_blank" rel="external nofollow" />
<script type="text/javascript" src="../extjs4/ext-all-debug.js"></script>
<script type="text/javascript" src="../MergeCellTable.js"></script>
<script type="text/javascript" src="../MergeCellPanel.js"></script>
<span style="font-size:18px;">Ext.onReady(function() {
Ext.create('Ext.data.Store', {
storeId:'simpsonsStore',
fields:['cat1', 'cat2', 'cat2_desc','name'],
data:{'items':[
{ "cat1": "分类1", "cat2": "分类21", "name": "名称1" },
{ "cat1": "分类1", "cat2": "分类22", "name": "名称1" },
{ "cat1": "分类1", "cat2": "分类22", "name": "名称2" },
{ "cat1": "分类1", "cat2": "分类23", "name": "名称1" },
{ "cat1": "分类2", "cat2": "分类21", "name": "名称1" },
{ "cat1": "分类2", "cat2": "分类21", "name": "名称2" },
{ "cat1": "分类2", "cat2": "分类22", "name": "名称1" },
{ "cat1": "分类2", "cat2": "分类23", "name": "名称1" },
{ "cat1": "分类2", "cat2": "分类24", "name": "名称1" },
{ "cat1": "分类2", "cat2": "分类24", "name": "名称2" },
{ "cat1": "分类2", "cat2": "分类24", "name": "名称3" },
{ "cat1": "分类3", "cat2": "分类21", "name": "名称1" },
{ "cat1": "分类3", "cat2": "分类22", "name": "名称1" },
{ "cat1": "分类3", "cat2": "分类23", "name": "名称1" },
{ "cat1": "分类3", "cat2": "分类24", "name": "名称1" },
{ "cat1": "分类3", "cat2": "分类24", "name": "名称2" },
{ "cat1": "分类3", "cat2": "分类25", "name": "名称1" }
]},
proxy: {
type: 'memory',
reader: {
type: 'json',
root: 'items'
}
}
});
Ext.create('Ext.ux.grid.MergeCellPanel', {
title: 'Simpsons',
renderTo: Ext.getBody(),
viewConfig: {
mergeColumns: 'cat1>cat2|cat2_desc'
},
store: Ext.data.StoreManager.lookup('simpsonsStore'),
columns: [
{ text: '分类1', dataIndex: 'cat1' },
{ text: '分类2', dataIndex: 'cat2' },
{ text: '分类2描述', dataIndex: 'cat2_desc', flex: 1 },
{ text: '名称', dataIndex: 'name' },
{ text: 'C1' },
{ text: 'C2' },
{ text: 'C3' },
{ text: 'C4' },
{ text: 'C5' }
]
});
});
</span>
核心文件如下:
MergeCellTable.js
Ext.define('Ext.ux.view.MergeCellTable', {
extend: 'Ext.view.Table',
alias: ['widget.mergecelltableview'],
type: 'mergecelltableview',
baseCls: Ext.baseCSSPrefix + 'mergegrid-view',
stripeRows: false,
separator: '>', // 不同级别分割符
sameLevelSeparator: '|', // 同级别分割符
__rowspans: null, // 缓存要合并的列中每个单元格的rowspan, refresh时会删除重新计算
__merge_columns: null, // 缓存要合并的列, refresh时会删除重新获取
cellTpl: [ // 增加了rowspan属性
'<td role="gridcell" class="{tdCls}" {tdAttr} id="{[Ext.id()]}" rowspan="{rowspan}" <tpl if="hidden">style="display:none"</tpl>>',
'<div {unselectableAttr} class="' + Ext.baseCSSPrefix + 'grid-cell-inner {innerCls}"',
'style="height:auto;text-align:{align};<tpl if="style">{style}</tpl>">{value}</div>',
'</td>', {
priority: 0
}
],
renderCell: function(column, record, recordIndex, columnIndex, out) { // 添加rowspan与隐藏td
var me = this,
rowspans = me.getRowspans();
var rowspan = (rowspans[recordIndex] || {})[column.dataIndex];
cellValues = me.cellValues,
cellValues.rowspan = rowspan;
// cellTpl采用了display:none而不是不生成td, 因为若不生成td在使用rowediting时会出错
cellValues.hidden = rowspan === 0;
me.callParent(arguments);
},
onAdd: function(store, records, index, cfg) {
this.refresh();
},
onRemove: function() {
this.refresh();
},
onUpdate: function() {
this.refresh();
},
refresh: function() {
var me = this;
// 刷新时要重新计算rowspan
delete me.__rowspans;
delete me.__merge_columns;
me.callParent(arguments);
},
getRowspans: function() {
var me = this;
// 已经计算过直接返回
var rowspans = me.__rowspans;
if (rowspans != null) {
return rowspans;
}
// 计算rowspan
rowspans = [];
var store = me.dataSource,
mergeColumns = me.getMergeColumns();
var setSameLevelRowspan = function(rowspans, rowIndex, columns, rowspan) {// 设置同级其他列的rowspan
var i, temp, len = columns.length;
for (i = 1; i < len; i++) {
temp = columns[i];
rowspans[rowIndex][temp] = rowspan;
}
};
var calculateRowspans = function(rowspans, mergeColumns, currentColumnIndex, store, from, to) {
if (currentColumnIndex >= mergeColumns.length) {
return;
}
var columns = mergeColumns[currentColumnIndex],
i, current, prev, mergeStart = 0;
var column = columns[0];
try {
for (i = from; i < to + 1; i++) {
current = store.getAt(i).get(column);
if (current !== prev) {
rowspans[i] = rowspans[i] || {};
rowspans[i][column] = 1;
setSameLevelRowspan(rowspans, i, columns, 1);
// 递归获取子列
if (prev != null) {
calculateRowspans(rowspans, mergeColumns, currentColumnIndex + 1, store, mergeStart, i - 1);
}
prev = current;
mergeStart = i;
} else {
rowspans[mergeStart][column]++;
setSameLevelRowspan(rowspans, mergeStart, columns, rowspans[mergeStart][column]);
rowspans[i] = rowspans[i] || {};
rowspans[i][column] = 0;
setSameLevelRowspan(rowspans, i, columns, 0);
}
}
if (i > mergeStart) {
calculateRowspans(rowspans, mergeColumns, currentColumnIndex + 1, store, mergeStart, i - 1);
}
} catch(e) {
if (console) {
console.error(e);
}
}
};
calculateRowspans(rowspans, mergeColumns, 0, store, 0, store.data.length - 1);
// 缓存
me.__rowspans = rowspans;
return rowspans;
},
getMergeColumns: function() {
var me = this;
var columns = me.__merge_columns;
if (columns != null) {
return columns;
}
// 未配置时直接返回
var mergeColumns = me.mergeColumns;
if (Ext.isEmpty(mergeColumns)) {
return [];
}
// 转换合并规则
var separator = me.separator,
sameLevelSeparator = me.sameLevelSeparator;
columns = mergeColumns.split(separator);
var i, len = columns.length;
for (i = 0; i < len; i++) {
columns[i] = columns[i].split(sameLevelSeparator);
}
// 缓存
me.__merge_columns = columns;
return columns;
}
});
MergeCellPanel.js
Ext.define('Ext.ux.grid.MergeCellPanel', {
extend: 'Ext.grid.Panel',
alias: ['widget.mergecellgrid'],
viewType: 'mergecelltableview',
initComponent: function() {
var me = this;
Ext.apply(me, {
selType: 'cellmodel', // 按单元格选择
sortableColumns: false, // 禁止排序
columnLines: true, // 显示网格线
rowLines: true, // 显示网格线
trackMouseOver: false // 禁止跟踪鼠标变色
});
me.callParent();
}
});