原创,转载请标明出处。
Extjs自带的FormLayout布局若要使用多列,需要自己做tpl或者使用HTML+renderTo,很麻烦。而TableLyout只支持Flow排列,且用于FormPanel中不能正确显示Label,等等,两者皆不适合自由的table+form布局需求。FormTablLayout将解决该问题。
My.layout.FormTableLayout = FormLayout + TableLayout
增强项:可指定显示位置….等.
使用方法:适用于FormLayout/TableLayout所适用场合,指定layout为formtable即可。
1) 基本功能:
a) 指定表格列数(行数将根据内容自动添加)
b) 横、纵向融合
c) 一个单元格内容
d) 在一个单元格中显示多项内容
e) 指定显示位置
f) 支持显示form field
g) 单元格内容显示模板可设置
2) 使用范例
varitems=[
{
html:'横向融合',
row:3,
col:0,
colspan:2
},{
html:'-------我的地盘--------',
row:2,
col:1
},{
html:'========我也来===========',
row:2,
col:1
}……
vartableForm=newExt.FormPanel({
layout:'formtable',//使用FormTableLayout
style:'height:100%',
title:'TableForm',
items:items
});
3) 效果
/**
* @class My.ui.FormTableLayout
* @extends Ext.layout.ContainerLayout
* @author zfzheng
*
*
*
* FormTableLayout= FormLayout + TableLayout
*
* 适用于FormLayout/TableLayout适用的场合。
* 除拥有FormLayout/TableLayout特征外,还具有指定单元格显示功能:
* 设置autoFlow为true,根据items项的row/col属性定位单元格排列。
* 可以在同一单元格内显示多项item(指定row/col)为同一位置即可,按在原数组中的顺序排列。
* 注:
* 1)若autoFlow为true,items项没有row或col属性的均不被显示。
* 2)若某item的row/col设置在被融合了的单元格位置,则该项不被显示。
*
var items=[
{
html:'横向融合',
row:3,
col:0,
colspan:2
},{
html:'-------我的地盘--------',
row:2,
col:1
},{
html:'========我也来===========',
row:2,
col:1
},{
html:'~~~~~~~~~真挤啊~~~~~~~~~~',
row:2,
col:1
},{
html:'非第一单元格起填充',
row:0,
col:1
},{
html:'纵向融合',
row:1,
col:0,
rowspan:2
},{
html:'Hello,how are you?正常Cell',
row:1,
col:1
},{
name:'count',
fieldLabel:'件数',
value:'1',
xtype:'numberfield',
row:4,
col:0
},{
name:'logTime',
fieldLabel:'编制日期',
value:'',
xtype:'datefield',
row:4,
col:1
}
]
var tableForm=new Ext.FormPanel({
layout: 'formtable', //使用FormTableLayout
style: 'height:100%',
title: 'Table Form',
items: items
});
tableForm.renderTo(Ext.getBody());
*
*
*/
My.layout.FormTableLayout=Ext.extend(Ext.layout.ContainerLayout,{
// private
monitorResize:false,
/**
* Table列数
*/
columns:2,
/**
* 是否自动排列
* 若为true,则按行方向自动排列。
* 若为false,则根据item指定的[row,col]排列,无row/col属性的项被忽略显示。
*/
autoFlow:false,
/**
* Form Element显示模板
*/
fieldTpl:
new Ext.Template(
'<div class="x-form-item {5}" tabIndex="-1">',
'<label for="{0}" style="{2}" class="x-form-item-label">{1}{4}</label>',
'<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
'</div><div class="{6}"></div>',
'</div>'
),
/**
* 普通内容(非Form Element)显示模板
*/
cellContentTpl:new Ext.Template('<div class="x-form-item-label">{0}</div>'),
// private
setContainer : function(ct){
My.layout.FormTableLayout.superclass.setContainer.call(this, ct);
this.currentRow = 0;
this.currentColumn = 0;
this.spanCells = [];
this.maxCreatedRowIndex=-1;
},
// private
onLayout : function(ct, target){
var cs = ct.items.items, len = cs.length;
if(!this.table){
//target.addClass('x-table-layout-ct');
this.table = target.createChild(
{tag:'table', cls:'x-table-layout', border:1, cellspacing:3, cn: {tag: 'tbody'}}, null, true);
if(!this.autoFlow){
var items=[];
for(var i=0;i<len;i++){
//如果设置了autoFlow为true,则无row、col属性的字段将无法显示。
if(typeof cs[i].row!='undefined' && typeof cs[i].col!='undefined'){
cs[i].__index=i;
items.push(cs[i]);
}
}
ct.items.items=items.sort(function(item1,item2){
if(item1.row==item2.row){
if(item1.col==item2.col){
return item1.__index-item2.__index;
}
return item1.col-item2.col;
}
return item1.row-item2.row;
});
}
this.renderAll(ct, target);
}
},
// private
createRow : function(index){
var row = this.table.tBodies[0].childNodes[index];
if(!row){
row = document.createElement('tr');
this.table.tBodies[0].appendChild(row);
this.maxCreatedRowIndex=Math.max(this.maxCreatedRowIndex,index);
}
return row;
},
// private
getRow : function(index){
if(!this.autoFlow){
//确保创建index之前的row
for(var i=Math.max(0,this.maxCreatedRowIndex+1);i<index;i++){
this.createRow(i);
}
}
return this.createRow(index);
},
// private
getCellAt : function(c,row,col){
if(this.spanCells[col] && this.spanCells[col][row]){
return null;
}
//计算之前有多少spanCells
var spanCount=0;
for(var i=0;i<col;i++){
if(this.spanCells[i] && this.spanCells[i][row]){
spanCount++;
}
}
var r=this.getRow(row);
var cell=r.childNodes[col-spanCount];
if(cell){
return cell;
}
this.currentRow=row;
//预设置nextCell之前的初始列索引
this.currentColumn=spanCount-1;
for(var i=spanCount;i<=col;i++){
this.getNextCell(c);
}
return r.childNodes[col-spanCount];
},
// private
getNextCell : function(c){
var td = document.createElement('td'), row, colIndex;
if(!this.columns){
row = this.getRow(0);
}else {
colIndex = this.currentColumn;
if(colIndex !== 0 && (colIndex % this.columns === 0)){
this.currentRow++;
colIndex = (c.colspan || 1);
}else{
colIndex += (c.colspan || 1);
}
//advance to the next non-spanning row/col position
var cell = this.getNextNonSpan(colIndex, this.currentRow);
this.currentColumn = cell[0];
if(cell[1] != this.currentRow){
this.currentRow = cell[1];
if(c.colspan){
this.currentColumn += c.colspan - 1;
}
}
row = this.getRow(this.currentRow);
}
if(c.colspan){
td.colSpan = c.colspan;
}
td.className = 'x-table-layout-cell';
if(c.rowspan){
td.rowSpan = c.rowspan;
var rowIndex = this.currentRow, colspan = c.colspan || 1;
//track rowspanned cells to add to the column index during the next call to getNextCell
for(var r = rowIndex+1; r < rowIndex+c.rowspan; r++){
for(var col=this.currentColumn-colspan+1; col <= this.currentColumn; col++){
if(!this.spanCells[col]){
this.spanCells[col] = [];
}
this.spanCells[col][r] = 1;
}
}
}
row.appendChild(td);
return td;
},
// private
getNextNonSpan: function(colIndex, rowIndex){
var c = (colIndex <= this.columns ? colIndex : this.columns), r = rowIndex;
for(var i=c; i <= this.columns; i++){
if(this.spanCells[i] && this.spanCells[i][r]){
if(++c > this.columns){
//exceeded column count, so reset to the start of the next row
return this.getNextNonSpan(1, ++r);
}
}else{
break;
}
}
return [c,r];
},
// private
renderItem : function(c, position, target){
//target is form or other container
if(c && !c.rendered ){
var td=this.autoFlow?this.getNextCell(c):this.getCellAt(c,c.row,c.col);
if(!td){
//cell at[row,col] is spanCell,不渲染。(row,col设置不正确)
return;
}
if(c.isFormField && c.inputType != 'hidden'){
var args = [
c.id, c.fieldLabel,
c.labelStyle||this.labelStyle||'',
this.elementStyle||'',
typeof c.labelSeparator == 'undefined' ? this.labelSeparator : c.labelSeparator,
(c.itemCls||this.container.itemCls||'') + (c.hideLabel ? ' x-hide-label' : ''),
c.clearCls || 'x-form-clear-left'
];
if(typeof position == 'number'){
position = target.dom.childNodes[position] || null;
}
if(position){
//this.fieldTpl.insertBefore(position, args);
this.fieldTpl.append(td, args);
}else{
//this.fieldTpl.append(target, args);
this.fieldTpl.append(td, args);
}
c.render('x-form-el-'+c.id);
}else {
//My.layout.FormTableLayout.superclass.renderItem.apply(this, arguments);
this.cellContentTpl.append(td,[c.text||c.html||c.value]);
}
}
},
// private
isValidParent : function(c, target){
return true;
}
});
Ext.Container.LAYOUTS['formtable'] = My.layout.FormTableLayout;
* @class My.ui.FormTableLayout
* @extends Ext.layout.ContainerLayout
* @author zfzheng
*
*
*
* FormTableLayout= FormLayout + TableLayout
*
* 适用于FormLayout/TableLayout适用的场合。
* 除拥有FormLayout/TableLayout特征外,还具有指定单元格显示功能:
* 设置autoFlow为true,根据items项的row/col属性定位单元格排列。
* 可以在同一单元格内显示多项item(指定row/col)为同一位置即可,按在原数组中的顺序排列。
* 注:
* 1)若autoFlow为true,items项没有row或col属性的均不被显示。
* 2)若某item的row/col设置在被融合了的单元格位置,则该项不被显示。
*
var items=[
{
html:'横向融合',
row:3,
col:0,
colspan:2
},{
html:'-------我的地盘--------',
row:2,
col:1
},{
html:'========我也来===========',
row:2,
col:1
},{
html:'~~~~~~~~~真挤啊~~~~~~~~~~',
row:2,
col:1
},{
html:'非第一单元格起填充',
row:0,
col:1
},{
html:'纵向融合',
row:1,
col:0,
rowspan:2
},{
html:'Hello,how are you?正常Cell',
row:1,
col:1
},{
name:'count',
fieldLabel:'件数',
value:'1',
xtype:'numberfield',
row:4,
col:0
},{
name:'logTime',
fieldLabel:'编制日期',
value:'',
xtype:'datefield',
row:4,
col:1
}
]
var tableForm=new Ext.FormPanel({
layout: 'formtable', //使用FormTableLayout
style: 'height:100%',
title: 'Table Form',
items: items
});
tableForm.renderTo(Ext.getBody());
*
*
*/
My.layout.FormTableLayout=Ext.extend(Ext.layout.ContainerLayout,{
// private
monitorResize:false,
/**
* Table列数
*/
columns:2,
/**
* 是否自动排列
* 若为true,则按行方向自动排列。
* 若为false,则根据item指定的[row,col]排列,无row/col属性的项被忽略显示。
*/
autoFlow:false,
/**
* Form Element显示模板
*/
fieldTpl:
new Ext.Template(
'<div class="x-form-item {5}" tabIndex="-1">',
'<label for="{0}" style="{2}" class="x-form-item-label">{1}{4}</label>',
'<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
'</div><div class="{6}"></div>',
'</div>'
),
/**
* 普通内容(非Form Element)显示模板
*/
cellContentTpl:new Ext.Template('<div class="x-form-item-label">{0}</div>'),
// private
setContainer : function(ct){
My.layout.FormTableLayout.superclass.setContainer.call(this, ct);
this.currentRow = 0;
this.currentColumn = 0;
this.spanCells = [];
this.maxCreatedRowIndex=-1;
},
// private
onLayout : function(ct, target){
var cs = ct.items.items, len = cs.length;
if(!this.table){
//target.addClass('x-table-layout-ct');
this.table = target.createChild(
{tag:'table', cls:'x-table-layout', border:1, cellspacing:3, cn: {tag: 'tbody'}}, null, true);
if(!this.autoFlow){
var items=[];
for(var i=0;i<len;i++){
//如果设置了autoFlow为true,则无row、col属性的字段将无法显示。
if(typeof cs[i].row!='undefined' && typeof cs[i].col!='undefined'){
cs[i].__index=i;
items.push(cs[i]);
}
}
ct.items.items=items.sort(function(item1,item2){
if(item1.row==item2.row){
if(item1.col==item2.col){
return item1.__index-item2.__index;
}
return item1.col-item2.col;
}
return item1.row-item2.row;
});
}
this.renderAll(ct, target);
}
},
// private
createRow : function(index){
var row = this.table.tBodies[0].childNodes[index];
if(!row){
row = document.createElement('tr');
this.table.tBodies[0].appendChild(row);
this.maxCreatedRowIndex=Math.max(this.maxCreatedRowIndex,index);
}
return row;
},
// private
getRow : function(index){
if(!this.autoFlow){
//确保创建index之前的row
for(var i=Math.max(0,this.maxCreatedRowIndex+1);i<index;i++){
this.createRow(i);
}
}
return this.createRow(index);
},
// private
getCellAt : function(c,row,col){
if(this.spanCells[col] && this.spanCells[col][row]){
return null;
}
//计算之前有多少spanCells
var spanCount=0;
for(var i=0;i<col;i++){
if(this.spanCells[i] && this.spanCells[i][row]){
spanCount++;
}
}
var r=this.getRow(row);
var cell=r.childNodes[col-spanCount];
if(cell){
return cell;
}
this.currentRow=row;
//预设置nextCell之前的初始列索引
this.currentColumn=spanCount-1;
for(var i=spanCount;i<=col;i++){
this.getNextCell(c);
}
return r.childNodes[col-spanCount];
},
// private
getNextCell : function(c){
var td = document.createElement('td'), row, colIndex;
if(!this.columns){
row = this.getRow(0);
}else {
colIndex = this.currentColumn;
if(colIndex !== 0 && (colIndex % this.columns === 0)){
this.currentRow++;
colIndex = (c.colspan || 1);
}else{
colIndex += (c.colspan || 1);
}
//advance to the next non-spanning row/col position
var cell = this.getNextNonSpan(colIndex, this.currentRow);
this.currentColumn = cell[0];
if(cell[1] != this.currentRow){
this.currentRow = cell[1];
if(c.colspan){
this.currentColumn += c.colspan - 1;
}
}
row = this.getRow(this.currentRow);
}
if(c.colspan){
td.colSpan = c.colspan;
}
td.className = 'x-table-layout-cell';
if(c.rowspan){
td.rowSpan = c.rowspan;
var rowIndex = this.currentRow, colspan = c.colspan || 1;
//track rowspanned cells to add to the column index during the next call to getNextCell
for(var r = rowIndex+1; r < rowIndex+c.rowspan; r++){
for(var col=this.currentColumn-colspan+1; col <= this.currentColumn; col++){
if(!this.spanCells[col]){
this.spanCells[col] = [];
}
this.spanCells[col][r] = 1;
}
}
}
row.appendChild(td);
return td;
},
// private
getNextNonSpan: function(colIndex, rowIndex){
var c = (colIndex <= this.columns ? colIndex : this.columns), r = rowIndex;
for(var i=c; i <= this.columns; i++){
if(this.spanCells[i] && this.spanCells[i][r]){
if(++c > this.columns){
//exceeded column count, so reset to the start of the next row
return this.getNextNonSpan(1, ++r);
}
}else{
break;
}
}
return [c,r];
},
// private
renderItem : function(c, position, target){
//target is form or other container
if(c && !c.rendered ){
var td=this.autoFlow?this.getNextCell(c):this.getCellAt(c,c.row,c.col);
if(!td){
//cell at[row,col] is spanCell,不渲染。(row,col设置不正确)
return;
}
if(c.isFormField && c.inputType != 'hidden'){
var args = [
c.id, c.fieldLabel,
c.labelStyle||this.labelStyle||'',
this.elementStyle||'',
typeof c.labelSeparator == 'undefined' ? this.labelSeparator : c.labelSeparator,
(c.itemCls||this.container.itemCls||'') + (c.hideLabel ? ' x-hide-label' : ''),
c.clearCls || 'x-form-clear-left'
];
if(typeof position == 'number'){
position = target.dom.childNodes[position] || null;
}
if(position){
//this.fieldTpl.insertBefore(position, args);
this.fieldTpl.append(td, args);
}else{
//this.fieldTpl.append(target, args);
this.fieldTpl.append(td, args);
}
c.render('x-form-el-'+c.id);
}else {
//My.layout.FormTableLayout.superclass.renderItem.apply(this, arguments);
this.cellContentTpl.append(td,[c.text||c.html||c.value]);
}
}
},
// private
isValidParent : function(c, target){
return true;
}
});
Ext.Container.LAYOUTS['formtable'] = My.layout.FormTableLayout;