---
title: "Grid 表格"
description: "WebBuilder Grid 的样式、合计、编辑、分页与交互用法。"
---

> Documentation Index
> Fetch the complete documentation index at: https://ag.879818327.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Grid 表格

## 表格背景颜色

![image-20210104093045038](/webbulider/image-20210104093045038.png)

``` javascript
//在viewConfig 的 getRowClass中
//Wb预定义行背景色种类：wb_red_row, wb_green_row, wb_blue_row, wb_yellow_row, wb_hotpink_row, wb_gold_row, wb_bisque_row, wb_aqua_row
var cls, finalMoney = record.get('FINAL_MONEY');
if (finalMoney == '0'||finalMoney==null)
  cls = '';
else 
  cls = '';
return cls;
```

## 表格中文字格式

``` javascript
//initialize中加入
Ext.util.CSS.createStyleSheet(".font_red{color:red}","font_red");

//如果是grid表格的背景
Ext.util.CSS.createStyleSheet(".xxx .x-grid-cell {background-color: red}","xxx");

//在viewConfig 的 getRowClass中
//Wb预定义行背景色种类：wb_red_row, wb_green_row, wb_blue_row, wb_yellow_row, wb_hotpink_row, wb_gold_row, wb_bisque_row, wb_aqua_row
var cls, finalMoney = record.get('FINAL_MONEY');
if (finalMoney == '0'||finalMoney==null)
  cls = '';
else 
  cls = '';
return cls;
```

## 表格合计

![image-20210104093045038](/webbulider/image-20210104093045038.png)

![image-20210104093353913](/webbulider/image-20210104093353913.png)

![image-20210104093427024](/webbulider/image-20210104093427024.png)



``` javascript
// features 中放入feature
// feature 中的 ftype 设置分别表示统计和分组统计
//summaryRenderer代表最终输出的格式 可改变 字体和背景
//summaryType可选 求和 平均等
```

## 选中合计

***grid/bottomHtml***

~~~html
<lable id='underDiv' style = 'margin-left:330px;font-weight:bold;'></lable>
~~~

***tagEvent***

~~~js
{
  select: function(editor, e, eOpts) { //e是要选中的行
    var resc = app.unRcvGrid.getSelection();
    showTotal(resc, "unGridDiv222");
  },
  deselect: function(editor, e, eOpts) { //e是取消的行
    var resc = app.unRcvGrid.getSelection();
    showTotal(resc, "unGridDiv222");
  }
}
~~~

~~~js
function showTotal(recs, divId){
  var usd = 0, cny = 0;
  Wb.each(recs, function(one) {
    if (one.data.CURRENCY === 'USD') { //美元合计
      usd += one.data.MONEY;
    }
    if (one.data.CURRENCY === 'CNY') { //人民币合计
      cny += one.data.MONEY;
    }
  });
  var context = '选中合计:';
  context += "&nbsp; CNY：<font color='red'>" + Wb.format(cny, '0,000.00')+"</font>";  
  context += "&nbsp;&nbsp; USD：<font color='red'>" + Wb.format(usd, '0,000.00')+"</font>";
  document.getElementById(divId).innerHTML = context;
}
~~~

## 表格分组

![image-20210204171639545](/webbulider/image-20210204171639545.png)

![image-20210204171716331](/webbulider/image-20210204171716331.png)

![image-20210204171726858](/webbulider/image-20210204171726858.png)

## 可编辑表格判断某行某一列修改没修改

```javascript
var recsm = Wb.getModified(app.gridContract);
var flag = 0;
Wb.each(recsm.update, function(one) {
  if (!Wb.isEmpty(one.APPLYTIM)) {
if (!Wb.isEmpty(one.RELEASE_NO) && one.RELEASE_NO != one["#RELEASE_NO"]) {
  //修改了外放箱号,可执行
} else {
  flag = 1;
}
  }
});
```

## 可编辑式表格控制符合某种条件的行不可编辑

在 grid 的 tagEvents 事件中添加如下代码，判断条件以实际情况而定

```javascript
{
  beforeedit: function(editor, e, eOpts) {
   if(e.record.data.AC_CODE=='CNH004502'){
return false;//不可编辑
   }else{
return true;//可以编辑
   }
  }
}
```

## 为某一列添加背景色

在该列的renderer方法里设置

```javascript
metaData.style='background-color:#fff;';
return '<span style="color:#f00">'+value+'</span>';//修改字的颜色
```

## 为表格中单元格添加点击事件

### 增加事件

```javascript
//首先在we_debug.js中添加以下方法
columnClick:function(view,e,columnname,func){
   var column = view.getHeaderByCell(e.getTarget(view.getCellSelector()));
   if(column && column.dataIndex == columnname){
  func();
   }
},
```

``` javascript
//grid的itemclick事件添加以下代码
Wb.columnClick(view,e,"CARGO_NAM",function(){
  if(!Wb.isEmpty(record.data.SHIP_CARGO_NO))
return;
  Wb.reset(app.winTrust);
  app.winTrust.show();
  ...//方法处理
});
```

### 使用actioncolumn

```javascript
//handler中写入点击后执行函数
setTimeout(function() {
  app.sealWin.show();
  var id = record.data.ID;
  app.sealGrid.store.load({
params: {
  cntrId: id
}
  });
}, 100);
```

## 鼠标悬浮某单元格时显示全部信息

```javascript
//在某列column的renerder属性添加以下代码
metaData.tdAttr = 'data-qtip="' + Ext.String.htmlEncode(value) + '"'; //给每一个Cell添加tooltip效果
return value;
```

## 大数据虚拟渲染

`bufferedrenderer` 是 Ext Grid 的虚拟行渲染插件。它只创建当前可视区域及前后缓冲区的行 DOM；滚动时复用和更新这些行，而不是为每条记录同时创建页面元素。

适用于固定或基本固定行高的普通表格。行展开、频繁变化的自动换行行高、复杂合并单元格等场景可能影响滚动定位，应单独验证。

### 本地数据测试

插件配置加在 Grid 上，`fields` 配置加在 Grid 下的 Store 上。下面模块点击 `check` 后一次写入 10 万条记录，用于验证表格的渲染和滚动性能。

```javascript
module = {
  itemId: "module",
  items: [{
itemId: "viewport1",
layout: "fit",
items: [{
  itemId: "grid1",
  pagingBar: false,
  plugins: {
    ptype: "bufferedrenderer",
    leadingBufferZone: 200,
    trailingBufferZone: 100
  },
  items: [{
    itemId: "tbar",
    items: [{
      iconCls: "accept_icon",
      itemId: "item1",
      text: "check",
      events: {
        click: function(item) {
          var store = item.up("grid").store;
          var rows = new Array(100000);
          var i;

          for (i = 0; i < 100000; i++) {
            rows[i] = {
              ID: i + 1,
              SEX: i % 2 === 0 ? "男" : "女"
            };
          }

          store.loadData(rows, false);
        }
      }
    }]
  }, {
    itemId: "store",
    fields: ["ID", "SEX"]
  }, {
    itemId: "columns",
    items: [{
      dataIndex: "ID",
      itemId: "IDCol",
      text: "主键",
      width: 120
    }, {
      dataIndex: "SEX",
      itemId: "SEXCol",
      text: "性别",
      width: 100
    }]
  }]
}]
  }]
};
```

不要在循环中执行 `store.add()`。每次调用都会触发 Store 和 Grid 的更新；先构建数组，再执行一次 `store.loadData(rows, false)` 才是用于批量测试和导入的合适方式。

此例仍会将 10 万条 `Model` 保留在浏览器内存中，只是避免创建 10 万行 DOM。因此它用于验证渲染性能，不是生产环境加载海量数据的最终方案。

### URL 数据源

对于生产数据，将分页、排序和筛选交由服务端处理。Grid 继续配置 `bufferedrenderer`，并在 Store 上启用缓冲加载：

```javascript
{
  itemId: "grid1",
  pagingBar: false,
  plugins: {
ptype: "bufferedrenderer",
leadingBufferZone: 200,
trailingBufferZone: 100
  },
  items: [{
itemId: "store",
url: "m?xwl=demo/order/query",
fields: ["ID", "SEX"],
buffered: true,
pageSize: 100,
purgePageCount: 5,
remoteSort: true,
remoteFilter: true
  }]
}
```

Store 会以 `start` 和 `limit` 请求数据。服务端必须使用这两个参数分页查询，并在响应中提供当前页记录和总记录数。例如：

```json
{
  "data": [
{ "ID": 1, "SEX": "男" }
  ],
  "total": 100000
}
```

若接口仍一次返回全部记录，不要配置 `buffered: true`；仅使用 Grid 插件即可减少 DOM 数量。启用 `buffered: true` 前，也要确认当前 Ext Store Reader 的记录数组字段和总数字段名称与接口响应一致。

### 参数建议

| 配置 | 建议值 | 作用 |
| --- | --- | --- |
| `pageSize` | `100` | 每次从服务端读取的记录数。可根据单行数据大小在 50 到 200 间调整。 |
| `leadingBufferZone` | `200` | 当前视口之后预先渲染的行数，降低向下滚动时的空白感。 |
| `trailingBufferZone` | `100` | 当前视口之前保留的行数，改善向上回滚。 |
| `purgePageCount` | `5` | Store 缓存的页数，限制客户端内存占用。 |

不要同时使用传统分页栏和缓冲滚动作为同一张 Grid 的主导航方式。前者适合可明确跳页的业务表格，后者适合持续浏览大量记录的场景。
## grid数据以数组形式传参到wb数据层

```javascript
//参数获取
cntrsdata: app.gridReceipt.store.data.items.map(function(r) {
   return r.data;
})

//wb数据层接收示例
cntrsdata = Wb.decode(data.cntrsdata); //grid数据
```

## 给表格某行某列赋值

```javascript
var rec = app.workloadGrid.getSelection()[0];//获取当前选中的行
rec.set('PROJECT_NAME',"sssss");//给该行某一列赋值
```

## 动态选中表格行

```javascript
app.gridxxx.getSelectionModel().select(records[0]);//选中第一行

var index = app.gridxxx.store.find("ID","123");//选中ID为123的行
app.gridxxx.getSelectionModel().select(index);//选中第一行

//选中某些符合条件的行
Wb.request({
url: 'm?xwl=imtos/basecode/C_Client/selectTypeByClientType',
params: {
 ID:clientId
},
success: function(resp) {
  var respArr = JSON.parse(resp.responseText).rows;  
  var roleRec, selRecs = [];
  console.log(respArr);
  Wb.each(respArr, function(rec) {
    roleRec = Wb.find(app.gridmessage.store, 'TYPE_CODE', rec.CLIENT_TYPE_CODE);
    if (roleRec)
      selRecs.push(roleRec);
  });
  app.gridmessage.setSelection(selRecs);
}
  });
```

## 表格内容竖向展示

```javascript
//在需要竖向展示的列的render方法中添加以下代码
return '<div style="writing-mode:tb-rl">'+value+'</div>'
```

## 表格前加复选框多选时，禁止点击单元格（数据行）选择，只能点击复选框选择

```javascript
//在grid的selModel属性中添加以下代码
new Ext.selection.CheckboxModel({ checkOnly: true })
```

## 合并行

![img](/webbulider/image1600843740205.png)

```javascript
mergeGrid(app.gridFee, [2,3,4], true);//列标从0开始
/**
 * 合并Grid的数据列
 * @param grid {Ext.Grid.Panel} 需要合并的Grid
 * @param colIndexArray {Array} 需要合并列的Index(序号)数组；从0开始计数，序号也包含。
 * @param isAllSome {Boolean} 是否2个tr的colIndexArray必须完成一样才能进行合并。true：完成一样；false：不完全一样
 */
function mergeGrid(grid, colIndexArray, isAllSome) {
  isAllSome = isAllSome == undefined ? true : isAllSome; // 默认为true
  // 1.是否含有数据
  var gridView = document.getElementById(grid.getView().getId() + '-body');
  if (gridView == null) {
return;
  }
  // 2.获取Grid的所有tr
  var trArray = [];
  if (grid.layout.type == 'table') { // 若是table部署方式，获取的tr方式如下
trArray = gridView.childNodes;
  } else {
trArray = gridView.getElementsByTagName('tr');
  }
  // 3.进行合并操作
  if (isAllSome) { // 3.1 全部列合并：只有相邻tr所指定的td都相同才会进行合并
var lastTr = trArray[0]; // 指向第一行
// 1)遍历grid的tr，从第二个数据行开始
for (var i = 1, trLength = trArray.length; i < trLength; i++) {
  var thisTr = trArray[i];
  var isPass = true; // 是否验证通过
  // 2)遍历需要合并的列
  for (var j = 0, colArrayLength = colIndexArray.length; j < colArrayLength; j++) {
    var colIndex = colIndexArray[j];
    // 3)比较2个td的列是否匹配，若不匹配，就把last指向当前列
   // console.info(lastTr.childNodes[colIndex].innerText);
   // console.info(thisTr.childNodes[colIndex].innerText);
    if(colIndex != 11 && colIndex != 16){
      if (lastTr.childNodes[colIndex].innerText != thisTr.childNodes[colIndex].innerText) {
        lastTr = thisTr;
        isPass = false;
        break;
      }
    }
  }
  // 4)若colIndexArray验证通过，就把当前行合并到'合并行'
  if (isPass) {
    for (var j = 0, colArrayLength = colIndexArray.length; j < colArrayLength; j++) {
      var colIndex = colIndexArray[j];

        // 5)设置合并行的td rowspan属性
        if (lastTr.childNodes[colIndex].hasAttribute('rowspan')) {
          var rowspan = lastTr.childNodes[colIndex].getAttribute('rowspan') - 0;
          rowspan++;
          lastTr.childNodes[colIndex].setAttribute('rowspan', rowspan);
        } else {
          lastTr.childNodes[colIndex].setAttribute('rowspan', '2');
        }
        // lastTr.childNodes[colIndex].style['text-align'] = 'center';; // 水平居中
        lastTr.childNodes[colIndex].style['vertical-align'] = 'middle'; // 纵向居中
        //lastTr.childNodes[colIndex].style['vertical-align'] = 'top'; // 纵向居中
        thisTr.childNodes[colIndex].style.display = 'none';

    }
  }
}
  } else { // 3.2 逐个列合并：每个列在前面列合并的前提下可分别合并
// 1)遍历列的序号数组
for (var i = 0, colArrayLength = colIndexArray.length; i < colArrayLength; i++) {
  var colIndex = colIndexArray[i];
  var lastTr = trArray[0]; // 合并tr，默认为第一行数据
  // 2)遍历grid的tr，从第二个数据行开始
  for (var j = 1, trLength = trArray.length; j < trLength; j++) {
    var thisTr = trArray[j];
    // 3)2个tr的td内容一样
    if (lastTr.childNodes[colIndex].innerText == thisTr.childNodes[colIndex].innerText) {
      // 4)若前面的td未合并，后面的td都不进行合并操作
      if (i > 0 && thisTr.childNodes[colIndexArray[i - 1]].style.display != 'none') {
        lastTr = thisTr;
        continue;
      } else {
        // 5)符合条件合并td
        if (lastTr.childNodes[colIndex].hasAttribute('rowspan')) {
          var rowspan = lastTr.childNodes[colIndex].getAttribute('rowspan') - 0;
          rowspan++;
          lastTr.childNodes[colIndex].setAttribute('rowspan', rowspan);
        } else {
          lastTr.childNodes[colIndex].setAttribute('rowspan', '2');
        }
        //lastTr.childNodes[colIndex].style['text-align'] = 'center'; // 水平居中
        lastTr.childNodes[colIndex].style['vertical-align'] = 'middle'; // 纵向居中
        thisTr.childNodes[colIndex].style.display = 'none'; // 当前行隐藏
      }
    } else {
      // 5)2个tr的td内容不一样
      lastTr = thisTr;
    }
  }
}
  }
}
```

## 表格列展示位复选框形式

![img](/webbulider/image1600844027181.png)

```javascript
//tagConfigs属性添加以下代码
{
  getValue: function() {
if (this.value==true||this.value=='1') {
  return 1;
} else {
  return 0;
}
  }
}
//该列 字段查询语句要做转数字：to_number(HIRE_FLAG,'9')  HIRE_FLAG
```

## 符合条件的行禁止选中

![image-20210114171539198](/webbulider/image-20210114171539198.png)

```javascript
if(record.data.DELIVERY_FLAG !='1'&&record.data.DELIVERY_FLAG !=1){
  return true;
}else{
  return false;
}
```

## 表格后台分页

![image-20210114171706986](/webbulider/image-20210114171706986.png)

![image-20210114171805821](/webbulider/image-20210114171805821.png)

```sql
SELECT * FROM (
   SELECT ROWNUM AS rowno, t.* 
   FROM emp t 
   WHERE hire_date BETWEEN TO_DATE ('20060501', 'yyyymmdd') 
   AND TO_DATE ('20060731', 'yyyymmdd') 
   AND ROWNUM <= {?bigint.endIndex?}) table_alias 
WHERE table_alias.rowno >= {?bigint.beginIndex?};
```

## 分组合计小计

(1)gird增加合计和分组合计控件

![img](/webbulider/image1600676676384.png)

groupingsummary属性配置如下，summary为常规配置

![img](/webbulider/image1600676699824.png)

(2)页面区别“合计”和“小计”字眼

![image-20210114172321954](/webbulider/image-20210114172321954.png)

```javascript
var re = summaryData._featureObject instanceof Ext.grid.feature.Summary ? '合计：' : '小计：';
return "<span style='font-weight:bold;'>" + re + "</span>";
```

(3)按某个属性合计显示，该实例中是以箱尺寸分类显示

```javascript
//在需要显示的列的summaryRenderer中添加代码
/**
 * 合计小计按尺寸自定义显示。
 */
var cntr20 = 0,
  cntr40 = 0;
if (summaryData._featureObject instanceof Ext.grid.feature.GroupingSummary) { //小计
  if (summaryData.CNTR_NUM > 0) {
app.coutStore.each(function(rec) {
  if (rec.data.CNTR_NUM > 0) {
    if (rec.data.SIZE_CODE === '20') {
      cntr20 += rec.data.CNTR_NUM;
    } else if (rec.data.SIZE_CODE === '40') {
      cntr40 += rec.data.CNTR_NUM;
    }
  }
});
  } else if (summaryData.CNTR_NUM < 0) {
app.coutStore.each(function(rec) {
  if (rec.data.CNTR_NUM < 0) {
    if (rec.data.SIZE_CODE === '20') {
      cntr20 += rec.data.CNTR_NUM;
    } else if (rec.data.SIZE_CODE === '40') {
      cntr40 += rec.data.CNTR_NUM;
    }
  }
});
  }
} else if (summaryData._featureObject instanceof Ext.grid.feature.Summary) { //合计
  app.coutStore.each(function(rec) {
if (rec.data.SIZE_CODE === '20') {
  cntr20 += rec.data.CNTR_NUM;
} else if (rec.data.SIZE_CODE === '40') {
  cntr40 += rec.data.CNTR_NUM;
}
  });
}

return Wb.format("<span style='font-weight:bold;'>" + cntr20 + "<br>" + cntr40 + "<br>{0}</span>", value);
```

## 满足条件才可进行编辑表格

**grid /tagEvent**

```javascript
beforeedit: function(editor, e, eOpts) {
//条件
  var condition = e.record.data.xxx;
//控制的字段
  var field = e.field;
  if(e.field==""&&condition=""){
return false;
  }else{
return true;
  }
}
```

## 对单元格中的action进行隐藏

```javascript
if (record.get('SEX') !=2) {
  return 'x-hidden';
} else {
  return '';
}
```

## 隐藏某一列数据

```javascript
app.xxgrid.down('#BILL_NOCol').hide()
```

## grid上单元格的悬浮显示

```javascript
//
function showTip(data, metadata, record_start, rowindex, columnindex, store) {
  if (!Wb.isEmpty(data)) { // 此处对空白文本做过滤。
metadata.tdAttr = "data-qtip='" + data + "'";
return data;
  }
}
//renderer
return showTip(record.data.STATE_NOTE_ERROR, metaData, record, rowIndex, colIndex, store);
```

## 单元格换行 单元格文字颜色

```javascript
//首先要够长
value=value.replaceAll('\n','</br>');
value=value.replaceAll('新增','<font color="red">新增</font>');
value=value.replaceAll('更新','<font color="red">更新</font>');
value=value.replaceAll('删除','<font color="red">删除</font>');
return value;
```

Source: https://ag.879818327.xyz/webbuilder/grid/index.mdx
