一个基于Extjs 3的支持单元格合并的面板
2016-07-11 14:45
519 查看
这是一个支持单元格合并的Panel,不是基于 GridPanel,而是基于 Ext.Panel 这个类。内部表格实现是基于 html 的 <table> 标签的。支持增删。效果图如下:
![](http://img.blog.csdn.net/20160711144715641?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
代码依赖 Extjs 3 和 JQuery,请自行添加。废话少说,上代码。
js 部分(直接调用 tableTag() 方法就可以显示出效果了):
表格样式 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;
}
代码依赖 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;
}
相关文章推荐
- JQuery1——基础($对象,选择器,对象转换)
- Android学习笔记(二九):嵌入浏览器
- Android java 与 javascript互访(相互调用)的方法例子
- JavaScript演示排序算法
- javascript实现10进制转为N进制数
- 最后一次说说闭包
- Ajax
- 2019年开发人员应该学习的8个JavaScript框架
- HTML中的script标签研究
- 对一个分号引发的错误研究
- 异步流程控制:7 行代码学会 co 模块
- ES6 走马观花(ECMAScript2015 新特性)
- JavaScript拆分字符串时产生空字符的原因
- Canvas 在高清屏下绘制图片变模糊的解决方法
- Redux系列02:一个炒鸡简单的react+redux例子
- JavaScript 各种遍历方式详解