Saiku Table展示数据合并bug修复(二十五)

Saiku Table展示数据合并bug修复

 

Saiku以table的形式展示数据,如果点击了 非空的字段 按钮,则会自动进行数据合并,为空的数据行以及数据列都会自动隐藏掉。

 

首先我们应该定位问题:

1.查看接口返回值,会发现接口返回都正常,数值没有任何问题,所以我们能清楚的知道与后台没有关系。

2.从页面上定位问题,会发现是table渲染问题 : /saiku-ui/js/saiku/render/SaikuTableRenderer.js  (如果是编译好的saiku,请找到 saiku-server\tomcat\webapps\ROOT\js\saiku\render\SaikuTableRenderer.js  )

 

接下来我们来分析一下问题原因:

1.渲染页面主要是 SaikuTableRenderer.js文件中的此方法:

  SaikuTableRenderer.prototype.internalRender = function(allData, options)

2.数据内容都存放在<table>标签中,存放内容为 tableContent,tableContent包含所有的数据信息。

js代码里面主要有两层for循环,将数据取出然后以字符串形式拼凑到 tableContent 里面,最后返回到html页面中渲染。

第一次是循环取出行数据信息

  for (var row = 0, rowLen = table.length; row < rowLen; row++) 

第二层是第一层循环中的行数据中取出对应的列数据,然后拼凑为<tr>标记的行数据 rowContent 里面 

 for (var col = 0, colLen = table[row].length; col < colLen; col++) 

3.主要逻辑:

有个boolean类型的 same 标记,用来判断上下文数据信息是否一致,如果一致 same=true,后面就会自动合并

 var same = !headerSame && !isHeaderLowestLvl && (col == 0 || !topParentsDiffer(data, row, col)) && header.value === previousRow[col].value ;

我们主要来看一下这个值:header.value === previousRow[col].value ,意思就是说当前记录列值与上一条记录的列数据相同时,same就为true,让其自动合并

  自动合并的原理: 将当前记录的此列值置为空

   var value = (same ? "<div>&nbsp;</div>" : '<div rel="' + row + ":" + col + '">' + (sameAsPrevValue && Settings.ALLOW_TABLE_DATA_COLLAPSE ? '<span class="expander expanded" style="cursor: pointer;">&#9660;</span>' : '' ) + header.value + '</div>');

不断的循环出当前行的列值,然后将列值拼凑为 rowContent

   rowContent += '<th class="' + cssclass + '" ' + (colspan > 0 ? ' colspan="' + colspan + '"' : "") + tipsy + '>' + value + '</th>';

但是下面还有个判断,就是当数据(DATA_CELL 就是标记度量值信息)为空时,saiku会将当前rowContent置为空 

  

这里三个条件分别是: 

  用户是否点击了隐藏空数据按钮,也就是我们开头说的 非空的字段 按钮 : option.hideEmpty

  当前数据信息 header.type属性 是否为 DATA_CELL 类型

  当前行数据的指标信息是否都为空 (这里上方有代码如判断,我没有全部截取图片): rowWithOnlyEmptyCells

 

这样子一来,问题就暴露啦

如果我有两个部门的数据信息,每个部门个 3条数据,第一个部门三条数据都完整,第二个部门第一条数据 指标值都为空,后面两条数据完整

以上逻辑就会这样判断:

  第一个部门数据正常展示,部门信息相同会自动合并,后面的数据也会正常展示,这个没问题

  第二个部门的数据,第一条数据 首先部门不会合并到第一个部门,因为当前数据的部门value值与上一条记录的部门value值不同,但是由于cell指标数据为空,所以 rowContent最后就会置为空,相当于不展示此条数据

  第二个部门第二条数据,部门数据会先判断当前数据的value是否与上一条数据的value相同,结果会发现相同,就会把当前数据的部门信息置为空,后面因为有cell指标数据,所以后面就会展示这条rowContent信息,但是此条数据不完整了,没有了部门名称信息。

  第二个部门第三条数据同第二条数据一样。

最后我们就会得出这样的结果:

  第一个部门的数据正常展示

  第二个部门的数据由于都没有部门信息就会自动合并到第一个部门去展示。

 

其次我们来讲一下处理方案:

能定位到问题,了解问题原因离解决问题就不远了哈哈哈哈

我这边的处理方案是增加一个boolean类型标记值 headerFlag,用于决定same值,因为其实数据展示的时候是否合并就是在用same的取值

这个headerFlag值定义在两个for循环以外,默认为true,意思就是默认情况下影响数据的合并展示

headerFlag定义

 

根据headerFlag的值来定义same值

当有rowContent被置为空的时候,就将headerFlag标记置为false,就是为了保证当前一行数据为空时,下一次不会自动将部门列信息置为空了

 

接下来我们就应该开始考虑什么时候讲headerFlag标记转为true了啦

 有以下两种情况我们需要考虑到

>>>  当前行的列数据值不等于上一行数据的列数据值时,表示这是一次新的判断,我们将headerFlag置为true,一切按照原来的一样

>>> 当前tableContent中包含当前行列数据信息时,表示当前列已经存在了,我们可以让它去做合并了,就将headerFlag置为true.

 

这样子我们就能处理好这个table自动合并的bug了,可能还有更好的办法或者是有全的解法,如有还望不吝赐教哈,多谢~

 

最后提供完整的 SaikuTableRenderer.js文件(修改后的)

function SaikuTableRenderer(data, options) {this._data = data;this._options = _.extend({}, SaikuRendererOptions, options);
}function getAxisLevelsName(data, axisName) {var queryData = data.query.queryModel.axes[axisName].hierarchies;var len = queryData.length;var arrLevels = [];for (var i = 0; i < len; i++) {for (var level in queryData[i].levels) {if (queryData[i].levels.hasOwnProperty(level)) {if (Settings.COLUMN_TITLE_TABLE_USE_LEVEL_CAPTION_NAME) {arrLevels.push(queryData[i].levels[level].caption);}else {arrLevels.push(level);}}}}return arrLevels;
}function setStyleNegativeNumber(value) {var className = '';if (Settings.STYLE_NEGATIVE_NUMBER && parseFloat(value) < 0) {className = ' style_negative_number ';}return className;
}function getAxisSize(data, axisName) {var queryData = data.query.queryModel.axes[axisName].hierarchies;var len = queryData.length;var axisSize = 0;for (var i = 0; i < len; i++) {axisSize += _.size(queryData[i].levels);}return axisSize;
}function getDomColumnsLevelsName(htmlObject) {var $htmlObject = $(htmlObject.closest('.workspace').find('.workspace_fields').find('.columns.axis_fields').find('.hierarchy').find('.d_level'));var arrLevels = [];$.each($htmlObject, function(key, level) {if ($(level).attr('style') === 'display: list-item;') {if (Settings.COLUMN_TITLE_TABLE_USE_LEVEL_CAPTION_NAME) {arrLevels.push($(level).find('.level').attr('title'));}else {arrLevels.push($(level).find('.level').attr('level'));}}});return arrLevels;
}/*table render method*/
SaikuTableRenderer.prototype.render = function(data, options) {var self = this;if (data) {this._data = data;}if (options) {this._options = _.extend({}, SaikuRendererOptions, options);}if (typeof this._data == "undefined") {return;}if (this._data != null && this._data.error != null) {return;}if (this._data == null || (this._data.cellset && this._data.cellset.length === 0)) {return;}this.hideEmpty = this._options.hideEmpty;if (this._options.htmlObject) {
//            $(this._options.htmlObject).stickyTableHeaders("destroy");// in case we have some left over scrollersif (self._options.hasOwnProperty('batch')) {$(self._options.htmlObject).parent().parent().unbind('scroll');}_.defer(function(that) {if (self._options.hasOwnProperty('batch') && !self._options.hasOwnProperty('batchSize')) {self._options['batchSize'] = 1000;}// the key method to render data by table form. 20190423var html =  self.internalRender(self._data, self._options);$(self._options.htmlObject).html(html);// Render the totals summary$('#totals_summary').remove(); // Remove one previous totals div, if present$(self._options.htmlObject).after(self.renderSummary(data)); // Render the new summary//                $(self._options.htmlObject).stickyTableHeaders( { container: self._options.htmlObject.parent().parent(), fixedOffset: self._options.htmlObject.parent().parent().offset().top });_.defer(function(that) {if (self._options.hasOwnProperty('batch') && self._options.hasBatchResult) {var batchRow = 0;var batchIsRunning = false;var batchIntervalSize = self._options.hasOwnProperty('batchIntervalSize') ? self._options.batchIntervalSize : 20;var batchIntervalTime = self._options.hasOwnProperty('batchIntervalTime') ? self._options.batchIntervalTime : 20;var len = self._options.batchResult.length;var batchInsert = function() {// maybe add check for reach table bottom - ($('.workspace_results').scrollTop() , $('.workspace_results table').height()if (!batchIsRunning && len > 0 && batchRow < len) {batchIsRunning = true;var batchContent = "";var startb = batchRow;for (var i = 0;  batchRow < len && i < batchIntervalSize ; i++, batchRow++) {batchContent += self._options.batchResult[batchRow];}if (batchRow > startb) {$(self._options.htmlObject).append( $(batchContent));}batchIsRunning = false;}if (batchRow >= len) {$(self._options.htmlObject).parent().parent().unbind('scroll');}};var lazyBatchInsert = _.debounce(batchInsert, batchIntervalTime);$(self._options.htmlObject).parent().parent().scroll(function () {lazyBatchInsert();});}});return html;});} else {var html =  this.internalRender(this._data, self._options);return html;}};SaikuTableRenderer.prototype.clear = function(data, options) {var self = this;if (this._options && this._options.htmlObject && this._options.hasOwnProperty('batch')) {$(self._options.htmlObject).parent().parent().unbind('scroll');}};SaikuTableRenderer.prototype.processData = function(data, options) {this._hasProcessed = true;
};function genTotalDataCells(currentIndex, cellIndex, scanSums, scanIndexes, lists) {var contents = '';var lists = lists[ROWS];for (var i = scanSums.length - 1; i >= 0; i--) {if (currentIndex == scanSums[i]) {var currentListNode = lists[i][scanIndexes[i]];for (var m = 0; m < currentListNode.cells.length; m++) {contents += '<td class="data total">' + currentListNode.cells[m][cellIndex].value + '</td>';}scanIndexes[i]++;if (scanIndexes[i] < lists[i].length)scanSums[i] += lists[i][scanIndexes[i]].width;}}return contents;
}function genTotalHeaderCells(currentIndex, bottom, scanSums, scanIndexes, lists, wrapContent) {var contents = '';for (var i = bottom; i >= 0; i--) {if (currentIndex == scanSums[i]) {var currentListNode = lists[i][scanIndexes[i]];var cssClass;if (i == 0 && bottom == 1)cssClass = "col";else if (i == bottom)cssClass = "col_total_corner";else if (i == bottom - 1 && currentListNode.captions)cssClass = "col_total_first";else cssClass = "col_null";for (var m = 0; m < currentListNode.cells.length; m++) {var text = ' ';if (bottom == lists.length - 1) {if (currentListNode.captions) {text = lists[i][scanIndexes[i]].captions[m];}if (i == 0 && scanIndexes[i] == 0) {if (currentListNode.captions)text += " ";else text = "";text += (wrapContent ? "<span class='i18n'>Grand Total</span>" :  "Grand Total");}}contents += '<th class="' + cssClass + '">'+ (wrapContent ? '<div>' + text + '</div>' : text ) + '</th>';}scanIndexes[i]++;if (scanIndexes[i] < lists[i].length)scanSums[i] += lists[i][scanIndexes[i]].width;}}return contents;
}function totalIntersectionCells(currentIndex, bottom, scanSums, scanIndexes, lists) {var contents = '';for (var i = bottom; i >= 0; i--) {if (currentIndex == scanSums[i]) {var currentListNode = lists[i][scanIndexes[i]];var cssClass = "data total";for (var m = 0; m < currentListNode.cells.length; m++) {var text = ' ';contents += '<td class="' + cssClass + '">' + text + '</td>';}scanIndexes[i]++;if (scanIndexes[i] < lists[i].length)scanSums[i] += lists[i][scanIndexes[i]].width;}}return contents;
}function isNextTotalsRow(currentIndex, scanSums, scanIndexes, totalsLists, wrapContent) {var colLists = totalsLists[COLUMNS];var colScanSums = scanSums[COLUMNS];var colScanIndexes = scanIndexes[COLUMNS];var bottom = colLists.length - 2;var contents = -1;for (var i = bottom; i >= 0; i--) {if (currentIndex == colScanSums[i]) {for (var m = 0; m < colLists[i][colScanIndexes[i]].cells.length; m++) {contents += '<tr>';for (var j = 0; j <= bottom; j++) {var cssClass;var text = ' ';if (i == 0 && j == 0)cssClass = 'row';else if (i == j + 1){cssClass = 'row_total_corner';return j;}else if (i == j && colLists[i][colScanIndexes[i]].captions) {cssClass = 'row_total_first';} else if (i < j + 1)cssClass = 'row_total';elsecssClass = 'row_null';if (j == bottom ) {if (colLists[i][colScanIndexes[i]].captions) {text = colLists[i][colScanIndexes[i]].captions[m];}if (i == 0 && colScanIndexes[i] == 0) {if (colLists[i][colScanIndexes[i]].captions)text += " ";else text = "";text += (wrapContent ? "<span class='i18n'>Grand Total</span>" :  "Grand Total");}}}}}}return -1;
}function genTotalHeaderRowCells(currentIndex, scanSums, scanIndexes, totalsLists, wrapContent) {var colLists = totalsLists[COLUMNS];var colScanSums = scanSums[COLUMNS];var colScanIndexes = scanIndexes[COLUMNS];var bottom = colLists.length - 2;var contents = '';for (var i = bottom; i >= 0; i--) {if (currentIndex == colScanSums[i]) {for (var m = 0; m < colLists[i][colScanIndexes[i]].cells.length; m++) {contents += '<tr>';for (var j = 0; j <= bottom; j++) {var cssClass;var text = ' ';if (i == 0 && j == 0)cssClass = 'row';else if (i == j + 1)cssClass = 'row_total_corner';else if (i == j && colLists[i][colScanIndexes[i]].captions) {cssClass = 'row_total_first';} else if (i < j + 1)cssClass = 'row_total';elsecssClass = 'row_null';if (j == bottom ) {if (colLists[i][colScanIndexes[i]].captions) {text = colLists[i][colScanIndexes[i]].captions[m];}if (i == 0 && colScanIndexes[i] == 0) {if (colLists[i][colScanIndexes[i]].captions)text += " ";else text = "";text += (wrapContent ? "<span class='i18n'>Grand Total</span>" :  "Grand Total");}}contents += '<th class="' + cssClass + '">'+ (wrapContent ? '<div>' + text + '</div>' : text ) + '</th>';}var scanIndexes = {};var scanSums = {};if (totalsLists[ROWS]) {for (var z = 0; z < totalsLists[ROWS].length; z++) {scanIndexes[z] = 0;scanSums[z] = totalsLists[ROWS][z][scanIndexes[z]].width;}}for (var k = 0; k < colLists[i][colScanIndexes[i]].cells[m].length; k++) {contents += '<td class="data total">' + colLists[i][colScanIndexes[i]].cells[m][k].value + '</td>';if (totalsLists[ROWS]) {contents += totalIntersectionCells(k + 1, totalsLists[ROWS].length - 1, scanSums, scanIndexes, totalsLists[ROWS]);}}contents += '</tr>';}colScanIndexes[i]++;if (colScanIndexes[i] < colLists[i].length) {colScanSums[i] += colLists[i][colScanIndexes[i]].width;}}}return contents;
}var ROWS = "ROWS";
var COLUMNS = "COLUMNS";function nextParentsDiffer(data, row, col) {while (row-- > 0) {if (data[row][col].properties.uniquename != data[row][col + 1].properties.uniquename)return true;}return false;
}function topParentsDiffer(data, row, col) {while (col-- > 0)if (data[row][col].properties.uniquename != data[row - 1][col].properties.uniquename)return true;return false;
}/*** This function is intended to traverse the totals arrays and cleanup empty* totals. This will optimize the query result on screen, displaying just the* needed cells.* @param dirs The direction array ['ROWS', 'COLUMNS']* @param totalsLists The totals from allData.rowTotalsLists and allData.colTotalsLists.*/
function cleanupTotals(dirs, totalsLists) {// For each direction (ROWS/COLUMNS)for (var dirIndex = 0; dirIndex < dirs.length; dirIndex++) {var dir = dirs[dirIndex];// If there are defined totalsif (totalsLists[dir]) {var isEmpty = true; // A flag to indicate if this total is emptyfor (var row = 0; row < totalsLists[dir].length; row++) {var totalsInfoArray = totalsLists[dir][row];for (var totalIndex = 0; totalIndex < totalsInfoArray.length; totalIndex++) {var cells = totalsLists[dir][row][totalIndex].cells;for (var cellIndex = 0; cellIndex < cells.length; cellIndex++) {var cellArray = cells[cellIndex];// For each total cellfor (var i = 0; i < cellArray.length; i++) {var cell = cellArray[i];// If it contains a value different from emptyif (cell.value !== '-') {isEmpty = false; // So, this total is not empty}}}}}if (isEmpty) { // If this total is emptytotalsLists[dir] = null; // Remove it}}}
}/*the main method to render data by table form. 20190423*/
SaikuTableRenderer.prototype.internalRender = function(allData, options) {var tableContent = "";var rowContent = "";var data = allData.cellset;var newRowContent = '';var arrRowData = [];var objRowData = [];var table = data ? data : [];var colSpan;var colValue;var isHeaderLowestLvl;var isBody = false;var firstColumn;var isLastColumn, isLastRow;var nextHeader;var processedRowHeader = false;var lowestRowLvl = 0;var rowGroups = [];var batchSize = null;var batchStarted = false;var isColHeader = false, isColHeaderDone = false;var resultRows = [];var wrapContent = true;if (options) {batchSize = options.hasOwnProperty('batchSize') ? options.batchSize : null;wrapContent = options.hasOwnProperty('wrapContent') ? options.wrapContent : true;}var totalsLists = {};totalsLists[COLUMNS] = allData.rowTotalsLists;totalsLists[ROWS] = allData.colTotalsLists;var scanSums = {};var scanIndexes = {};var dirs = [ROWS, COLUMNS];var hasMeasures = allData.query && allData.query.queryModel && allData.query.queryModel.details? allData.query.queryModel.details.measures.length: 0;if (typeof this._options.htmlObject === 'object' &&Settings.ALLOW_AXIS_COLUMN_TITLE_TABLE &&hasMeasures > 0 &&allData.query.type === 'QUERYMODEL' &&allData.query.queryModel.details.axis === 'COLUMNS' &&allData.query.queryModel.details.location === 'BOTTOM') {var arrColumnTitleTable = getAxisLevelsName(allData, COLUMNS);var arrDomColumnTitleTable = getDomColumnsLevelsName(this._options.htmlObject);var colspanColumnTitleTable = getAxisSize(allData, ROWS);var auxColumnTitleTable = 0;if (arrColumnTitleTable.length === arrDomColumnTitleTable.length) {arrColumnTitleTable = arrDomColumnTitleTable;}else {arrColumnTitleTable = _.intersection(arrDomColumnTitleTable, arrColumnTitleTable);}}for (var i = 0; i < dirs.length; i++) {scanSums[dirs[i]] = new Array();scanIndexes[dirs[i]] = new Array();}// Here we cleaup the empty totalscleanupTotals(dirs, totalsLists);if (totalsLists[COLUMNS]) {for (var i = 0; i < totalsLists[COLUMNS].length; i++) {scanIndexes[COLUMNS][i] = 0;scanSums[COLUMNS][i] = totalsLists[COLUMNS][i][scanIndexes[COLUMNS][i]].width;}}var headerFlag=true;// add this flag to solve the bug when same data to merge。 20190423for (var row = 0, rowLen = table.length; row < rowLen; row++) {var rowShifted = row - allData.topOffset;colSpan = 1;colValue = "";isHeaderLowestLvl = false;isLastColumn = false;isLastRow = false;isColHeader = false;var headerSame = false;if (totalsLists[ROWS]) {for (var i = 0; i < totalsLists[ROWS].length; i++) {scanIndexes[ROWS][i] = 0;scanSums[ROWS][i] = totalsLists[ROWS][i][scanIndexes[ROWS][i]].width;}}rowWithOnlyEmptyCells = true;rowContent = "<tr>";var header = null;if (row === 0) {rowContent = "<thead>" + rowContent;}if (typeof this._options.htmlObject === 'object' &&Settings.ALLOW_AXIS_COLUMN_TITLE_TABLE &&hasMeasures > 0 &&allData.query.type === 'QUERYMODEL' &&allData.query.queryModel.details.axis === 'COLUMNS' &&allData.query.queryModel.details.location === 'BOTTOM' &&auxColumnTitleTable < arrColumnTitleTable.length) {rowContent += '<th class="row_header" style="text-align: right;" colspan="' + colspanColumnTitleTable + '" title="' + arrColumnTitleTable[auxColumnTitleTable] + '">'+ (wrapContent ? '<div>' + arrColumnTitleTable[auxColumnTitleTable] + '</div>' : arrColumnTitleTable[auxColumnTitleTable])+ '</th>';auxColumnTitleTable += 1;}for (var col = 0, colLen = table[row].length; col < colLen; col++) {var colShifted = col - allData.leftOffset;header = data[row][col];if (header.type === "COLUMN_HEADER") {isColHeader = true;}// If the cell is a column header and is null (top left of table)if (header.type === "COLUMN_HEADER" && header.value === "null" && (firstColumn == null || col < firstColumn)) {if (((!Settings.ALLOW_AXIS_COLUMN_TITLE_TABLE || (Settings.ALLOW_AXIS_COLUMN_TITLE_TABLE && allData.query.queryModel.details.location !== 'BOTTOM')) || hasMeasures === 0) ||allData.query.type === 'MDX') {rowContent += '<th class="all_null"> </th>';}} // If the cell is a column header and isn't null (column header of table)else if (header.type === "COLUMN_HEADER") {if (firstColumn == null) {firstColumn = col;}if (table[row].length == col+1)isLastColumn = true;elsenextHeader = data[row][col+1];if (isLastColumn) {// Last column in a row...if (header.value == "null") {rowContent += '<th class="col_null"> </th>';} else {if (totalsLists[ROWS])colSpan = totalsLists[ROWS][row + 1][scanIndexes[ROWS][row + 1]].span;rowContent += '<th class="col" style="text-align: center;" colspan="' + colSpan + '" title="' + header.value + '">'+ (wrapContent ? '<div rel="' + row + ":" + col +'">' + header.value + '</div>' : header.value)+ '</th>';}} else {// All the rest...var groupChange = (col > 1 && row > 1 && !isHeaderLowestLvl && col > firstColumn) ?data[row-1][col+1].value != data[row-1][col].value || data[row-1][col+1].properties.uniquename != data[row-1][col].properties.uniquename: false;var maxColspan = colSpan > 999 ? true : false;if (header.value != nextHeader.value || nextParentsDiffer(data, row, col) || isHeaderLowestLvl || groupChange || maxColspan) {if (header.value == "null") {rowContent += '<th class="col_null" colspan="' + colSpan + '"> </th>';} else {if (totalsLists[ROWS])colSpan = totalsLists[ROWS][row + 1][scanIndexes[ROWS][row + 1]].span;rowContent += '<th class="col" style="text-align: center;" colspan="' + (colSpan == 0 ? 1 : colSpan) + '" title="' + header.value + '">'+ (wrapContent ? '<div rel="' + row + ":" + col +'">' + header.value + '</div>' : header.value)+ '</th>';}colSpan = 1;} else {colSpan++;}}if (totalsLists[ROWS])rowContent += genTotalHeaderCells(col - allData.leftOffset + 1, row + 1, scanSums[ROWS], scanIndexes[ROWS], totalsLists[ROWS], wrapContent);} // If the cell is a row header and is null (grouped row header)else if (header.type === "ROW_HEADER" && header.value === "null") {rowContent += '<th class="row_null"> </th>';} // If the cell is a row header and isn't null (last row header)else if (header.type === "ROW_HEADER") {if (lowestRowLvl == col)isHeaderLowestLvl = true;elsenextHeader = data[row][col+1];var previousRow = data[row - 1];var nextRow = data[row + 1];// when  same set fixed value is false ,It means the same data will not merge。table data will  show row by row.20190423//var same=false; /*judge the current value and previousRow value,if equals ,all set comeback,set the headerFlag is true, we can judge the data as usual. 20190423*/if(header.value !== previousRow[col].value){headerFlag =true;}/*judge the tableContent include value or not, if include ,set the headerFlag value is true to avoid repeat datas showed in table.20190423*/if(tableContent.indexOf(header.value) > -1 ){headerFlag =true;}/*add headerFlag to judge the data is same ,then control the data merge wheather or not.20190423 */var same = !headerSame && !isHeaderLowestLvl && (col == 0 || !topParentsDiffer(data, row, col)) && header.value === previousRow[col].value && headerFlag;headerSame = !same;var sameAsPrevValue = false;if(Settings.ALLOW_TABLE_DATA_COLLAPSE){if (row > 0 && row < rowLen - 1) {if (totalsLists[ROWS] == null || (col <= colLen - totalsLists[ROWS].length - 1)) {var checkOther = true;if (totalsLists[COLUMNS] && rowShifted >= 0 && col <= isNextTotalsRow(rowShifted + 1, scanSums, scanIndexes, totalsLists, wrapContent)) {sameAsPrevValue = true;checkOther = false;}if (checkOther && nextRow[col].value == header.value) {if (col > 0) {for (var j = 0; j < col; j++) {if (nextRow[j].value == data[row][j].value) {sameAsPrevValue = true;} else {sameAsPrevValue = false;break;}}} else {sameAsPrevValue = true;}}}} else if(row > 0 && row == rowLen - 1) {if (totalsLists[COLUMNS] && rowShifted >= 0 && col <= isNextTotalsRow(rowShifted + 1, scanSums, scanIndexes, totalsLists, wrapContent)) {sameAsPrevValue = true;}}}var value = (same ? "<div> </div>" : '<div rel="' + row + ":" + col + '">'+ (sameAsPrevValue && Settings.ALLOW_TABLE_DATA_COLLAPSE ? '<span class="expander expanded" style="cursor: pointer;">▼</span>' : '' ) + header.value + '</div>');if (!wrapContent) {value = (same ? " " : header.value );}var tipsy = "";/* var tipsy = ' original-title="';if (!same && header.metaproperties) {for (key in header.metaproperties) {if (key.substring(0,1) != "$" && key.substring(1,2).toUpperCase() != key.substring(1,2)) {tipsy += "<b>" + safe_tags_replace(key) + "</b> : " + safe_tags_replace(header.metaproperties[key]) + "<br>";}}}tipsy += '"';*/var cssclass = (same ? "row_null" : "row");var colspan = 0;if (!isHeaderLowestLvl && (typeof nextHeader == "undefined" || nextHeader.value === "null")) {colspan = 1;var group = header.properties.dimension;var level = header.properties.level;var groupWidth = (group in rowGroups ? rowGroups[group].length - rowGroups[group].indexOf(level) : 1);for (var k = col + 1; colspan < groupWidth && k <= (lowestRowLvl+1) && data[row][k] !== "null"; k++) {colspan = k - col;}col = col + colspan -1;}/*when the content is to long ,we will set new line to show it.*/// eg value: <div rel="3:0">新業務及保單行政部</div>if(cssclass == "row" && value.length>0){var startPos = value.indexOf('>'); //find  start position of the tag. eg: <div rel="3:0">var endPos = value.lastIndexOf('<'); //find  end position of the tag. eg: </div>var tmpValue = value.substr( startPos+1 ,endPos-startPos-1); // get the content value. eg: 新業務及保單行政部//将value值每隔40个字自动加上换行符//each 40 character add one <br> tag to get new line. if(tmpValue.length>120){ tmpValue = tmpValue.substr(0,40)+"<br/>"+tmpValue.substr(40,40)+"<br/>"+tmpValue.substr(80,40)+"<br/>"+tmpValue.substr(120,tmpValue.length-120);}else if(tmpValue.length>80){ tmpValue = tmpValue.substr(0,40)+"<br/>"+tmpValue.substr(40,40)+"<br/>"+tmpValue.substr(80,tmpValue.length-80);}else if(tmpValue.length>40){tmpValue = tmpValue.substr(0,40)+"<br/>"+tmpValue.substr(40,tmpValue.length-40);}// compared with old value, this value only add <br> tag for show data in table more beautiful.value = value.substr(0,startPos+1) + tmpValue + value.substr(endPos,value.length-endPos);}rowContent += '<th class="' + cssclass + '" ' + (colspan > 0 ? ' colspan="' + colspan + '"' : "") + tipsy + '>' + value + '</th>';}else if (header.type === "ROW_HEADER_HEADER") {var hierName = function(data) {var hier = data.properties.hierarchy;var name = hier.replace(/[\[\]]/gi, '').split('.')[1]? hier.replace(/[\[\]]/gi, '').split('.')[1]: hier.replace(/[\[\]]/gi, '').split('.')[0];return name;};var arrPosRowData = [];if (_.contains(arrRowData, header.value)) {for (var i = 0; i < arrRowData.length; i++) {if (arrRowData[i] === header.value) {arrPosRowData.push(i);}}arrPosRowData.push(col);}rowContent += '<th class="row_header">' + (wrapContent ? '<div>' + header.value + '</div>' : header.value) + '</th>';arrRowData.push(header.value);objRowData.push({name: header.value,hierName: hierName(header) + '/' + header.value});isHeaderLowestLvl = true;processedRowHeader = true;lowestRowLvl = col;if (header.properties.hasOwnProperty("dimension")) {var group = header.properties.dimension;if (!(group in rowGroups)) {rowGroups[group] = [];}rowGroups[group].push(header.properties.level);}if (arrPosRowData.length > 0) {var aux = 0;rowContent = '<tr>';if (row === 0) {rowContent = '<thead>' + rowContent;}for (var i = 0; i < objRowData.length; i++) {if (arrPosRowData[aux] === i) {newRowContent += '<th class="row_header">' + (wrapContent ? '<div>' + objRowData[i].hierName + '</div>' : objRowData[i].hierName) + '</th>';aux += 1;}else {newRowContent += '<th class="row_header">' + (wrapContent ? '<div>' + objRowData[i].name + '</div>' : objRowData[i].name) + '</th>';}}rowContent += newRowContent;}} // If the cell is a normal data cellelse if (header.type === "DATA_CELL") {batchStarted = true;var color = "";var val = _.isEmpty(header.value) ? Settings.EMPTY_VALUE_CHARACTER : header.value;var arrow = "";if (header.properties.hasOwnProperty('image')) {var img_height = header.properties.hasOwnProperty('image_height') ? " height='" + header.properties.image_height + "'" : "";var img_width = header.properties.hasOwnProperty('image_width') ? " width='" + header.properties.image_width + "'" : "";val = "<img " + img_height + " " + img_width + " style='padding-left: 5px' src='" + header.properties.image + "' border='0'>";}// Just apply formatting to non-empty cellsif (val !== '-' && val !== '' && header.properties.hasOwnProperty('style')) {color = " style='background-color: " + header.properties.style + "' ";}if (header.properties.hasOwnProperty('link')) {val = "<a target='__blank' href='" + header.properties.link + "'>" + val + "</a>";}if (header.properties.hasOwnProperty('arrow')) {arrow = "<img height='10' width='10' style='padding-left: 5px' src='./images/arrow-" + header.properties.arrow + ".gif' border='0'>";}if (val !== '-' && val !== '') {rowWithOnlyEmptyCells = false;}rowContent += '<td class="data" ' + color + '>'+ (wrapContent ? '<div class="datadiv '+ setStyleNegativeNumber(header.properties.raw) + '" alt="' + header.properties.raw + '" rel="' + header.properties.position + '">' : "")+ val + arrow+ (wrapContent ? '</div>' : '') + '</td>';if (totalsLists[ROWS])rowContent += genTotalDataCells(colShifted + 1, rowShifted, scanSums[ROWS], scanIndexes[ROWS], totalsLists, wrapContent);}}rowContent += "</tr>";// Change it to let hideEmpty true by defaultif (options.hideEmpty && header.type === "DATA_CELL" && rowWithOnlyEmptyCells) {/*when data_cell is null,set the headerFlag is false ,to fix the problem data merge inccrrect.*/headerFlag=false;rowContent = '';}var totals = "";if (totalsLists[COLUMNS] && rowShifted >= 0) {totals += genTotalHeaderRowCells(rowShifted + 1, scanSums, scanIndexes, totalsLists, wrapContent);}if (batchStarted && batchSize) {if (row <= batchSize) {if (!isColHeader && !isColHeaderDone) {tableContent += "</thead><tbody>";isColHeaderDone = true;}tableContent += rowContent;if (totals.length > 0) {tableContent += totals;}} else {resultRows.push(rowContent);if (totals.length > 0) {resultRows.push(totals);}}} else {if (!isColHeader && !isColHeaderDone) {tableContent += "</thead><tbody>";isColHeaderDone = true;}tableContent += rowContent;if (totals.length > 0) {tableContent += totals;}}}if (options) {options['batchResult'] = resultRows;options['hasBatchResult'] = resultRows.length > 0;}return "<table>" + tableContent + "</tbody></table>";
};SaikuTableRenderer.prototype.renderSummary = function(data) {if (data && data.query) {var hasSomethingToRender = false;var measures = data.query.queryModel.details? data.query.queryModel.details.measures: [];var summaryData = {};for (var i = 0; i < measures.length; i++) {var m = measures[i];if (m.aggregators) {for (var j = 0; j < m.aggregators.length; j++) {var a = m.aggregators[j];if (a.indexOf('_') > 0) {var tokens = a.split('_');var aggregator = tokens[0];var axis = tokens[1];if (aggregator !== 'nil' && aggregator !== 'not') {hasSomethingToRender = true;aggregator = aggregator.capitalizeFirstLetter();if (!(axis in summaryData)) summaryData[axis] = [];summaryData[axis].push(m.name + ": " + aggregator);}}}}}if (hasSomethingToRender) {var summary = "<div id='totals_summary'><br/>";$.each(summaryData, function(key, aggregators) {summary += "<h3>" + key.capitalizeFirstLetter();for (var i = 0; i < aggregators.length; i++) {summary += "<br/> " + aggregators[i];}summary += "</h3>";});return summary + "</div>";}}return "";
};String.prototype.capitalizeFirstLetter = function() {return this.charAt(0).toUpperCase() + this.slice(1).toLowerCase();
}

  

 

转载于:https://www.cnblogs.com/DFX339/p/10758503.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/248092.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

软件开发工具(第2章:软件开发过程及其组织)

一、软件开发的困难 程序员做好软件工作的关键是什么&#xff1f; 关键是两个转换&#xff1a; 第一个转换是用户对软件功能的理解与程序员对软件功能的理解之间的转换。 &#xff08;不同⾏业的⼈员对于事物的认识⽅法与描述⽅法是不同的。&#xff09; 第二个转换要解决的是人…

数据可视化(BI报表的开发)第四天

练习题&#xff1a; 先找出字符串 ‘8587263747153203552943982’ 中出现次数最多的数字及次数&#xff0c;然后去重后并排序&#xff08;不准使用sort&#xff09;&#xff0c;使得到结果为 ‘0123456789’。 要求1&#xff1a;找到出现最多的数字和出现的次数 // 1、找出出…

网络经济与企业管理(第 1 章:企业管理概论)

一、企业的概念 什么是企业&#xff1f; 1.企业以市场为导向&#xff0c;以价值增值作为经济活动的目的 2.企业是从事商品生产和流通的经济组织 3.企业经营过程中要自主经营、自负盈亏、独立核算和承担风险&#xff0c;具有法人资格的基本经济单位 企业发展的三个时期&#xff…

去死吧!USB转串口!!!

首先&#xff0c;这个题目有两种歧义&#xff1a;1、USB转232串口&#xff08;严格说就是这种&#xff09;&#xff01; 2、USB转USART串口&#xff08;通常都是这么叫&#xff0c;认为就是这&#xff0c;理论上是错误的&#xff0c;歧义所在&#xff09;&#xff01; USB转TTL…

IDEA微服务项目的application.yml没有绿色叶子的解决办法

1.今天在写微服务项目的时候成功入坑&#xff0c;那么问题是啥呢&#xff1f;接下来和我一起走入bug的世界吧&#xff0c;让我们看看究竟是怎么回事。 *问题描述    1.application.yml是灰色的小格子 2.实在难看 *需要解决的最终结果 1.有绿色的小叶子 解决方案上图 1. 2. …

Ajax — 第一天

上网的目的 本质目的&#xff1a;浏览和消费资源 资源&#xff1a;文字、图片、音频、视频…资源存在哪里&#xff1a;服务器上 服务器的概念 是负责存放和对外提供资源的电脑。它的性能比普通的计算机好太多了 客户端 获取和消费资源的电脑&#xff0c;叫做客户端。 我…

Swift傻傻分不清楚系列(四)基本运算符 o_O!???

本页包含内容&#xff1a; 术语赋值运算符算术运算符组合赋值运算符比较运算符三目运算符空合运算符区间运算符逻辑运算符 运算符是检查、改变、合并值的特殊符号或短语。例如&#xff0c;加号&#xff08;&#xff09;将两个数相加&#xff08;如 let i 1 2&#xff09;。更…

网络经济与企业管理(第 2 章:企业战略管理)

一、战略的性质与类型 什么是战略&#xff1a;企业发展的谋划 企业战略的三个层次&#xff1a; 企业总体战略&#xff1a;又称经营战略&#xff0c;是企业整体运营的指导思想事业层战略&#xff1a;又称竞争战略、业务层次战略、SBU战略&#xff0c; 指导具体战略经营单位。职能…

Ajax — 图书管理

注意&#xff1a;本项目基于 jQuery 文件下进行的 Ajax 请求项目&#xff0c;需要映入jQuery文件&#xff01; <body style"padding: 15px;"><!-- 添加图书的Panel面板 --><div class"panel panel-primary"><div class"panel-h…

错误: 找不到或无法加载主类 com.leyou.LeyouItemApplication Process finished with exit code 1...

在IDEA的使用过程中&#xff0c;经常断掉服务或者重启服务&#xff0c;最近断掉服务重启时突然遇到了一个启动报错&#xff1a; 错误&#xff1a;找不到或无法加载主类 猜测&#xff1a;1&#xff0c;未能成功编译&#xff1b; 尝试&#xff1a;菜单---》Build---》Rebuild Pro…

Ajax — 第二天

Ajax-02 今天内容&#xff1a; 安装浏览器插件 聊天机器人案例 Form表单提交 模板引擎&#xff08;新的概念&#xff0c;难点&#xff09; 两个案例 Chrome浏览器插件安装 安装步骤 下载插件的网站&#xff1a; https://www.gugeapps.net/ 无需扫码 http://www.cnplu…

Ajax — 评论列表

<body style"padding: 15px;"><!-- 评论面板 --><div class"panel panel-primary"><div class"panel-heading"><h3 class"panel-title">发表评论</h3></div><form class"panel-bod…

Swift傻傻分不清楚系列(七)控制流

本页包含内容&#xff1a; For-In 循环While 循环条件语句控制转移语句&#xff08;Control Transfer Statements&#xff09;提前退出检测 API 可用性 Swift提供了多种流程控制结构&#xff0c;包括可以多次执行任务的while循环&#xff0c;基于特定条件选择执行不同代码分支…

java课程之团队开发冲刺1.8

一.总结昨天进度 1.初步实现用户交互 增删课程表 二.遇到的困难 1.主界面一段程序一直报错 三.今天的任务 1.解决报错问题&#xff0c; 编写查询空教室功能 照片 燃尽图 转载于:https://www.cnblogs.com/qfsr/p/10873636.html

Ajax — 聊天机器人演示

<body><div class"wrap"><!-- 头部 Header 区域 --><div class"header"><h3>小思同学</h3><img src"img/person01.png" alt"icon" /></div><!-- 中间 聊天内容区域 --><div…

Ajax — 第三天

Ajax-03 模板引擎原理 正则回顾 区分正则方法和字符串方法 正则方法 test()exec() 字符串方法 match()replace()split()search() 正则方法由正则表达式调用&#xff1b;字符串方法由字符串调用&#xff1b; exec方法 功能&#xff1a;使用正则表达式匹配字符串&#xff0c…

d3.js 共享交换平台demo

今天在群里遇到一张图 遂来玩一玩&#xff0c;先来上图!! 点击相应按钮&#xff0c;开关线路&#xff0c;此项目的重点是计算相应图形的位置&#xff0c;由于是个性化项目就没有封装布局。好了直接上代码。 <!DOCTYPE html> <html lang"en"> <head&g…

Java知识系统回顾整理01基础05控制流程07结束外部循环

一、break是结束当前循环 二、结束当前循环实例 break; 只能结束当前循环 public class HelloWorld { public static void main(String[] args) { //打印单数 for (int i 0; i < 10; i) { for (int j 0; j < 1…

Ajax — 新闻列表

注意&#xff1a;本项目主要利用到了template&#xff0c;模板引擎进行编写 模板引擎代码下载地址 <div id"news-list"><!-- 这里放数据 --></div>.news-item {display: flex;border: 1px solid #eee;width: 700px;padding: 10px;margin-bottom: …

Ajax — 第四天

数据交换格式 XML 写法&#xff1a; 一个文档有且只有一个根标签标签必须闭合属性值必须加引号 如果说服务器返回的数据是xml格式的 前端需要把服务器返回的xml当做document对象来处理目前无法演示&#xff0c;自己写接口的时候&#xff0c;我们可以测试一下。 JSON 写法…