@antv/s2
Version:
effective spreadsheet render core lib
454 lines • 22 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PivotDataSet = void 0;
const lodash_1 = require("lodash");
const common_1 = require("../common");
const constant_1 = require("../common/constant");
const debug_1 = require("../common/debug");
const i18n_1 = require("../common/i18n");
const node_1 = require("../facet/layout/node");
const utils_1 = require("../utils");
const data_set_operate_1 = require("../utils/data-set-operate");
const pivot_data_set_1 = require("../utils/dataset/pivot-data-set");
const number_calculate_1 = require("../utils/number-calculate");
const sort_action_1 = require("../utils/sort-action");
const base_data_set_1 = require("./base-data-set");
const cell_data_1 = require("./cell-data");
class PivotDataSet extends base_data_set_1.BaseDataSet {
constructor() {
super(...arguments);
/**
* 排序优先级:
* 1、sortParams里的条件优先级高于原始数据
* 2、sortParams多个item:按照顺序优先级,排在后面的优先级高
* 3、item中多个条件:sortByField > sortFunc > sortBy > sortMethod
*/
this.handleDimensionValuesSort = () => {
(0, lodash_1.each)(this.sortParams, (item) => {
const { sortFieldId, sortByMeasure } = item;
// 万物排序的前提
if (!sortFieldId) {
return;
}
const originValues = [...(this.sortedDimensionValues[sortFieldId] || [])];
const result = (0, sort_action_1.handleSortAction)({
dataSet: this,
sortParam: item,
originValues,
isSortByMeasure: !(0, lodash_1.isEmpty)(sortByMeasure),
});
this.sortedDimensionValues[sortFieldId] = result;
this.handlePivotMetaSort(sortFieldId, result);
});
};
this.getTotalStatus = (query) => {
const { columns, rows } = this.fields;
const isTotals = (dimensions, isSubTotal) => {
if (isSubTotal) {
const firstDimension = (0, lodash_1.find)(dimensions, (item) => !(0, lodash_1.has)(query, item));
return !!(firstDimension && firstDimension !== (0, lodash_1.first)(dimensions));
}
return (0, lodash_1.every)(dimensions, (item) => !(0, lodash_1.has)(query, item));
};
return {
isRowGrandTotal: isTotals((0, pivot_data_set_1.filterExtraDimension)(rows)),
isRowSubTotal: isTotals(rows, true),
isColGrandTotal: isTotals((0, pivot_data_set_1.filterExtraDimension)(columns)),
isColSubTotal: isTotals(columns, true),
};
};
}
getExistValuesByDataItem(data, values) {
return (0, pivot_data_set_1.getExistValues)(data, values);
}
/**
* When data related config changed, we need
* 1、re-process config
* 2、re-transform origin data
* 3、sort and other things
* @param dataCfg
*/
setDataCfg(dataCfg) {
super.setDataCfg(dataCfg);
const { rows } = this.fields;
this.sortedDimensionValues = {};
this.rowPivotMeta = new Map();
this.colPivotMeta = new Map();
this.dimensionValuesCache = new Map();
this.transformIndexesData(this.originData, rows);
this.handleDimensionValuesSort();
}
transformIndexesData(data, rows) {
const { columns, values, valueInCols } = this.fields;
let result;
debug_1.DebuggerUtil.getInstance().debugCallback(debug_1.DEBUG_TRANSFORM_DATA, () => {
result = (0, pivot_data_set_1.transformIndexesData)({
rows: (0, pivot_data_set_1.getIndexFields)(rows),
columns: (0, pivot_data_set_1.getIndexFields)(columns),
values: values,
valueInCols: valueInCols,
data,
indexesData: this.indexesData,
sortedDimensionValues: this.sortedDimensionValues,
rowPivotMeta: this.rowPivotMeta,
colPivotMeta: this.colPivotMeta,
getExistValuesByDataItem: this.getExistValuesByDataItem,
});
this.indexesData = result.indexesData;
this.rowPivotMeta = result.rowPivotMeta;
this.colPivotMeta = result.colPivotMeta;
this.sortedDimensionValues = result.sortedDimensionValues;
});
return result;
}
/**
* Provide a way to append some drill-down data in indexesData
* @param extraRowField
* @param drillDownData
* @param rowNode
*/
transformDrillDownData(extraRowField, drillDownData, rowNode) {
var _a;
const currentRowFields = node_1.Node.getFieldPath(rowNode, true);
const nextRowFields = [...currentRowFields, extraRowField];
const store = this.spreadsheet.store;
// 2. 检查该节点是否已经存在下钻维度
const rowNodeId = rowNode === null || rowNode === void 0 ? void 0 : rowNode.id;
const idPathMap = (_a = store.get('drillDownIdPathMap')) !== null && _a !== void 0 ? _a : new Map();
if (idPathMap.has(rowNodeId)) {
// the current node has a drill-down field, clean it
(0, lodash_1.forEach)(idPathMap.get(rowNodeId), (path) => {
(0, lodash_1.unset)(this.indexesData, path);
});
(0, pivot_data_set_1.deleteMetaById)(this.rowPivotMeta, rowNodeId);
}
// 3、转换数据
const { paths: drillDownDataPaths } = this.transformIndexesData(drillDownData, nextRowFields);
/*
* 4、record data paths by nodeId
* set new drill-down data path
*/
idPathMap.set(rowNodeId, drillDownDataPaths);
store.set('drillDownIdPathMap', idPathMap);
}
/**
* Clear drill down data by rowNodeId
* rowNodeId is undefined => clear all
* @param rowNodeId
*/
clearDrillDownData(rowNodeId) {
const store = this.spreadsheet.store;
const idPathMap = store.get('drillDownIdPathMap');
if (!idPathMap) {
return false;
}
const drillDownDataCache = store.get('drillDownDataCache', []);
if (rowNodeId) {
// 1. 删除 indexesData 当前下钻层级对应数据
const currentIdPathMap = idPathMap.get(rowNodeId);
if (currentIdPathMap) {
(0, lodash_1.forEach)(currentIdPathMap, (path) => {
(0, lodash_1.unset)(this.indexesData, path);
});
}
// 2. 删除 rowPivotMeta 当前下钻层级对应 meta 信息
(0, pivot_data_set_1.deleteMetaById)(this.rowPivotMeta, rowNodeId);
// 3. 删除下钻缓存路径
idPathMap.delete(rowNodeId);
// 4. 过滤清除的下钻缓存
const restDataCache = (0, lodash_1.filter)(drillDownDataCache, (cache) => idPathMap.has(cache === null || cache === void 0 ? void 0 : cache.rowId));
store.set('drillDownDataCache', restDataCache);
// 5. 过滤清除的下钻层级
const restDrillLevels = restDataCache.map((cache) => cache === null || cache === void 0 ? void 0 : cache.drillLevel);
const drillDownFieldInLevel = store.get('drillDownFieldInLevel', []);
const restFieldInLevel = drillDownFieldInLevel.filter((filed) => (0, lodash_1.includes)(restDrillLevels, filed === null || filed === void 0 ? void 0 : filed.drillLevel));
store.set('drillDownFieldInLevel', restFieldInLevel);
}
else {
idPathMap.clear();
/*
* 需要对应清空所有下钻后的dataCfg信息
* 因此如果缓存有下钻前原始dataCfg,需要清空所有的下钻数据
*/
const originalDataCfg = this.spreadsheet.store.get('originalDataCfg');
if (!(0, lodash_1.isEmpty)(originalDataCfg)) {
this.spreadsheet.setDataCfg(originalDataCfg);
}
// 清空所有的下钻信息
this.spreadsheet.store.set('drillItemsNum', -1);
this.spreadsheet.store.set('drillDownDataCache', []);
this.spreadsheet.store.set('drillDownFieldInLevel', []);
}
store.set('drillDownIdPathMap', idPathMap);
return true;
}
handlePivotMetaSort(sortFieldId, sortedDimensionValues) {
const { rows, columns } = this.fields;
if ((0, lodash_1.includes)(rows, sortFieldId)) {
this.rowPivotMeta = (0, sort_action_1.getSortedPivotMeta)({
pivotMeta: this.rowPivotMeta,
dimensions: rows,
sortFieldId,
sortedDimensionValues,
});
}
else if ((0, lodash_1.includes)(columns, sortFieldId)) {
this.colPivotMeta = (0, sort_action_1.getSortedPivotMeta)({
pivotMeta: this.colPivotMeta,
dimensions: columns,
sortFieldId,
sortedDimensionValues,
});
}
}
processDataCfg(dataCfg) {
const { data, meta = [], fields, sortParams = [] } = dataCfg;
const { columns = [], rows = [], values, valueInCols, customValueOrder, } = fields;
let newColumns = columns;
let newRows = rows;
if (valueInCols) {
newColumns = this.isCustomMeasuresPosition(customValueOrder)
? this.handleCustomMeasuresOrder(customValueOrder, newColumns)
: (0, lodash_1.uniq)([...columns, constant_1.EXTRA_FIELD]);
}
else {
newRows = this.isCustomMeasuresPosition(customValueOrder)
? this.handleCustomMeasuresOrder(customValueOrder, newRows)
: (0, lodash_1.uniq)([...rows, constant_1.EXTRA_FIELD]);
}
const newMeta = this.processMeta(meta, (0, i18n_1.i18n)('数值'));
return {
data,
meta: newMeta,
fields: Object.assign(Object.assign({}, fields), { rows: newRows, columns: newColumns, values }),
sortParams,
};
}
getFieldsAndPivotMetaByField(field) {
const { rows = [], columns = [] } = this.fields || {};
if (rows.includes(field)) {
return {
dimensions: (0, pivot_data_set_1.getIndexFields)(rows),
pivotMeta: this.rowPivotMeta,
};
}
if (columns.includes(field)) {
return {
dimensions: (0, pivot_data_set_1.getIndexFields)(columns),
pivotMeta: this.colPivotMeta,
};
}
return {};
}
getDimensionValues(field, query = {}) {
var _a;
const { pivotMeta, dimensions } = this.getFieldsAndPivotMetaByField(field);
if (!pivotMeta || !dimensions) {
return [];
}
const isGetAllDimensionValues = (0, lodash_1.isEmpty)(query);
// 暂时先对获取某一个维度所有的 labels 这样的场景做缓存处理,因为内部 flatten 逻辑比较耗时
if (this.dimensionValuesCache.has(field) && isGetAllDimensionValues) {
return (_a = this.dimensionValuesCache.get(field)) !== null && _a !== void 0 ? _a : [];
}
const dimensionValues = (0, pivot_data_set_1.transformDimensionsValues)(query, dimensions, common_1.MULTI_VALUE);
const metaValues = (0, pivot_data_set_1.getSatisfiedPivotMetaValues)({
pivotMeta,
dimensionValues,
fields: dimensions,
fieldIdx: (0, lodash_1.indexOf)(dimensions, field),
queryType: common_1.QueryDataType.DetailOnly,
sortedDimensionValues: this.sortedDimensionValues,
});
const result = (0, lodash_1.uniq)(metaValues.map((meta) => (0, utils_1.resolveNillString)(meta.value)));
if (isGetAllDimensionValues) {
this.dimensionValuesCache.set(field, result);
}
return result;
}
getTotalValue(query, totalStatus) {
const { options } = this.spreadsheet;
const effectiveStatus = (0, lodash_1.some)(totalStatus);
const status = effectiveStatus ? totalStatus : this.getTotalStatus(query);
const { aggregation, calcFunc } = (0, data_set_operate_1.getAggregationAndCalcFuncByQuery)(status, options === null || options === void 0 ? void 0 : options.totals) || {};
// 聚合方式从用户配置的 s2Options.totals 取, 在触发前端兜底计算汇总逻辑时, 如果没有汇总的配置, 默认按 [求和] 计算,避免排序失效.
const defaultAggregation = (0, lodash_1.isEmpty)(options === null || options === void 0 ? void 0 : options.totals) && !this.spreadsheet.isHierarchyTreeType()
? common_1.Aggregation.SUM
: '';
const calcAction = number_calculate_1.calcActionByType[aggregation || defaultAggregation];
// 前端计算汇总值
if (calcAction || calcFunc) {
const data = this.getCellMultiData({
query,
queryType: common_1.QueryDataType.DetailOnly,
});
let totalValue = null;
if (calcFunc) {
totalValue = calcFunc(query, data, this.spreadsheet);
}
else if (calcAction) {
totalValue = calcAction(data, constant_1.VALUE_FIELD);
}
return cell_data_1.CellData.getCellData(Object.assign(Object.assign({}, (0, lodash_1.omit)(query, [constant_1.EXTRA_FIELD])), { [query[constant_1.EXTRA_FIELD]]: totalValue }), query[constant_1.EXTRA_FIELD]);
}
}
getCellData(params) {
var _a, _b, _c;
const { query = {}, rowNode, isTotals = false, totalStatus } = params || {};
const { rows: originRows, columns } = this.fields;
let rows = originRows;
const drillDownIdPathMap = (_a = this.spreadsheet) === null || _a === void 0 ? void 0 : _a.store.get('drillDownIdPathMap');
/*
* 判断当前是否为下钻节点
* 需检查 rowNode.id 是否属于下钻根节点(drillDownIdPathMap.keys)的下属节点
*/
const isDrillDown = Array.from((_b = drillDownIdPathMap === null || drillDownIdPathMap === void 0 ? void 0 : drillDownIdPathMap.keys()) !== null && _b !== void 0 ? _b : []).some((parentPath) => rowNode === null || rowNode === void 0 ? void 0 : rowNode.id.startsWith(parentPath));
// 如果是下钻结点,行维度在 originRows 中并不存在
if (rowNode && isDrillDown) {
rows = (_c = node_1.Node.getFieldPath(rowNode, isDrillDown)) !== null && _c !== void 0 ? _c : originRows;
}
const indexRows = (0, pivot_data_set_1.getIndexFields)(rows);
const indexColumns = (0, pivot_data_set_1.getIndexFields)(columns);
const rowDimensionValues = (0, pivot_data_set_1.transformDimensionsValues)(query, indexRows);
const colDimensionValues = (0, pivot_data_set_1.transformDimensionsValues)(query, indexColumns);
const path = (0, pivot_data_set_1.getDataPath)({
rowDimensionValues,
colDimensionValues,
rowPivotMeta: this.rowPivotMeta,
colPivotMeta: this.colPivotMeta,
rowFields: indexRows,
colFields: indexColumns,
prefix: (0, pivot_data_set_1.getDataPathPrefix)(indexRows, indexColumns),
});
const rawData = (0, lodash_1.get)(this.indexesData, path);
if (rawData) {
// 如果已经有数据则取已有数据
return cell_data_1.CellData.getCellData(rawData, query[constant_1.EXTRA_FIELD]);
}
if (isTotals) {
return this.getTotalValue(query, totalStatus);
}
}
getQueryExtraFields(query) {
const { values = [] } = this.fields;
const extra = query[constant_1.EXTRA_FIELD];
if (extra) {
return (0, lodash_1.includes)(values, extra) ? [extra] : [];
}
return values;
}
getCellMultiData(params) {
const { query = {}, queryType = common_1.QueryDataType.All, drillDownFields = [], } = params || {};
if ((0, lodash_1.isEmpty)(query)) {
// 如果查询的 query 为空,这样的场景其实没有意义,如果用户想获取全量数据,可以直接从 data 中获取
// eslint-disable-next-line no-console
console.warn(`query: ${query} shouldn't be empty, you can get all data from dataCfg if you're intended.\n you should use { EXTRA_FIELD: xxx} as least if you want query all specific value data`);
}
const { rows, columns } = this.fields;
const totalRows = !(0, lodash_1.isEmpty)(drillDownFields)
? rows.concat(drillDownFields)
: rows;
const indexRows = (0, pivot_data_set_1.getIndexFields)(totalRows);
const indexColumns = (0, pivot_data_set_1.getIndexFields)(columns);
const rowDimensionValues = (0, pivot_data_set_1.transformDimensionsValues)(query, indexRows, common_1.MULTI_VALUE);
const colDimensionValues = (0, pivot_data_set_1.transformDimensionsValues)(query, indexColumns, common_1.MULTI_VALUE);
const { rowQueries, colQueries } = (0, pivot_data_set_1.getFlattenDimensionValues)({
rowDimensionValues,
colDimensionValues,
rowPivotMeta: this.rowPivotMeta,
colPivotMeta: this.colPivotMeta,
rowFields: indexRows,
colFields: indexColumns,
sortedDimensionValues: this.sortedDimensionValues,
queryType,
});
const prefix = (0, pivot_data_set_1.getDataPathPrefix)(indexRows, indexColumns);
const all = [];
for (const rowQuery of rowQueries) {
for (const colQuery of colQueries) {
const path = (0, pivot_data_set_1.getDataPath)({
rowDimensionValues: rowQuery,
colDimensionValues: colQuery,
rowPivotMeta: this.rowPivotMeta,
colPivotMeta: this.colPivotMeta,
rowFields: indexRows,
colFields: indexColumns,
prefix,
});
let hadMultiField = false;
let result = this.indexesData;
for (let i = 0; i < path.length; i++) {
const current = path[i];
if (hadMultiField) {
if ((0, pivot_data_set_1.isMultiValue)(current)) {
result = (0, pivot_data_set_1.flattenIndexesData)(result, queryType);
}
else {
result = (0, lodash_1.compact)((0, lodash_1.map)(result, (item) => item === null || item === void 0 ? void 0 : item[current]));
}
}
else if ((0, pivot_data_set_1.isMultiValue)(current)) {
hadMultiField = true;
result = (0, lodash_1.compact)([result]);
i--;
}
else {
result = result === null || result === void 0 ? void 0 : result[current];
}
}
// 如果每一个维度都是被指定好的,那么最终获取的数据就是单个的
if ((0, lodash_1.isArray)(result)) {
all.push(...result);
}
else if (result) {
all.push(result);
}
}
}
const extraFields = this.getQueryExtraFields(query);
// 多个 extra field 有时对应的同一个对象,需要进行去重
return (0, lodash_1.flatMap)((0, lodash_1.uniq)(all), (item) => {
return item ? cell_data_1.CellData.getCellDataList(item, extraFields) : [];
});
}
getFieldFormatter(field, cellMeta) {
// 兼容总计小计场景
if (field === constant_1.TOTAL_VALUE) {
return this.getFieldFormatterForTotalValue(cellMeta);
}
return super.getFieldFormatter(field);
}
getFieldFormatterForTotalValue(cellMeta) {
let valueField = '';
// 当数据置于行头时,小计总计列尝试去找对应的指标
if (!this.spreadsheet.isValueInCols() && cellMeta) {
valueField = (0, lodash_1.get)(cellMeta.rowQuery, constant_1.EXTRA_FIELD);
}
// 如果没有找到对应指标,则默认取第一个维度
valueField = valueField !== null && valueField !== void 0 ? valueField : (0, lodash_1.get)(this.fields.values, 0);
return super.getFieldFormatter(valueField);
}
/**
* 自定义度量组位置值
* @param customValueOrder 用户配置度量组位置,从 0 开始
* @param fields Rows || Columns
*/
handleCustomMeasuresOrder(customValueOrder, fields) {
const newFields = (0, lodash_1.uniq)([...fields]);
if (fields.length >= customValueOrder) {
newFields.splice(customValueOrder, 0, constant_1.EXTRA_FIELD);
return newFields;
}
// 当用户配置的度量组位置大于等于度量组数量时,默认放在最后
return [...newFields, constant_1.EXTRA_FIELD];
}
// 是否开启自定义度量组位置值
isCustomMeasuresPosition(customValueOrder) {
return (0, lodash_1.isNumber)(customValueOrder);
}
getRowData(cellMeta) {
return this.getCellMultiData({ query: cellMeta.rowQuery });
}
}
exports.PivotDataSet = PivotDataSet;
//# sourceMappingURL=pivot-data-set.js.map