最近做项目用到了jquery easyui,其中一组DataGrid做的报表是给客户大领导看的,客户要求报表样式跟他们原有系统的一模一样(如下图1)。

DataGrid样式好调,只是城市名称单元格跨行这个难度稍大,本着用户体验无尺度的用户价值观,无尺度的修改了easyui源代码


图1 – “城市”和“名称”列跨行的处理效果

一、分析准备:

1、jquery版本:1.8.0;easyui版本:1.3.2
2、首先读easyui文档,只有关于表头rowspan和colspan的配置的方式,表体装载数据的配置并没有介绍有关rowspan和colspan的,
再仔细读,发现文档中有如下内容:


图2 –DataGrid View文档描述

初步判定render方法应该就是装载DataGrid数据的方法吧。

3、打开jquery.easyui.min.js,代码格式明显很乱,为了提高可读性,可以使用工具格式化一下,Eclipse或者Visual Studio都有相应的快捷工具。

搜索render方法,发现由于太多没有办法确定具体是哪一个,继续看文档,renderRow的描述“This is an option function and will be called by render function.”,说明renderRow是被render调用的。搜索renderRow,直接就可以跟踪到了,它定义在一个叫“_5ef”变量中(jquery.easyui.min.js的变量或者方法命名都是3位长度的16进制数,在文件的8276行,格式化代码之后大约在10564行,直接搜索var _5ef就能找到它了),会发现DataGrid View的所有方法都在这里。

4、读render和renderRow方法,结合easyui文档介绍,文档和源码对比:

  Render有三个参数分别是:target, container, frozen,对应源代码就是render : function(_5f0, _5f1, _5f2)
  renderRow 的五个参数如下:target, fields, frozen, rowIndex, rowData也就是对应renderRow : function(_5fe, _5ff, _600, _601, _602)
其中render方法是装载datagrid数据的主方法,render方法首先把数据根据记录分行,然后逐行调用renderRow方法,把每一条记录装载为datagrid表格的一行数据。
附renderRow的五个参数解释:
  _5fe:datagrid对象,翻译过来基本上是JSON格式的字符串,需要装载的数据保存在这里面
  _5ff:表头Field集合,也就是页面中定义Datagrid的每一列的Field都保存在_5ff中
  _600: frozen顾名思义,是一个冻结标记。在本方法中,和opts.rownumbers组合成一个&&运算,用来标记是否显示行号
  _601:是行的索引,以0为初始下标
  _602:是对应行的数据,装载datagrid的数据
  其中render的功能是根据条件逐行装载需要装载的数据,应该说_5f5.push(this.renderRow.call(this, _5f0, _5f4, _5f2, i,rows[i]))调用就是装载每一行数据。
renderRow的功能是把装载的行装载到DataGrid对应的table中,cc.push("<td field=\"" + _604 + "\" " + _607 + ">");就是装载<td>的代码。
因此,我们可以有如下结论:

renderRow方法中的cc.push("<td field=\"" + _604 + "\" " + _607 + ">");修改为cc.push("<td rowspan=\"" + rownumber + "\" field=\"" + _604 + "\" " + _607 + ">");的形式,就一切OK

我们可以在需要处理的<td>标签中增加一些能够显示的数据来验证我们的思路是否正确。验证思路通过,那下一步就要处理它了

二、初步修改

我们可以确定,我们要把表格处理成如下图3右所示的样式

         
    图3左                图3右

1、获取要跨行的单元格和数量:

我们要把一个城市名称都显示的列(如上-左)做成如上(右)所示的效果,首先要明确我们在哪一行(城市如重庆,出现的第一行)需要rowspan来处理,既然有rowspan,那肯定要有rowspan的数量。因此,需要两个变量来统计跨行的那一行以及跨越的数量

2、在起初,我们由_5fe入参可以获得所有的数据_5fe1 = $.data(_5fe, "datagrid").data.rows;,以行为记录对象的二维数组,循环rows,就可以得到每一个单元格的数据了。例:rows [i][_5ff[3]]就是获取i行对应第3(四)列的数据了,_5ff是rows对列的特殊处理
在renderRow方法中加入以下代码:

  1. var _5fe1 = $.data(_5fe, "datagrid");
  2. var rows = _5fe1.data.rows;

确定两个数组变量以及针对它们的处理:

  1. //记录需要跨列的单元格行号
  2. var city = [];
  3. //记录需要跨列的单元格跨列数量
  4. var cityRowspanCount = [];
  5. //因为我处理城市列数据是倒序处理的,对于第1行数据可能会处理不到,所以初始化city[0]和定义cityCount,
  6. city[0] = 1;
  7. var cityCount = 1;
  8. //处理city序列
  9. for (var i = rows.length - 1; i > 0; i--){
  10. if (rows[i][_5ff[3]] != rows[i - 1][_5ff[3]]){
  11. city[i] = 1;
  12. }
  13. }
  14. //处理city对应的数量cityRowspanCount
  15. for (var j = rows.length - 1; j > 0; j--){
  16. if (rows[j][_5ff[3]] != rows[j - 1][_5ff[3]]){
  17. cityRowspanCount[j] = cityCount;
  18. cityCount = 1;
  19. }
  20. else{
  21. cityCount++;
  22. }
  23. if (j == 1){
  24. cityRowspanCount[0] = cityCount;
  25. }
  26. }

通过两个for循环,得到city和cityRowspanCount 两个数组,
我们以图3的处理效果,打印城市的处理结果
    city : [1,1,,1,,,,,,1]
    cityRowspanCount : [1,2,,6,,,,,,5]
这样,就可以用以上两个数组处理表格了,接着读renderRow方法剩下的内容,if的功能是加载表格的行号,for循环的作用就是对当前行的数据依次读取
  if (_600 && opts.rownumbers)
  for ( var i = 0; i < _5ff.length; i++)
分析或者打印_5ff,我们可以发现它是所有列field属性的集合。确定是哪一列需要跨行处理,注意隐藏列也要算上,以0下标为初始。

3、修改:

  1. // 读取表中内容
  2. for ( var i = 0; i < _5ff.length; i++) {
  3. //第四(3)列需要用rowspan合并,因此重新处理
  4. if (i == 3)
  5. {
  6. //对于需要装载的单元格,执行if中的语句
  7. if (city[_601])
  8. {
  9. var _604 = _5ff[i];
  10. var col = $(_5fe).datagrid("getColumnOption", _604);
  11. if (col) {
  12. var _605 = _602[_604];
  13. var _606 = col.styler ? (col.styler(_605, _602, _601) || "") : "";
  14. var _607 = col.hidden ? "style=\"display:none;" + _606 + "\"" : (_606 ? "style=\"" + _606 + "\"" : "");
  15. //需要装载的单元格处理rowspan为cityRowspanCount对应的数量
  16. cc.push("<td rowspan=\"" + cityRowspanCount[_601] + "\" field=\"" + _604 + "\" " + _607 + ">");
  17. if (col.checkbox) {
  18. //省略…
  19. }
  20. cc.push("<div style=\"" + _607 + "\" ");
  21. if (col.checkbox) {
  22. //省略…
  23. }
  24. cc.push("\">");
  25. if (col.checkbox) {
  26. //省略…
  27. }
  28. cc.push("</div>");
  29. cc.push("</td>");
  30. }
  31. }
  32. }
  33. else{
  34. //其它列的内容按照正常模式装载
  35. }
  36. }

通过以上修改,Datagrid单元格跨列的功能就大体实现了。

三、完善&优化

  通过修改renderRow方法,基本实现了单元格跨列的功能,但是现在处理city和cityRowspanCount是在renderRow方法里面处理的,由于renderRow方法每装载一行数据就要执行一次,city和cityRowspanCount就要重新生成一次,不仅不符合设计逻辑,还会造成资源浪费等问题,因此把city和cityRowspanCount的定义可以放在render方法里面,提高代码执行效率(注:可能用IE8的朋友会出现表格高度出错的现象,不要着急,后面会告诉大家怎么处理)。

1、首先在render方法中定义citycityRowspanCount并实现功能,同时为了区别renderRow方法,定义renderRowNew方法,
参数:this.renderRowNew.call(this, _5f0, _5f4, _5f2, i, rows[i],city,cityRowspanCount)

  1. render : function(_5f0, _5f1, _5f2) {
  2. var _5f3 = $.data(_5f0, "datagrid");
  3. var opts = _5f3.options;
  4. var rows = _5f3.data.rows;
  5. var _5f4 = $(_5f0).datagrid("getColumnFields", _5f2);
  6. //begin 初始化需要rowspan的行以及数量
  7. var city = [];
  8. city[0] = 1;
  9. var cityRowspanCount = [];
  10. var cityCount = 1;
  11. for (var i = rows.length - 1; i > 0; i--){
  12. if (rows[i][_5f4[3]] != rows[i - 1][_5f4[3]]){
  13. city[i] = 1;
  14. }
  15. }
  16. for (var j = rows.length - 1; j > 0; j--){
  17. if (rows[j][_5f4[3]] != rows[j - 1][_5f4[3]]){
  18. cityRowspanCount[j] = cityCount;
  19. cityCount = 1;
  20. }
  21. else{
  22. cityCount++;
  23. }
  24. if (j == 1){
  25. cityRowspanCount[0] = cityCount;
  26. }
  27. }
  28. //end
  29. if (_5f2) {
  30. if (!(opts.rownumbers || (opts.frozenColumns && opts.frozenColumns.length))) {
  31. return;
  32. }
  33. }
  34. var _5f5 = [ "<table class=\"datagrid-btable\" cellspacing=\"0\" cellpadding=\"0\" border=\"0\"><tbody>" ];
  35. for ( var i = 0; i < rows.length; i++) {
  36. var cls = (i % 2 && opts.striped) ? "class=\"datagrid-row datagrid-row-alt\""
  37. : "class=\"datagrid-row\"";
  38. var _5f6 = opts.rowStyler ? opts.rowStyler.call(_5f0, i, rows[i]) : "";
  39. var _5f7 = _5f6 ? "style=\"" + _5f6 + "\"" : "";
  40. var _5f8 = _5f3.rowIdPrefix + "-" + (_5f2 ? 1 : 2) + "-" + i;
  41.  
  42. _5f5.push("<tr id=\"" + _5f8 + "\" datagrid-row-index=\"" + i + "\" " + cls + " " + _5f7 + ">");
         //调用新写的renderRowNew并注释原来的renderRow方法
  43. _5f5.push(this.renderRowNew.call(this, _5f0, _5f4, _5f2, i, rows[i],city,cityRowspanCount));
  44. //_5f5.push(this.renderRow.call(this, _5f0, _5f4, _5f2, i, rows[i]));
  45. _5f5.push("</tr>");
  46. }
  47. _5f5.push("</tbody></table>");
  48. $(_5f1).html(_5f5.join(""));
  49. }

2、编写renderRowNew方法:

  1. renderRowNew : function(_5fe, _5ff, _600, _601, _602,_601a,_601b,_601c,_601d) {
  2. var opts = $.data(_5fe, "datagrid").options;
  3. var cc = [];
  4. // 装载行号
  5. if (_600 && opts.rownumbers) {
  6. // _603表格当前行
  7. var _603 = _601 + 1;
  8. if (opts.pagination) {
  9. _603 += (opts.pageNumber - 1) * opts.pageSize;
  10. }
  11. cc.push("<td class=\"datagrid-td-rownumber\"><div class=\"datagrid-cell-rownumber\">" + _603
  12. + "</div></td>");
  13. }
  14. // 装载当前行数据
  15. for ( var i = 0; i < _5ff.length; i++) {
  16. //第3列需要用rowspan合并,因此重新写
  17. if (i == 3){
  18. //对于需要展示的单元格,才能执行if中的语句
  19. if (_601a[_601]){
  20. var _604 = _5ff[i];
  21. var col = $(_5fe).datagrid("getColumnOption", _604);
  22. if (col) {
  23. var _605 = _602[_604];
  24. var _606 = col.styler ? (col.styler(_605, _602, _601) || "") : "";
  25. var _607 = col.hidden ? "style=\"display:none;" + _606 + "\"" : (_606 ? "style=\"" + _606 + "\"": "");
  26. cc.push("<td rowspan=\"" + _601b[_601] + "\" field=\"" + _604 + "\" " + _607 + ">");
  27. if (col.checkbox) {
  28. //省略…
  29. }
  30. cc.push("<div style=\"" + _607 + "\" ");
  31. if (col.checkbox) {
  32. //省略…
  33. }
  34. cc.push("\">");
  35. if (col.checkbox) {
  36. //省略…
  37. }
  38. cc.push("</div>");
  39. cc.push("</td>");
  40. }
  41. }
  42. }
  43. else{
  44. //省略…
  45. }
  46. }
  47. return cc.join("");
  48. }

3、通过以上的处理,就大功告成了,有问题继续看“问题处理”

四、问题处理

用IE8的朋友会出现表格高度不一致的现象,IE8兼容性不管怎么调都是不好用,原因是datagrid在装载完毕之后,会根据高度自动修改table样式,
这个是在function _474(_475, _476, _477)中进行处理的,把以下几行代码注释就可以了

  1. //_47b += c._outerHeight();
  2.  
  3. //_479.height(_47b);
  4. //_47a.height(_47b);
  5.  
  6. //var _47d = Math.max(tr1.height(), tr2.height());
  7. //tr1.css("height", _47d);
  8. //tr2.css("height", _47d);

--------------------------------------------
参考资料:
Jquery.easyui官方文档:http://www.jeasyui.com/documentation/index.php#

转载请注明出处http://www.cnblogs.com/panzhaohui

easyui DataGrid表体单元格跨列rowspan的更多相关文章

  1. JQuery EasyUI DataGrid动态合并单元格

    /**        * EasyUI DataGrid根据字段动态合并单元格        * @param fldList 要合并table的id        * @param fldList ...

  2. EasyUI——DataGrid的自定义单元格点击事件

    1.当点击的单元格需要传递参数,并且传递的是row的值时,需要进行转义 function initCompareTable(){ $("#deviceCompareTable"). ...

  3. EasyUI DataGrid可编辑单元格

    效果如图: 首先在需要可编辑的列上添加一个editor属性,列定义为numberbox编辑类型 <th field="SCORES" editor="{type:' ...

  4. WPF:获取DataGrid控件单元格DataGridCell

    转载:http://blog.csdn.net/jhqin/article/details/7645357 /* ------------------------------------------- ...

  5. 雷林鹏分享:jQuery EasyUI 数据网格 - 合并单元格

    jQuery EasyUI 数据网格 - 合并单元格 数据网格(datagrid)经常需要合并一些单元格.本教程将向您展示如何在数据网格(datagrid)中合并单元格. 为了合并数据网格(datag ...

  6. Excel2007VBA数组和工作表及单元格的引用

    动态数组使用: https://zhidao.baidu.com/question/1432222709706721499.html 使用Redim动态数组即可. 1 2 3 4 5 6 7 8 Su ...

  7. c# WPF DataGrid 获取选中单元格信息

    private void Dg_SelectedCellsChanged(object sender, SelectedCellsChangedEventArgs e) { Console.Write ...

  8. PHP使用PHPExcel删除Excel单元格指定列的方法是怎样

    有一个系统仅公司内部和外部经销商使用,在一个导出功能中公司内部员工跟外部经销商导出的列是不一样的(某些数据是不能提供给经销商的)因为导出的数据都是一样的(某些列外数据外部没有)因此并没有单独处理,而是 ...

  9. WPF备忘录(3)如何从 Datagrid 中获得单元格的内容与 使用值转换器进行绑定数据的转换IValueConverter

    一.如何从 Datagrid 中获得单元格的内容 DataGrid 属于一种 ItemsControl, 因此,它有 Items 属性并且用ItemContainer 封装它的 items. 但是,W ...

随机推荐

  1. ch3:文件处理与异常

    如何从文件读入数据? python中的基本输入机制是基于行的: python中标准的“打开-处理-关闭”代码: the_file=open('文件全称') #处理文件中的数据 the_file.clo ...

  2. Django SimpleCMDB WSGI

    一.WSGI 介绍 (1) 在前面的学习中,我们是通过 python manage.py runserver 0.0.0.0:8000 来启动并访问开发服务器的:(2) 但在实际中我们是通过直接访问 ...

  3. HTML 格式化

    格式化标签: <!DOCTYPE HTML> <html> <body> <b> This text is bold </b> # < ...

  4. Python迭代器笔记

    python中的三大器有迭代器,生成器,装饰器,本文重点讲解下迭代器的概念,使用,自定义迭代器等的介绍. 1.概念: 迭代器是一个对象,一个可以记住遍历位置的对象,迭代器对象从集合的第一个元素开始访问 ...

  5. phaser相关

    phaser.js这个插件,中文翻译的开发文档还在翻译中,至于英文的开发文档,勉勉强强查阅,有些方法名和开发文档的有着一些区别,开发文档上时带着er的.不过大体上还是一一对应查找的到的 eg:load ...

  6. JS 验证URL

    var strVal = $("#urlText").val(); var Expression = "^((https|http|ftp|rtsp|mms)?://)& ...

  7. css笔记 - 张鑫旭css课程笔记之 border 篇

    border地址 border特性: 能形成BFC但是不能清除浮动.但是bfc也是把子元素的margin包裹进来,但是拿自己的margin穿透没办法的. 边框宽度不支持百分比 透明border可以突破 ...

  8. 原生js--cookie操作的封装

    封装cookie的操作:查询cookie个数.查询所有cookie的键.获取cookie.设置cookie.删除cookie.清除全部cookie /** * cookieStorage */func ...

  9. 使用TELNET手工操作 IMAP 查看邮件

    http://www.cnblogs.com/CrazyWill/archive/2006/08/12/474884.html IMAP 协议收信与POP收信有很大的不同,最明显的一点就是发送的每条命 ...

  10. maven打war包的过程中,都用了哪些插件呢?

    一.maven生命周期 http://ifeve.com/introduction-to-the-lifecycle/ https://maven.apache.org/guides/introduc ...