页面树结构
转至元数据结尾
转至元数据起始

提示:本文档的示例代码仅适用于本文档中的示例报表/场景。若实际报表/场景与示例代码无法完全适配(如报表使用冻结,或多个宏代码冲突等),需根据实际需求开发代码。

示例说明

如下图,电子表格中存在层级关系,并且用户只需填写最底层数据,其上级数据无需填写即可自动汇总下级数据。

  • "运营支出"= "一、销售支出"+"二、营销类支出";
  • "一、销售支出" = "(一)销售种类一支出" + "(二)销售种类二支出";
  • "(一)销售种类一支出" = "1.服装类支出" + "2.食品类支出" + "3.办公类支出"

示例演示效果如下:

数据准备

数据集预览如下:

  • 数据须以正确的顺序排序
  • 数据中需要有层级标识字段,标明该项数据在层级中的关系
    比如层级标识为0一项(运营支出)的支出数据,由下方所有层级标识为1的支出数据汇总; 
    层级标识为1一项(一、销售支出)的支出数据,由于第10行出现了另一个层级标识为1的数据,因此该项数据由两个层级标识为1的两行中间所有层级标识为2的支出数据汇总。
  • 所有层级标识必须一级一级标识,不能够跨级汇总。比如层级标识为1的两行中间,不存在层级标识为2的项,只有层级标识为3的项,这种情况是不允许的。

实现步骤

1、在电子表格设计器(Microsoft Office Excel)中创建一张电子表格,将上一步数据集中"层级标识"字段拖到单元格A4,"指标ID"字段拖到单元格 B4, "指标名称"字段拖到单元格 C4, "刚性支出"字段拖到单元格D4, "弹性支出"字段拖到单元格 E4。并设置D4和E4单元格扩展方式为"不扩展-汇总-求和"。

  

2、设置A列列宽为0,隐藏层级标识所在列后发布预览。

3、在浏览器的资源定制节点下,选中电子表格,右键选择 编辑宏 进入报表宏界面

4、在报表宏界面 新建服务端模块。在弹出的 新建模块 对话框中,选择对象为 spreadsheetReport、事件为onBeforeOutput、并把下面的宏代码复制到代码编辑区域。根据实际情况修改相应的代码

宏类型

类型

对象

事件

ServerSide

spreadsheetReport

onBeforeOutput

宏代码

function main(spreadsheetReport) {
    var positions = spreadsheetReport.sheets[0].getExpandedPositions("C4"); //获取数据列的前一列
    var fromRow = positions[0].row;
    var toRow = positions[positions.length - 1].row;
    var cells = spreadsheetReport.workbook.worksheets.get(0).cells;
    var levelColumnName = "A"; //层级标识所在的列
    var fromColumnName = "D"; //需要汇总的所有列中的第一列的位置
    var toColumnName = "E"; //需要汇总的所有列的最后一列的位置
    var levelColumnIndex = cells.get(levelColumnName + "1").column; //数据列下标为2,从0开始计起
    var fromColumnIndex = cells.get(fromColumnName + "1").column;
    var toColumnIndex = cells.get(toColumnName + "1").column;
    var formulaCells = [];
    for (var i = fromRow; i < toRow; i++) {
        var level = parseInt(cells.get(i, levelColumnIndex).value); //获取层级标识
        var children = [];
        for (var j = i + 1; j <= toRow; j++) {
            var nextLevel = parseInt(cells.get(j, levelColumnIndex).value); //获取下一层级标识
            //判断是否为其下第一层级
            if (nextLevel == level) break;
            if (nextLevel == level + 1) children.push(j); //将此层级下的子节点放入组数中
        }
        if (children.length > 0) {
            for (var j = fromColumnIndex; j <= toColumnIndex; j++) {
                var c = cells.get(i, j);
                if (c.isFormula()) continue;
                var formula = new Array();
                for (var k = 0; k < children.length; k++) {
                    formula.push(cells.get(children[k], j).name);
                }
                formula = "=" + formula.join('+');
                c.formula = formula;
                formulaCells.push(i + ":" + j);
            }
        }
    }
    spreadsheetReport.customProperties.put("formulaCells", formulaCells);
    spreadsheetReport.workbook.calculateFormula();
}

5、在报表宏界面 新建客户端模块。在弹出的 新建模块 对话框中,选择对象为 spreadsheetReport、事件为onRender、并把下面的宏代码复制到代码编辑区域。该宏代码实现汇总项不允许填报功能。

宏类型

类型

对象

事件

ClientSide

spreadsheetReport

onRender

宏代码

function main(spreadsheetReport) {
    var formulaCells = spreadsheetReport.customProperties["formulaCells"];
    var writableMap = spreadsheetReport.elemSheetFrame.contentWindow.writableMap;
    for (var i = 0; i < formulaCells.length; i++) {
        var c = writableMap[formulaCells[i]];
        if (c) c.modifiable = false;
    }
}

6、如果需要将汇总单元格的数据写入到数据库中,可添加下面两个宏代码:

(1)在报表宏界面新建客户端模块。在弹出的 新建模块 对话框中,选择对象为 spreadsheetReport、事件为onRender、并把下面的宏代码复制到代码编辑区域。该宏代码实现在点击保存时将汇总数据回写到数据库功能。

宏类型

类型

对象

事件

ClientSide

spreadsheetReport

onRender

宏代码

function main(spreadsheetReport, isAjaxRefreshCallback) {
    spreadsheetReport.spreadsheetReportWriteBack.removeListener(spreadsheetReport.spreadsheetReportWriteBack.elem_btnSave, "click");
    spreadsheetReport.spreadsheetReportWriteBack.addListener(spreadsheetReport.spreadsheetReportWriteBack.elem_btnSave, "click", doNewSaveClick, spreadsheetReport);
}

function doNewSaveClick(e) {
    var formulaCells = this.customProperties["formulaCells"];
    if (!this.spreadsheetReportWriteBack.cellValueChanged) {
        return;
    }
    for (var i = 0; i < formulaCells.length; i++) {
        var formulaCellPosition = formulaCells[i];
        var index = formulaCellPosition.indexOf(':');
        var row = parseInt(formulaCellPosition.substring(0, index));
        var column = parseInt(formulaCellPosition.substring(index + 1));
        var cell = this.getCell(row, column);
        this.spreadsheetReportWriteBack.updateWriteBackData(this.currentSheetIndex, 
	    formulaCellPosition, cell, null, cell.innerText, cell.innerText, "NUMBER", false);
    }
    this.spreadsheetReportWriteBack.cellValueChanged = false;
    this.spreadsheetReportWriteBack.doSaveClick();
}

(2)在报表宏界面 新建客户端模块。在弹出的 新建模块 对话框中,选择对象为 spreadsheetReport、事件为onWriteBackCellValueChanged、并把下面的宏代码复制到代码编辑区域。该宏代码主要功能用来判断是否改变了单元格的值。

宏类型

类型

对象

事件

ClientSide

spreadsheetReport

onWriteBackCellValueChanged

宏代码

function main(spreadsheetReport, editingCellPosition, oldValue, newValue, newDisplayValue) {
    //标志位,用于后面在执行onRender宏事件时判断是否需要更新writeBackData
    spreadsheetReport.spreadsheetReportWriteBack.cellValueChanged = true;
}

此方法仅支持数字类型

资源下载

资源:migrate.xml