diff --git a/packages/s2-core/__tests__/spreadsheet/miss-dimension-values-spec.ts b/packages/s2-core/__tests__/spreadsheet/miss-dimension-values-spec.ts new file mode 100644 index 0000000000..0ebf2b44af --- /dev/null +++ b/packages/s2-core/__tests__/spreadsheet/miss-dimension-values-spec.ts @@ -0,0 +1,246 @@ +import { getContainer } from 'tests/util/helpers'; +import { EMPTY_FIELD_VALUE, type S2DataConfig, type S2Options } from '@/common'; +import { PivotSheet, SpreadSheet } from '@/sheet-type'; + +const s2Options: S2Options = { + debug: true, + width: 600, + height: 400, + hierarchyType: 'grid', + totals: { + row: { + showGrandTotals: false, + showSubTotals: { + always: false, + }, + reverseLayout: false, + reverseSubLayout: false, + subTotalsDimensions: [ + '2d7feabd-76a2-4c11-8f24-79764af936b4', + '30b4b32d-d69a-4772-b7f9-84cd54cf0cec', + ], + }, + col: { + showGrandTotals: false, + showSubTotals: false, + reverseLayout: false, + reverseSubLayout: false, + subTotalsDimensions: [], + }, + }, + style: { + layoutWidthType: 'adaptive', + cellCfg: { + height: 30, + }, + }, + showDefaultHeaderActionIcon: false, +}; + +const testDataCfg: S2DataConfig = { + meta: [ + { + field: '2d7feabd-76a2-4c11-8f24-79764af936b4', + name: '一级维度', + }, + { + field: '30b4b32d-d69a-4772-b7f9-84cd54cf0cec', + name: '二级维度', + }, + { + field: 'c5ce4e54-795a-42b3-9cc8-e8b685da44ee', + name: '数值', + }, + ], + fields: { + rows: [ + '2d7feabd-76a2-4c11-8f24-79764af936b4', + '30b4b32d-d69a-4772-b7f9-84cd54cf0cec', + ], + columns: [], + values: ['c5ce4e54-795a-42b3-9cc8-e8b685da44ee'], + valueInCols: true, + }, + data: [ + { + '2d7feabd-76a2-4c11-8f24-79764af936b4': '总计', + 'c5ce4e54-795a-42b3-9cc8-e8b685da44ee': 1732771, + }, + { + '2d7feabd-76a2-4c11-8f24-79764af936b4': '维值-1', + '30b4b32d-d69a-4772-b7f9-84cd54cf0cec': '维值-2', + 'c5ce4e54-795a-42b3-9cc8-e8b685da44ee': 172245, + }, + { + '2d7feabd-76a2-4c11-8f24-79764af936b4': '维值-1', + '30b4b32d-d69a-4772-b7f9-84cd54cf0cec': '维值-2', + 'c5ce4e54-795a-42b3-9cc8-e8b685da44ee': 12222, + }, + { + '2d7feabd-76a2-4c11-8f24-79764af936b4': '维值-1', + '30b4b32d-d69a-4772-b7f9-84cd54cf0cec': '维值-3', + 'c5ce4e54-795a-42b3-9cc8-e8b685da44ee': 11111, + }, + { + '2d7feabd-76a2-4c11-8f24-79764af936b4': '维值-1', + '30b4b32d-d69a-4772-b7f9-84cd54cf0cec': '维值-3', + 'c5ce4e54-795a-42b3-9cc8-e8b685da44ee': 11111, + }, + { + '2d7feabd-76a2-4c11-8f24-79764af936b4': '维值-1', + 'c5ce4e54-795a-42b3-9cc8-e8b685da44ee': 456, + }, + { + '2d7feabd-76a2-4c11-8f24-79764af936b4': '测试-1', + '30b4b32d-d69a-4772-b7f9-84cd54cf0cec': '测试-2', + 'c5ce4e54-795a-42b3-9cc8-e8b685da44ee': 12, + }, + { + '2d7feabd-76a2-4c11-8f24-79764af936b4': '测试-1', + '30b4b32d-d69a-4772-b7f9-84cd54cf0cec': '测试-2', + 'c5ce4e54-795a-42b3-9cc8-e8b685da44ee': 4444567, + }, + { + '2d7feabd-76a2-4c11-8f24-79764af936b4': '测试-1', + '30b4b32d-d69a-4772-b7f9-84cd54cf0cec': '测试-3', + 'c5ce4e54-795a-42b3-9cc8-e8b685da44ee': 111233, + }, + { + '2d7feabd-76a2-4c11-8f24-79764af936b4': '测试-1', + '30b4b32d-d69a-4772-b7f9-84cd54cf0cec': '测试-3', + 'c5ce4e54-795a-42b3-9cc8-e8b685da44ee': 785222, + }, + { + '2d7feabd-76a2-4c11-8f24-79764af936b4': '测试-1', + '30b4b32d-d69a-4772-b7f9-84cd54cf0cec': '测试-4', + 'c5ce4e54-795a-42b3-9cc8-e8b685da44ee': 6455644, + }, + { + '2d7feabd-76a2-4c11-8f24-79764af936b4': '测试-1', + '30b4b32d-d69a-4772-b7f9-84cd54cf0cec': '测试-4', + 'c5ce4e54-795a-42b3-9cc8-e8b685da44ee': 289898, + }, + { + '2d7feabd-76a2-4c11-8f24-79764af936b4': '测试-1', + '30b4b32d-d69a-4772-b7f9-84cd54cf0cec': '测试-5', + 'c5ce4e54-795a-42b3-9cc8-e8b685da44ee': 2222, + }, + { + '2d7feabd-76a2-4c11-8f24-79764af936b4': '测试-1', + '30b4b32d-d69a-4772-b7f9-84cd54cf0cec': '测试-5', + 'c5ce4e54-795a-42b3-9cc8-e8b685da44ee': 1111, + }, + { + '2d7feabd-76a2-4c11-8f24-79764af936b4': '测试-1', + 'c5ce4e54-795a-42b3-9cc8-e8b685da44ee': 125555, + }, + { + '2d7feabd-76a2-4c11-8f24-79764af936b4': '测试-6', + '30b4b32d-d69a-4772-b7f9-84cd54cf0cec': '测试-x', + 'c5ce4e54-795a-42b3-9cc8-e8b685da44ee': 409090, + }, + { + '2d7feabd-76a2-4c11-8f24-79764af936b4': '测试-6', + '30b4b32d-d69a-4772-b7f9-84cd54cf0cec': '测试-x', + 'c5ce4e54-795a-42b3-9cc8-e8b685da44ee': 111111, + }, + { + '2d7feabd-76a2-4c11-8f24-79764af936b4': '测试-6', + '30b4b32d-d69a-4772-b7f9-84cd54cf0cec': '测试-7', + 'c5ce4e54-795a-42b3-9cc8-e8b685da44ee': 5555, + }, + { + '2d7feabd-76a2-4c11-8f24-79764af936b4': '测试-6', + '30b4b32d-d69a-4772-b7f9-84cd54cf0cec': '测试-7', + 'c5ce4e54-795a-42b3-9cc8-e8b685da44ee': 67878, + }, + { + '2d7feabd-76a2-4c11-8f24-79764af936b4': '测试-6', + '30b4b32d-d69a-4772-b7f9-84cd54cf0cec': '测试-8', + 'c5ce4e54-795a-42b3-9cc8-e8b685da44ee': 53445.464, + }, + { + '2d7feabd-76a2-4c11-8f24-79764af936b4': '测试-6', + '30b4b32d-d69a-4772-b7f9-84cd54cf0cec': '测试-8', + 'c5ce4e54-795a-42b3-9cc8-e8b685da44ee': 456.464, + }, + { + '2d7feabd-76a2-4c11-8f24-79764af936b4': '测试-6', + 'c5ce4e54-795a-42b3-9cc8-e8b685da44ee': 123.416, + }, + ], +}; + +describe('Miss Dimension Values Tests', () => { + let s2: SpreadSheet; + + beforeEach(() => { + s2 = new PivotSheet(getContainer(), testDataCfg, s2Options); + s2.render(); + }); + + test('should get correctly empty dimension values', () => { + const emptyDimensionValueNode = s2.getRowNodes()[0].children[0]; + + expect(emptyDimensionValueNode.value).toEqual(EMPTY_FIELD_VALUE); + expect(emptyDimensionValueNode.id).toEqual( + `root[&]总计[&]${EMPTY_FIELD_VALUE}`, + ); + expect(emptyDimensionValueNode.belongsCell.getActualText()).toEqual('-'); + }); + + test('should get correctly empty dimension values and use custom placeholder text', () => { + const placeholder = '*'; + + s2.setOptions({ + placeholder, + }); + s2.render(false); + + const emptyDimensionValueNode = s2.getRowNodes()[0].children[0]; + expect(emptyDimensionValueNode.belongsCell.getActualText()).toEqual( + placeholder, + ); + }); + + test('should get correctly dimension data and ignore empty dimension value', () => { + const emptyDimensionValueNode = s2.getRowNodes()[0].children[0]; + + const data = s2.dataSet.getMultiData(emptyDimensionValueNode.query); + const dimensionValues = s2.dataSet.getDimensionValues( + emptyDimensionValueNode.field, + ); + const emptyDimensionDataCell = + s2.interaction.getPanelGroupAllDataCells()[0]; + + expect(emptyDimensionValueNode.query).toEqual( + emptyDimensionValueNode.parent.query, + ); + expect(emptyDimensionDataCell.getMeta().fieldValue).toEqual(1732771); + expect(data).toMatchInlineSnapshot(` + Array [ + Array [ + Object { + "$$extra$$": "c5ce4e54-795a-42b3-9cc8-e8b685da44ee", + "$$value$$": 1732771, + "2d7feabd-76a2-4c11-8f24-79764af936b4": "总计", + "c5ce4e54-795a-42b3-9cc8-e8b685da44ee": 1732771, + }, + ], + ] + `); + expect(dimensionValues).toMatchInlineSnapshot(` + Array [ + "维值-2", + "维值-3", + "测试-2", + "测试-3", + "测试-4", + "测试-5", + "测试-x", + "测试-7", + "测试-8", + ] + `); + }); +}); diff --git a/packages/s2-core/src/cell/base-cell.ts b/packages/s2-core/src/cell/base-cell.ts index 0a1c4523e4..7bcfa41b54 100644 --- a/packages/s2-core/src/cell/base-cell.ts +++ b/packages/s2-core/src/cell/base-cell.ts @@ -14,6 +14,7 @@ import { } from 'lodash'; import { CellTypes, + EMPTY_FIELD_VALUE, InteractionStateName, SHAPE_ATTRS_MAP, SHAPE_STYLE_MAP, @@ -135,7 +136,12 @@ export abstract class BaseCell extends Group { } public getFieldValue() { - return this.getFormattedFieldValue().formattedValue; + const { formattedValue } = this.getFormattedFieldValue(); + + if (formattedValue === EMPTY_FIELD_VALUE) { + return this.getActualText(); + } + return formattedValue; } /** diff --git a/packages/s2-core/src/cell/header-cell.ts b/packages/s2-core/src/cell/header-cell.ts index a2dfd1eb3f..538b9c654b 100644 --- a/packages/s2-core/src/cell/header-cell.ts +++ b/packages/s2-core/src/cell/header-cell.ts @@ -74,6 +74,10 @@ export abstract class HeaderCell extends BaseCell { this.hasDefaultHiddenIcon = false; } + public getTreeIcon(): GuiIcon { + return this.treeIcon; + } + protected getInteractiveBorderShapeStyle(border: number) { const { x, y, height, width } = this.getCellArea(); return { diff --git a/packages/s2-core/src/cell/row-cell.ts b/packages/s2-core/src/cell/row-cell.ts index 413a77f5e1..3b0107e0fa 100644 --- a/packages/s2-core/src/cell/row-cell.ts +++ b/packages/s2-core/src/cell/row-cell.ts @@ -1,6 +1,6 @@ import type { Point } from '@antv/g-canvas'; import { GM } from '@antv/g-gesture'; -import { find, get } from 'lodash'; +import { find, get, isEmpty } from 'lodash'; import type { SimpleBBox } from '@antv/g-canvas'; import { CellTypes, @@ -130,6 +130,7 @@ export class RowCell extends HeaderCell { ) { return; } + return get(this.meta, 'parent.belongsCell.treeIcon.cfg'); } diff --git a/packages/s2-core/src/common/constant/basic.ts b/packages/s2-core/src/common/constant/basic.ts index ab288ab89d..c7e0a95922 100644 --- a/packages/s2-core/src/common/constant/basic.ts +++ b/packages/s2-core/src/common/constant/basic.ts @@ -4,6 +4,7 @@ export const EXTRA_FIELD = '$$extra$$'; export const EXTRA_COLUMN_FIELD = '$$extra_column$$'; export const TOTAL_VALUE = '$$total$$'; export const SERIES_NUMBER_FIELD = '$$series_number$$'; +export const EMPTY_FIELD_VALUE = '$$empty_field_value$$'; export const BACK_GROUND_GROUP_CONTAINER_Z_INDEX = 0; diff --git a/packages/s2-core/src/facet/layout/build-gird-hierarchy.ts b/packages/s2-core/src/facet/layout/build-gird-hierarchy.ts index eb8de392ec..53864399f3 100644 --- a/packages/s2-core/src/facet/layout/build-gird-hierarchy.ts +++ b/packages/s2-core/src/facet/layout/build-gird-hierarchy.ts @@ -1,5 +1,5 @@ import { isEmpty, isUndefined } from 'lodash'; -import { EXTRA_FIELD } from '../../common/constant'; +import { EMPTY_FIELD_VALUE, EXTRA_FIELD } from '../../common/constant'; import { addTotals } from '../../utils/layout/add-totals'; import { generateHeaderNodes } from '../../utils/layout/generate-header-nodes'; import { getDimsCondition } from '../../utils/layout/get-dims-condition-by-node'; @@ -23,9 +23,8 @@ const buildTotalGridHierarchy = (params: GridHeaderParams) => { const { dataSet, values, spreadsheet } = facetCfg; const fieldValues: FieldValue[] = []; - const fieldName = dataSet.getFieldName(currentField); - let query = {}; + let query: Record = {}; const totalsConfig = spreadsheet.getTotalsConfig(currentField); const dimensionGroup = parentNode.isGrandTotals ? totalsConfig.totalsGroupDimensions @@ -44,15 +43,15 @@ const buildTotalGridHierarchy = (params: GridHeaderParams) => { }), ), ); - if (isEmpty(fieldValues)) { - fieldValues.push(fieldName); + if (isEmpty(fieldValues) && currentField) { + fieldValues.push(EMPTY_FIELD_VALUE); } } else if (addTotalMeasureInTotal && currentField === EXTRA_FIELD) { // add total measures query = getDimsCondition(parentNode); fieldValues.push(...values.map((v) => new TotalMeasure(v))); } else if (whetherLeafByLevel({ facetCfg, level: index, fields })) { - // 如果最后一级没有分组维度,则将上一个结点设为叶子结点 + // 如果最后一级没有分组维度,则将上一个结点设为叶子节点 parentNode.isLeaf = true; hierarchy.pushIndexNode(parentNode); parentNode.rowIndex = hierarchy.getIndexNodes().length - 1; @@ -80,9 +79,8 @@ const buildNormalGridHierarchy = (params: GridHeaderParams) => { const { dataSet, spreadsheet } = facetCfg; const fieldValues: FieldValue[] = []; - const fieldName = dataSet.getFieldName(currentField); - let query = {}; + let query: Record = {}; // field(dimension)'s all values query = getDimsCondition(parentNode, true); @@ -99,11 +97,11 @@ const buildNormalGridHierarchy = (params: GridHeaderParams) => { // add skeleton for empty data - if (isEmpty(fieldValues)) { + if (isEmpty(fieldValues) && currentField) { if (currentField === EXTRA_FIELD) { fieldValues.push(...dataSet.fields?.values); } else { - fieldValues.push(fieldName); + fieldValues.push(EMPTY_FIELD_VALUE); } } diff --git a/packages/s2-core/src/utils/layout/generate-header-nodes.ts b/packages/s2-core/src/utils/layout/generate-header-nodes.ts index aa33a7bf42..482a5d2c4c 100644 --- a/packages/s2-core/src/utils/layout/generate-header-nodes.ts +++ b/packages/s2-core/src/utils/layout/generate-header-nodes.ts @@ -1,5 +1,9 @@ import { isBoolean } from 'lodash'; -import { EXTRA_FIELD, SERIES_NUMBER_FIELD } from '../../common/constant'; +import { + EMPTY_FIELD_VALUE, + EXTRA_FIELD, + SERIES_NUMBER_FIELD, +} from '../../common/constant'; import { i18n } from '../../common/i18n'; import { buildGridHierarchy } from '../../facet/layout/build-gird-hierarchy'; import type { @@ -33,12 +37,13 @@ export const generateHeaderNodes = (params: HeaderNodesParams) => { const isTotals = fieldValue instanceof TotalClass; const isTotalMeasure = fieldValue instanceof TotalMeasure; let value: string; - let nodeQuery; + let nodeQuery: Record; let isLeaf = false; let isGrandTotals = false; let isSubTotals = false; let isTotalRoot = false; let adjustedField = currentField; + if (isTotals) { const totalClass = fieldValue as TotalClass; isGrandTotals = totalClass.isGrandTotals; @@ -72,7 +77,11 @@ export const generateHeaderNodes = (params: HeaderNodesParams) => { } else { value = fieldValue; // root[&]四川[&]成都 => {province: '四川', city: '成都' } - nodeQuery = { ...query, [currentField]: value }; + // 子维度的维值为空时, 使用父级节点的 query, 避免查询不到数据 + nodeQuery = + value === EMPTY_FIELD_VALUE + ? { ...query } + : { ...query, [currentField]: value }; isLeaf = whetherLeafByLevel({ facetCfg, level, fields }); } const uniqueId = generateId(parentNode.id, value); diff --git a/packages/s2-core/src/utils/text.ts b/packages/s2-core/src/utils/text.ts index 833940b721..755ff93d63 100644 --- a/packages/s2-core/src/utils/text.ts +++ b/packages/s2-core/src/utils/text.ts @@ -18,6 +18,7 @@ import type { ColCell } from '../cell'; import { CellTypes, ELLIPSIS_SYMBOL, + EMPTY_FIELD_VALUE, EMPTY_PLACEHOLDER, } from '../common/constant'; import type { @@ -205,9 +206,11 @@ export const getEllipsisText = ({ placeholder?: string; }) => { let font = {}; - const empty = placeholder ?? EMPTY_PLACEHOLDER; - // [null, undefined, ''] will return empty - const finalText = isNil(text) || text === '' ? empty : `${text}`; + const emptyPlaceholder = placeholder ?? EMPTY_PLACEHOLDER; + // 对应维度缺少维度数据时, 会使用 EMPTY_FIELD_VALUE 填充, 实际渲染时统一转成 "-" + const isEmptyText = isNil(text) || text === '' || text === EMPTY_FIELD_VALUE; + const finalText = isEmptyText ? emptyPlaceholder : `${text}`; + let priority = priorityParam; if (fontParam && isArray(fontParam)) { priority = fontParam as string[];