您的位置:首页 > Web前端 > JavaScript

一个基于Extjs 3的支持单元格合并的面板

2016-07-11 14:45 519 查看
这是一个支持单元格合并的Panel,不是基于 GridPanel,而是基于 Ext.Panel 这个类。内部表格实现是基于 html 的 <table> 标签的。支持增删。效果图如下:



代码依赖 Extjs 3 和 JQuery,请自行添加。废话少说,上代码。

js 部分(直接调用 tableTag() 方法就可以显示出效果了):

Array.prototype.remove=function(dx)
{
if(isNaN(dx) || dx > this.length || dx < 0) return false;

for(var i=0,n=0;i<this.length;i++)
{
if(this[i] != this[dx]) this[n++] = this[i]
}
this.length -= 1
};

Ext.ns('Ext.ns.my.tag');

/**
* 这是一个可以自适应、自动合并的表格面板。此代码依赖 JQuery。
*/
Ext.ns.my.tag.TagTablePanel = Ext.extend(Ext.Panel,
{
/**
* 默认自动显示滚动条
*/
autoScroll : true,
/**
* 列标题。元素是 js 对象。下面是对象各个字段的意义:
*
* header:
* 		必输项;类型:文本。表示列标题
*
* width:
* 		可选项;类型:文本,正整数;单位:px。表示列宽。支持正整数,和'*', 'auto'。默认 '*'。
*
* textHorizontalAlignment:
* 		可选项;类型:文本。表示数据域文本水停靠位置。可选的值为:'left'、'center'、'justify'、
* 		'inherit'和'right'。默认不设定此项。
*
* renderer:
* 		可选项;函数。用于根据数据渲染显示。参数 (oldValue, rowNum, colNum, record)。返回一个
* 		HTML 字符串。默认照常显示数据文本。
*
* hidden:
* 		可选项;类型:布尔值。表示当前列是否隐藏。true 表示隐藏。默认 false(不隐藏)。
*/
colHeaders: [],
/**
* 要合并的列的序号,从 0 开始
*/
mergeCols: [],
/**
* 面板中的数据
*/
records: [],
/**
* 被点击的单元格所在的列号,默认 -1
*/
selectedColNumber: -1,
/**
* 被点击的单元格所在的行号,默认 -1
*/
selectedRowNumber: -1,
initComponent : function()
{
Ext.ns.my.tag.TagTablePanel.superclass.initComponent.call(this); // 调用父类的初始化方法

// 初始化表格和列标题
this.html = '<table id="' + this.id + '-table" class="table-tag-table">' + this.getHeaderHtml(this) + '</table>';
},
/**
* 单元格被点击时,执行的回调函数。
*
* 参数 _this:指 Panel 本身
* 参数 cell :指单元格所在的 Dom(td)
*/
cellClickCallback : function(_this, cell) { },
/**
* private
* 获取表示列宽的 CSS 文本。
*/
getColWidthStyleText: function(width)
{
var w = 'width: ';
if(width == undefined || width == '*') w += '*;';
else if(width == 'auto') w += 'auto;';
else w += (width + 'px;');

return w;
},
/**
* private
* 获取列标题所在行的 HTML 表示,用于生成列标题。
*
* 参数 _this:指 Panel 本身
*/
getHeaderHtml: function(_this)
{
var html = '<tr>', co = null, width = null;

var width = null; // 表示列宽的 CSS 样式文本

for(var i = 0; i < _this.colHeaders.length; i++)
{
co = _this.colHeaders[i];

if(co.hidden == true) continue; // 如果隐藏,就干脆不渲染。
width = _this.getColWidthStyleText(co.width);

html += ('<th class="table-tag-th" style="' + width + '">' + co.header + "</th>");
}
html += '</tr>';

return html;
},
/**
* 获取被选择的单元格的 ID(td 的 ID)。
* 如果没有选择到单元格,返回 false。
*/
getSelectedCellId: function()
{
return (this.selectedRowNumber < 0 || this.selectedColNumber < 0) ? false
: (this.id + '-row-' + this.selectedRowNumber + '-col-' + this.selectedColNumber);
},
/**
* private
* 一个自动合并单元格的方法。这个合并是根据数据实际值来的,而非渲染结果
*/
merge : function()
{
var mergeCols = this.mergeCols;
var records = this.records;

/**
* 按列合并单元格
*/
for(var t = 0; t < mergeCols.length; t++)
{
var col = mergeCols[t];
var theCell = $('#' + this.id + '-row-0-col-' + col);               // 注意行和列
var theRecord = records[0];
for(var i = 1; i < records.length; ++i)
{
var currCell = $('#' + this.id + '-row-' + i + '-col-' + col);  // 注意行和列
var currRecord = records[i];
if(currRecord[col] == theRecord[col] && currCell.is(":visible") )
{
var rowspan = theCell.attr('rowspan');
if(rowspan == undefined) rowspan = 1;

theCell.attr('rowspan', parseInt(rowspan) + 1);
currCell.hide();    // 注意行和列
}
else
{
theCell = currCell;
theRecord = currRecord;
}
}
}
},
/**
* 设置被选择的单元格中的文本值。
* 如果一个单元格是跨行的,那么意味着,它下面的几行数据相同,但是被隐藏了。这时,会同步更改相邻的相同数据。
*/
getSeletedValue:function()
{
var td = $("#" + (this.id + '-row-' + this.selectedRowNumber + '-col-' + this.selectedColNumber));

return (this.selectedRowNumber < 0 || this.selectedColNumber < 0) ? false : td.text();
},
/**
* 改变被选择的单元格中的文本值。
*
* 参数 value:新的值
*/
setSeletedValue:function(value)
{
// 先更改数据域中的响应数据,再重新进行渲染

var row = this.selectedRowNumber, col = this.selectedColNumber;

if(this.records.length <= 0) return false;

// 往下找相同的数据进行更改
var basev = this.records[row][col];
for(var i = row; i < this.records.length; i++)
{
if(this.records[i][col] == basev) this.records[i][col] = value;
else break;
}

// 往上找相同的数据进行更改
for(var i = row; i >= 0; i--)
{
if(this.records[i][col] == basev) this.records[i][col] = value;
else break;
}

this.selfAdapt(this); // 渲染
},
/**
* private
* 获取排序算法。重写这个方法时,请确保它应该被用于对数据进行分类
*
* 参数 _this:指 Panel 本身
*/
getSortAalgorithm: function(_this)
{
// 记录比较方法
var recordSort = function(a, b)
{
var mergeCols = _this.mergeCols;

for(var i = 0; i < mergeCols.length; i++)
{
var col = mergeCols[i];
if(a[col] > b[col]) return 1;
else if(a[col] < b[col]) return -1;
}

return 0;
};

return recordSort;
},
/**
* private
* 根据列编号获取单元格文本的水平居中值。在方法 selfAdapt 中调用,用于渲染表格数据域。
*/
getTextHorizontalAlignmentByCol: function(col, _this)
{
var t = _this.colHeaders[col].textHorizontalAlignment;
if(t == undefined) return '';
else return "text-align: " + t + ';';
},
/**
* private
* 获取渲染后的文本。
*/
getRenderedText: function(oldValue, row, col, record, _this)
{
var t = _this.colHeaders[col].renderer;
if(t == undefined) return oldValue;
else return t(oldValue, row, col, record);
},
/**
* private
* 判断单元格所在的列要不要隐藏。
*/
isHiddenColumn: function(col, _this)
{
var t = _this.colHeaders[col].hidden;

if(t == undefined || t == false) return false;
else return true;
},
/**
* private
* 自适应,也就是根据数据内容重新渲染。
*
* 参数 me:指 Panel 本身
*/
selfAdapt: function(me)
{
me.records.sort(me.getSortAalgorithm(me)); // 根据要被合并的列进行排序

var tbl = $('#' + me.id + '-table');  // 获取表格

tbl.children().remove(); // 移除表下面的所有行,等待添加。

$(me.getHeaderHtml(me)).appendTo(tbl); // 添加标题

/**
* 重新添加所有行
*/
var row = null;
for(var i = 0; i < me.records.length; ++i)
{
var row = '<tr id="' + (me.id + '-row-' + i) +'" class="table-tag-tr">';

var cel = null; // 表示单元格的 HTML 字符串
var tha = null; // 数据文本的水平停靠方式
var rdt = null; // 被渲染后的文本
var ich = null; // 单元格所在的列是否被隐藏
for(var j = 0; j < me.records[i].length; j++)
{
ich = me.isHiddenColumn(j, me);
if(ich) continue; // 如果隐藏,就直接跳过渲染

tha = me.getTextHorizontalAlignmentByCol(j, me);
rdt = me.getRenderedText(me.records[i][j], i, j, me.records[i], me);

cel = '<td'
+ ' id="' + (me.id + '-row-' + i + '-col-' + j) +'"'
+ ' class="table-tag-td"'
+ ' style="' + tha + '">' + rdt + '</td>';
row += cel;
}
row += '</tr>';
$(row).appendTo(tbl);
}

// 合并单元格
me.merge();

// 添加单元格事件
$(".table-tag-td").click(function()
{
var inf = this.id.split('-');
me.selectedRowNumber = inf[inf.length - 3];
me.selectedColNumber = inf[inf.length - 1];

me.cellClickCallback(me, this); // 单元格单机事件
});
},
/**
* 添加一行。数据应该是数组的形式,注意数据与标题之间的对应。
* 会自动进行基本校验,如果失败,返回false。
*
* 参数 record:要增加的数据,数组形式
*/
addRow : function(record)
{
// 对数据进行校验
if(this.records.length > 0 && this.records[0].length != record.length)
{
return false;
}

this.records.push(record);

this.selfAdapt(this);
},
/**
* 根据行号移除一行。移除失败,就返回 false。
*
* 参数 rowNum:被删除行的行号
*/
removeRow: function(rowNum) // 参数:行号
{
if(isNaN(rowNum) || rowNum < 0 || this.records <= rowNum) return false;

this.records.remove(rowNum); // 移除数据
this.selfAdapt(this);        // 重新渲染
}
});

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

function tableTag()
{

var testdata =
[
// 年级,班级,学生,分数,班级平均分
['数学', '拓扑学', '张朝阳', 97, 91],
['数学', '概率论', '刘向阳', 67, 88],
['数学', '分析',   '段明海', 89, 91],
['数学', '概率论', '红太阳', 99, 88],
['数学', '拓扑学', '章北海', 98, 91],
['数学', '概率论', '左明阳', 87, 88],

['化学', '有机化学', '赵钱孙', 85, 91],

['物理学', '力学',   '小透', 91, 85],
['物理学', '热力学', '小兰', 84, 93],
['物理学', '力学',   '小红', 62, 85],
['物理学', '电磁学', '小明', 85, 93],
['物理学', '力学',   '小绿', 93, 85],
['物理学', '电磁学', '大为', 96, 93],

['化学', '有机化学', '周吴郑', 95, 91]
];

var c = 0;

/**
* 一个随机获取数据的方法
*/
var getARecord = function()
{
var cd = testdata[c++];
if(c == testdata.length)
c = 0;

return cd;
};

/**
* 表格所在 panel 的 id。
*
* panel 的 id 给定了,那么
* 表格的 id 也确定了,就是 [id]-table;
* 表格行的 id 也确定了,就是 [id]-row-[row];
* 单元格的 id 也确定了,就是 [id]-row-[row]-col-[col]
*/
var id = 'table-tag-panel';
var table = new Ext.ns.my.tag.TagTablePanel(
{
id: id,
colHeaders:
[
{
header: '学科', width: 60, textHorizontalAlignment: 'center',
renderer: function(oldValue, rowNum, colNum, record)
{
return '<a href="#"><b>' + oldValue + "</b></a>";
}
},
{ header: '课程' },
{ header: '讲师', textHorizontalAlignment: 'right' },
{ header: '课时', width: 50, hidden: true },
{ header: '平均课时', width: 50, textHorizontalAlignment: 'center' }
],
mergeCols: [0, 1, 4],
title: '<table>标签演示',
region: 'center', // 放在中心区域
tbar:

[{
text: '【增加一行】',
handler: function(a, b)
{
Ext.getCmp(id).addRow(getARecord());
}
},
{
text: '【删除第4行】',
handler: function(a, b)
{
Ext.getCmp(id).removeRow(3);
}
},
{
text: '【取值】',
handler: function(a, b)
{
var val = Ext.getCmp(id).getSeletedValue();
Ext.Msg.alert("数据", val == false ? "尚未选择单元格" : val);
}
},
{
text: '【改值】',
handler: function(a, b)
{
Ext.getCmp(id).setSeletedValue("XXX");
}
}]
});

// 创建一个窗口。
var win = new Ext.Window(
{
title: 		"HTML标签: <table>",
width:		600,
height:		480,
layout:		'border',
resizable:	false,
modal:		true, 		// 设置窗口为模态
bodyStyle: { background : '#FFFFFF' },
items : [ table ],
buttonAlign: 'center',	//居中
buttons :
[{
text : "关闭",
handler : function(){ win.close(); }
}]
});

// 显示窗口
win.show();

}

表格样式 css.

.table-tag-table {
width: 100%; /* 表格宽度自适应 */
}

.table-tag-tr {
border: 1px solid black;
}

.table-tag-th {
background-color: #FF2F2F;
font-weight: bold;
text-align: center;
color: white;
width: *;
padding: 10px;
}

.table-tag-td {
word-break: break-all;
padding: 10px;
background-color: #D8E4F3;
cursor:pointer;
}

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Javascript extjs 合并