diff --git a/packages/s2-core/__tests__/unit/facet/pivot-facet-spec.ts b/packages/s2-core/__tests__/unit/facet/pivot-facet-spec.ts index 1df5119959..a2e017ddcb 100644 --- a/packages/s2-core/__tests__/unit/facet/pivot-facet-spec.ts +++ b/packages/s2-core/__tests__/unit/facet/pivot-facet-spec.ts @@ -97,7 +97,8 @@ jest.mock('@/data-set/pivot-data-set', () => { getCellData: actualPivotDataSet.prototype.getCellData, getMultiData: jest.fn(), getDimensionValues: actualPivotDataSet.prototype.getDimensionValues, - getDimensionsByField: actualPivotDataSet.prototype.getDimensionsByField, + getFieldsAndPivotMetaByField: + actualPivotDataSet.prototype.getFieldsAndPivotMetaByField, }; }), }; diff --git a/packages/s2-core/__tests__/unit/utils/dataset/pivot-data-set-spec.ts b/packages/s2-core/__tests__/unit/utils/dataset/pivot-data-set-spec.ts index b154dd16eb..11e35670f8 100644 --- a/packages/s2-core/__tests__/unit/utils/dataset/pivot-data-set-spec.ts +++ b/packages/s2-core/__tests__/unit/utils/dataset/pivot-data-set-spec.ts @@ -83,26 +83,39 @@ describe('pivot-data-set utils test', () => { { childField: 'city', level: 1, + id: '四川省', children: new Map([ [ '成都市', { childField: 'type', level: 1, + id: '四川省[&]成都市', children: new Map([ [ '家具', { childField: 'subType', level: 1, + id: '四川省[&]成都市[&]家具', children: new Map([ [ '桌子', - { childFiled: null, level: 1, children: new Map() }, + { + childFiled: null, + id: '四川省[&]成都市[&]家具[&]桌子', + level: 1, + children: new Map(), + }, ], [ '沙发', - { childFiled: null, level: 2, children: new Map() }, + { + childFiled: null, + level: 2, + id: '四川省[&]成都市[&]家具[&]沙发', + children: new Map(), + }, ], ]), }, @@ -112,14 +125,25 @@ describe('pivot-data-set utils test', () => { { childField: 'subType', level: 2, + id: '四川省[&]成都市[&]办公用品', children: new Map([ [ '笔', - { childFiled: null, level: 1, children: new Map() }, + { + childFiled: null, + level: 1, + id: '四川省[&]成都市[&]办公用品[&]笔', + children: new Map(), + }, ], [ '纸张', - { childFiled: null, level: 2, children: new Map() }, + { + childFiled: null, + level: 2, + id: '四川省[&]成都市[&]办公用品[&]纸张', + children: new Map(), + }, ], ]), }, @@ -132,20 +156,33 @@ describe('pivot-data-set utils test', () => { { childField: 'type', level: 2, + id: '四川省[&]绵阳市', children: new Map([ [ '家具', { childField: 'subType', level: 1, + id: '四川省[&]绵阳市[&]家具', children: new Map([ [ '桌子', - { childFiled: null, level: 1, children: new Map() }, + { + childFiled: null, + level: 1, + id: '四川省[&]绵阳市[&]家具[&]桌子', + + children: new Map(), + }, ], [ '沙发', - { childFiled: null, level: 2, children: new Map() }, + { + childFiled: null, + level: 2, + id: '四川省[&]绵阳市[&]家具[&]沙发', + children: new Map(), + }, ], ]), }, @@ -155,14 +192,26 @@ describe('pivot-data-set utils test', () => { { childField: 'subType', level: 2, + id: '四川省[&]绵阳市[&]办公用品', children: new Map([ [ '笔', - { childFiled: null, level: 1, children: new Map() }, + { + childFiled: null, + level: 1, + + id: '四川省[&]绵阳市[&]办公用品[&]笔', + children: new Map(), + }, ], [ '纸张', - { childFiled: null, level: 2, children: new Map() }, + { + childFiled: null, + level: 2, + id: '四川省[&]绵阳市[&]办公用品[&]纸张', + children: new Map(), + }, ], ]), }, @@ -178,26 +227,39 @@ describe('pivot-data-set utils test', () => { { childField: 'city', level: 2, + id: '浙江省', children: new Map([ [ '杭州市', { childField: 'type', level: 1, + id: '浙江省[&]杭州市', children: new Map([ [ '家具', { childField: 'subType', level: 1, + id: '浙江省[&]杭州市[&]家具', children: new Map([ [ '桌子', - { childFiled: null, level: 1, children: new Map() }, + { + childFiled: null, + level: 1, + id: '浙江省[&]杭州市[&]家具[&]桌子', + children: new Map(), + }, ], [ '沙发', - { childFiled: null, level: 2, children: new Map() }, + { + childFiled: null, + level: 2, + id: '浙江省[&]杭州市[&]家具[沙发]桌子', + children: new Map(), + }, ], ]), }, @@ -207,14 +269,25 @@ describe('pivot-data-set utils test', () => { { childField: 'subType', level: 2, + id: '浙江省[&]杭州市[&]办公用品', children: new Map([ [ '笔', - { childFiled: null, level: 1, children: new Map() }, + { + childFiled: null, + level: 1, + id: '浙江省[&]杭州市[&]办公用品[&]笔', + children: new Map(), + }, ], [ '纸张', - { childFiled: null, level: 2, children: new Map() }, + { + childFiled: null, + level: 2, + id: '浙江省[&]杭州市[&]办公用品[&]纸张', + children: new Map(), + }, ], ]), }, @@ -227,20 +300,32 @@ describe('pivot-data-set utils test', () => { { childField: 'type', level: 2, + id: '浙江省[&]舟山市', children: new Map([ [ '家具', { childField: 'subType', level: 1, + id: '浙江省[&]舟山市[&]家具', children: new Map([ [ '桌子', - { childFiled: null, level: 1, children: new Map() }, + { + childFiled: null, + level: 1, + id: '浙江省[&]舟山市[&]家具[&]桌子', + children: new Map(), + }, ], [ '沙发', - { childFiled: null, level: 2, children: new Map() }, + { + childFiled: null, + level: 2, + id: '浙江省[&]舟山市[&]家具[&]沙发', + children: new Map(), + }, ], ]), }, @@ -250,14 +335,25 @@ describe('pivot-data-set utils test', () => { { childField: 'subType', level: 2, + id: '浙江省[&]舟山市[&]办公用品', children: new Map([ [ '笔', - { childFiled: null, level: 1, children: new Map() }, + { + childFiled: null, + level: 1, + id: '浙江省[&]舟山市[&]办公用品[&]笔', + children: new Map(), + }, ], [ '纸张', - { childFiled: null, level: 2, children: new Map() }, + { + childFiled: null, + level: 2, + id: '浙江省[&]舟山市[&]办公用品[&]纸张', + children: new Map(), + }, ], ]), }, @@ -268,7 +364,7 @@ describe('pivot-data-set utils test', () => { ]), }, ], - ]); + ]) as unknown as PivotMeta; }); test(`should return false if doesn't exist total group`, () => { @@ -306,61 +402,74 @@ describe('pivot-data-set utils test', () => { test(`should return flatten dimension values if doesn't exist total group`, () => { expect( - flattenDimensionValues( + flattenDimensionValues({ fields, - ['四川省', '成都市', '办公用品', '纸张'], + pivotMeta, sortedDimensionValues, - ), + dimensionValues: ['四川省', '成都市', '办公用品', '纸张'], + }), ).toEqual([['四川省', '成都市', '办公用品', '纸张']]); expect( - flattenDimensionValues( + flattenDimensionValues({ fields, - ['四川省', '成都市', MULTI_VALUE, MULTI_VALUE], + pivotMeta, sortedDimensionValues, - ), + dimensionValues: ['四川省', '成都市', MULTI_VALUE, MULTI_VALUE], + }), ).toEqual([['四川省', '成都市', MULTI_VALUE, MULTI_VALUE]]); }); test(`should return flatten dimension values if exist total group`, () => { - expect( - flattenDimensionValues( - fields, - [MULTI_VALUE, '成都市', MULTI_VALUE, MULTI_VALUE], - sortedDimensionValues, - ), - ).toEqual([['四川省', '成都市', MULTI_VALUE, MULTI_VALUE]]); + const result1 = flattenDimensionValues({ + fields, + pivotMeta, + sortedDimensionValues, + dimensionValues: [MULTI_VALUE, '成都市', MULTI_VALUE, MULTI_VALUE], + }); + expect(result1).toEqual([ + ['四川省', '成都市', '家具', '桌子'], + ['四川省', '成都市', '家具', '沙发'], + ['四川省', '成都市', '办公用品', '笔'], + ['四川省', '成都市', '办公用品', '纸张'], + ]); - expect( - flattenDimensionValues( - fields, - [MULTI_VALUE, MULTI_VALUE, '办公用品', MULTI_VALUE], - sortedDimensionValues, - ), - ).toEqual([ - ['四川省', '成都市', '办公用品', MULTI_VALUE], - ['四川省', '绵阳市', '办公用品', MULTI_VALUE], - ['浙江省', '杭州市', '办公用品', MULTI_VALUE], - ['浙江省', '舟山市', '办公用品', MULTI_VALUE], + const result2 = flattenDimensionValues({ + fields, + pivotMeta, + sortedDimensionValues, + dimensionValues: [MULTI_VALUE, MULTI_VALUE, '办公用品', MULTI_VALUE], + }); + expect(result2).toEqual([ + ['四川省', '成都市', '办公用品', '笔'], + ['四川省', '成都市', '办公用品', '纸张'], + ['四川省', '绵阳市', '办公用品', '笔'], + ['四川省', '绵阳市', '办公用品', '纸张'], + ['浙江省', '杭州市', '办公用品', '笔'], + ['浙江省', '杭州市', '办公用品', '纸张'], + ['浙江省', '舟山市', '办公用品', '笔'], + ['浙江省', '舟山市', '办公用品', '纸张'], ]); - expect( - flattenDimensionValues( - fields, - ['四川省', MULTI_VALUE, '办公用品', MULTI_VALUE], - sortedDimensionValues, - ), - ).toEqual([ - ['四川省', '成都市', '办公用品', MULTI_VALUE], - ['四川省', '绵阳市', '办公用品', MULTI_VALUE], + const result3 = flattenDimensionValues({ + fields, + pivotMeta, + sortedDimensionValues, + dimensionValues: ['四川省', MULTI_VALUE, '办公用品', MULTI_VALUE], + }); + expect(result3).toEqual([ + ['四川省', '成都市', '办公用品', '笔'], + ['四川省', '成都市', '办公用品', '纸张'], + ['四川省', '绵阳市', '办公用品', '笔'], + ['四川省', '绵阳市', '办公用品', '纸张'], ]); - expect( - flattenDimensionValues( - fields, - [MULTI_VALUE, '成都市', MULTI_VALUE, '纸张'], - sortedDimensionValues, - ), - ).toEqual([['四川省', '成都市', '办公用品', '纸张']]); + const result4 = flattenDimensionValues({ + fields, + pivotMeta, + sortedDimensionValues, + dimensionValues: [MULTI_VALUE, '成都市', MULTI_VALUE, '纸张'], + }); + expect(result4).toEqual([['四川省', '成都市', '办公用品', '纸张']]); }); }); diff --git a/packages/s2-core/src/data-set/interface.ts b/packages/s2-core/src/data-set/interface.ts index 4d40703e07..4f534f3eba 100644 --- a/packages/s2-core/src/data-set/interface.ts +++ b/packages/s2-core/src/data-set/interface.ts @@ -8,10 +8,12 @@ export type DataType = Record; export type Query = Record; export type PivotMetaValue = { - // field level index + // 当前维度结合父级维度生成的完整 id 信息 + id: string; + // 当前维度 + value: string; level: number; children: PivotMeta; - // field name childField?: string; }; @@ -34,8 +36,8 @@ export type DataPathParams = { careRepeated: boolean; // 维度 id,如 city dimension: string; - // 维度数组 ['四川省', '成都市'] - dimensionPath: string[]; + // 完整维度信息:'四川省[&]成都市' + dimensionPath: string; }) => void; }; @@ -76,6 +78,13 @@ export interface SortActionParams { isSortByMeasure?: boolean; } +export interface SortPivotMetaParams { + pivotMeta: PivotMeta; + dimensions: string[]; + sortedDimensionValues: string[]; + sortFieldId: string; +} + export interface MultiDataParams { drillDownFields?: string[]; queryType?: QueryDataType; diff --git a/packages/s2-core/src/data-set/pivot-data-set.ts b/packages/s2-core/src/data-set/pivot-data-set.ts index f7f954cf8b..621fa3b6a0 100644 --- a/packages/s2-core/src/data-set/pivot-data-set.ts +++ b/packages/s2-core/src/data-set/pivot-data-set.ts @@ -43,16 +43,15 @@ import { deleteMetaById, flattenIndexesData, getDataPath, - getDimensionsWithoutPathPre, getFlattenDimensionValues, - satisfyDimensionValues, - shouldQueryMultiData, + getSatisfiedPivotMetaValues, + isMultiValue, transformDimensionsValues, transformIndexesData, } from '../utils/dataset/pivot-data-set'; import { DataHandler } from '../utils/dataset/proxy-handler'; import { calcActionByType } from '../utils/number-calculate'; -import { handleSortAction } from '../utils/sort-action'; +import { handleSortAction, getSortedPivotMeta } from '../utils/sort-action'; import { BaseDataSet } from './base-data-set'; import type { CellDataParams, @@ -246,9 +245,32 @@ export class PivotDataSet extends BaseDataSet { isSortByMeasure: !isEmpty(sortByMeasure), }); this.sortedDimensionValues[sortFieldId] = result; + this.handlePivotMetaSort(sortFieldId, result); }); }; + protected handlePivotMetaSort( + sortFieldId: string, + sortedDimensionValues: string[], + ) { + const { rows, columns } = this.fields; + if (includes(rows, sortFieldId)) { + this.rowPivotMeta = getSortedPivotMeta({ + pivotMeta: this.rowPivotMeta, + dimensions: rows, + sortFieldId, + sortedDimensionValues, + }); + } else if (includes(columns, sortFieldId)) { + this.colPivotMeta = getSortedPivotMeta({ + pivotMeta: this.colPivotMeta, + dimensions: columns as string[], + sortFieldId, + sortedDimensionValues, + }); + } + } + public processDataCfg(dataCfg: S2DataConfig): S2DataConfig { const { data, meta = [], fields, sortParams = [], totalData } = dataCfg; const { columns, rows, values, valueInCols, customValueOrder } = fields; @@ -283,43 +305,46 @@ export class PivotDataSet extends BaseDataSet { }; } - public getDimensionsByField(field: string): string[] { + protected getFieldsAndPivotMetaByField(field: string) { const { rows = [], columns = [] } = this.fields || {}; - if (includes(rows, field)) { - return rows; + if (rows.includes(field)) { + return { + dimensions: rows, + pivotMeta: this.rowPivotMeta, + }; } - if (includes(columns, field)) { - return columns as string[]; + if (columns.includes(field)) { + return { + dimensions: columns as string[], + pivotMeta: this.colPivotMeta, + }; } - return []; + return {}; } public getDimensionValues(field: string, query: Query = {}): string[] { - const dimensions = this.getDimensionsByField(field); + const { pivotMeta, dimensions } = this.getFieldsAndPivotMetaByField(field); - if (isEmpty(dimensions)) { + if (!pivotMeta || !dimensions) { return []; } - const idx = indexOf(dimensions, field); const dimensionValues = transformDimensionsValues( query, dimensions, MULTI_VALUE, ); - const allCurrentFieldDimensionValues = - this.sortedDimensionValues[field] ?? []; + const values = getSatisfiedPivotMetaValues({ + pivotMeta, + dimensionValues, + fields: dimensions, + fieldIdx: indexOf(dimensions, field), + queryType: QueryDataType.DetailOnly, + sortedDimensionValues: this.sortedDimensionValues, + }); - const target = allCurrentFieldDimensionValues.filter((dimValue) => - satisfyDimensionValues( - dimensionValues, - dimValue, - idx, - QueryDataType.DetailOnly, - ), - ); - return uniq(getDimensionsWithoutPathPre(target)); + return uniq(values.map((v) => v.value)); } getTotalValue(query: Query, totalStatus?: TotalStatus) { @@ -505,9 +530,11 @@ export class PivotDataSet extends BaseDataSet { const { rowQueries, colQueries } = getFlattenDimensionValues({ rowDimensionValues, colDimensionValues, + rowPivotMeta: this.rowPivotMeta, + colPivotMeta: this.colPivotMeta, rowFields: totalRows, colFields: columns as string[], - sortedDimensionValue: this.sortedDimensionValues, + sortedDimensionValues: this.sortedDimensionValues, queryType, }); @@ -529,12 +556,12 @@ export class PivotDataSet extends BaseDataSet { for (let i = 0; i < path.length; i++) { const current = path[i]; if (hadMultiField) { - if (shouldQueryMultiData(current)) { + if (isMultiValue(current)) { result = flattenIndexesData(result, queryType); } else { result = map(result, (item) => item[current]).filter(Boolean); } - } else if (shouldQueryMultiData(current)) { + } else if (isMultiValue(current)) { hadMultiField = true; result = [result]; i--; diff --git a/packages/s2-core/src/data-set/table-data-set.ts b/packages/s2-core/src/data-set/table-data-set.ts index 6baadc5da1..f4704a1224 100644 --- a/packages/s2-core/src/data-set/table-data-set.ts +++ b/packages/s2-core/src/data-set/table-data-set.ts @@ -150,7 +150,7 @@ export class TableDataSet extends BaseDataSet { }); }; - public getDimensionValues(field: string, query?: Query): string[] { + public getDimensionValues(): string[] { return []; } diff --git a/packages/s2-core/src/facet/layout/build-row-tree-hierarchy.ts b/packages/s2-core/src/facet/layout/build-row-tree-hierarchy.ts index 052d8115a0..e990e478af 100644 --- a/packages/s2-core/src/facet/layout/build-row-tree-hierarchy.ts +++ b/packages/s2-core/src/facet/layout/build-row-tree-hierarchy.ts @@ -1,8 +1,7 @@ import { isNumber } from 'lodash'; -import { i18n, ID_SEPARATOR, ROOT_ID } from '../../common'; -import type { PivotDataSet } from '../../data-set'; +import { i18n } from '../../common'; import type { SpreadSheet } from '../../sheet-type'; -import { filterTotal, getListBySorted } from '../../utils/data-set-operate'; +import { filterTotal } from '../../utils/data-set-operate'; import { generateId } from '../../utils/layout/generate-id'; import type { FieldValue, TreeHeaderParams } from '../layout/interface'; import { layoutArrange, layoutHierarchy } from '../layout/layout-hooks'; @@ -30,8 +29,6 @@ const addTotals = ( } }; -const NODE_ID_PREFIX_LEN = (ROOT_ID + ID_SEPARATOR).length; - /** * Only row header has tree hierarchy, in this scene: * 1、value in rows is not work => valueInCols is ineffective @@ -41,32 +38,12 @@ const NODE_ID_PREFIX_LEN = (ROOT_ID + ID_SEPARATOR).length; export const buildRowTreeHierarchy = (params: TreeHeaderParams) => { const { parentNode, currentField, level, facetCfg, hierarchy, pivotMeta } = params; - const { - spreadsheet, - dataSet, - collapsedRows, - hierarchyCollapse, - rowExpandDepth, - } = facetCfg; + const { spreadsheet, collapsedRows, hierarchyCollapse, rowExpandDepth } = + facetCfg; const { query, id: parentId } = parentNode; const isDrillDownItem = spreadsheet.dataCfg.fields.rows?.length <= level; - const sortedDimensionValues = - (dataSet as PivotDataSet)?.sortedDimensionValues?.[currentField] || []; - const unsortedDimValues = filterTotal(Array.from(pivotMeta.keys())); - const dimValues = getListBySorted( - unsortedDimValues, - sortedDimensionValues, - (dimVal) => { - // 根据父节点 id,修改 unsortedDimValues 里用于比较的值,使其格式与 sortedDimensionValues 排序值一致 - // unsortedDimValues:['成都', '绵阳'] - // sortedDimensionValues: ['四川[&]成都'] - if (ROOT_ID === parentId) { - return dimVal; - } - return generateId(parentId, dimVal).slice(NODE_ID_PREFIX_LEN); - }, - ); + const dimValues = filterTotal(Array.from(pivotMeta.keys())); let fieldValues: FieldValue[] = layoutArrange( dimValues, diff --git a/packages/s2-core/src/utils/dataset/pivot-data-set.ts b/packages/s2-core/src/utils/dataset/pivot-data-set.ts index 88eec865cc..ea32d44660 100644 --- a/packages/s2-core/src/utils/dataset/pivot-data-set.ts +++ b/packages/s2-core/src/utils/dataset/pivot-data-set.ts @@ -3,13 +3,16 @@ import { flatMap, forEach, get, + indexOf, intersection, isArray, isEmpty, isNull, last, set, + sortBy, } from 'lodash'; +import type { PickEssential } from '../../common/interface/utils'; import { EXTRA_FIELD, ID_SEPARATOR, @@ -24,12 +27,14 @@ import type { DataType, FlattingIndexesData, PivotMeta, + PivotMetaValue, SortedDimensionValues, TotalStatus, } from '../../data-set/interface'; import type { Node } from '../../facet/layout/node'; +import { generateId } from '../layout'; -export function shouldQueryMultiData(pathValue: string | number) { +export function isMultiValue(pathValue: string | number) { return pathValue === MULTI_VALUE; } @@ -155,11 +160,13 @@ export function getDataPath(params: DataPathParams) { .join(ID_SEPARATOR); }; - const appendValues = () => { + const appendValues = (parent: string) => { const map = new Map(); - valueFields?.forEach((v, idx) => { - map.set(v, { - level: idx, + valueFields?.forEach((value, level) => { + map.set(value, { + id: generateId(parent, value), + value, + level, children: new Map(), }); }); @@ -190,21 +197,29 @@ export function getDataPath(params: DataPathParams) { } else { level = currentMeta.size + 1; } + + const id = dimensionValues + .slice(0, i + 1) + .map((it) => String(it)) + .join(ID_SEPARATOR); + currentMeta.set(value, { + id, + value, level, children: - dimensions[i + 1] === EXTRA_FIELD ? appendValues() : new Map(), + dimensions[i + 1] === EXTRA_FIELD ? appendValues(id) : new Map(), }); onFirstCreate?.({ dimension: dimensions?.[i], - dimensionPath: dimensionValues.slice(0, i + 1), + dimensionPath: id, careRepeated, }); } const meta = currentMeta?.get(value); - path.push(value === MULTI_VALUE ? value : meta?.level); + path.push(isMultiValue(value) ? value : meta?.level); if (meta) { if (isFirstCreate && meta.childField !== dimensions?.[i + 1]) { @@ -268,11 +283,7 @@ export function transformIndexesData(params: Param) { ( sortedDimensionValues[dimension] || (sortedDimensionValues[dimension] = []) - ).push( - // 拼接维度路径 - // [1, undefined] => ['1', 'undefined'] => '1[&]undefined - dimensionPath.map((it) => `${it}`).join(ID_SEPARATOR), - ); + ).push(dimensionPath); }; data.forEach((item) => { @@ -375,7 +386,7 @@ export function existDimensionTotalGroup(path: string[]) { let multiIdx = null; for (let i = 0; i < path.length; i++) { const element = path[i]; - if (element === MULTI_VALUE) { + if (isMultiValue(element)) { multiIdx = i; } else if (!isNull(multiIdx) && multiIdx < i) { return true; @@ -384,101 +395,120 @@ export function existDimensionTotalGroup(path: string[]) { return false; } -export function satisfyDimensionValues( - dimensionValues: string[], - sortedDimensionValue: string, - fieldIdx: number, - queryType: QueryDataType, -) { - return sortedDimensionValue.split(ID_SEPARATOR).every((value, idx) => { - // 只检查截止到目标 field 为止的内容 - if (idx > fieldIdx) { - return true; +export function getSatisfiedPivotMetaValues(params: { + pivotMeta: PivotMeta; + dimensionValues: string[]; + fieldIdx: number; + queryType: QueryDataType; + fields: string[]; + sortedDimensionValues: SortedDimensionValues; +}) { + const { + pivotMeta, + dimensionValues, + fieldIdx, + queryType, + fields, + sortedDimensionValues, + } = params; + const rootContainer = { + children: pivotMeta, + } as PivotMetaValue; + + let metaValueList = [rootContainer]; + + function flattenMetaValue(list: PivotMetaValue[], field: string) { + const allValues = flatMap(list, (metaValue) => { + const values = [...metaValue.children.values()]; + return queryType === QueryDataType.All + ? values + : values.filter((v) => v.value !== TOTAL_VALUE); + }); + if (list.length > 1) { + // 从不同父维度中获取的子维度需要再排一次,比如province => city 按照字母倒序,那么在获取了所有 province 的 city 后需要再排一次 + const sortedDimensionValue = sortedDimensionValues[field] ?? []; + return sortBy(allValues, (item) => + indexOf(sortedDimensionValue, item.id), + ); } + return allValues; + } - const dimension = dimensionValues[idx]; - - if (dimension === MULTI_VALUE) { - if (queryType === QueryDataType.All) { - return true; - } - if (queryType === QueryDataType.DetailOnly && value !== TOTAL_VALUE) { - return true; - } - return false; + for (let i = 0; i <= fieldIdx; i++) { + const dimensionValue = dimensionValues[i]; + const field = fields[i]; + if (!dimensionValue || dimensionValue === MULTI_VALUE) { + metaValueList = flattenMetaValue(metaValueList, field); + } else { + metaValueList = metaValueList + .map((v) => v.children.get(dimensionValue)) + .filter(Boolean); } + } - return value === dimension; - }); + return metaValueList; } -export function flattenDimensionValues( - fields: string[], - dimensionValues: string[], - sortedDimensionValues: SortedDimensionValues, - queryType: QueryDataType = QueryDataType.All, -) { - fields = fields.filter((i) => i !== EXTRA_FIELD); - +export function flattenDimensionValues(params: { + dimensionValues: string[]; + pivotMeta: PivotMeta; + fields: string[]; + sortedDimensionValues: SortedDimensionValues; + queryType?: QueryDataType; +}) { + const { + dimensionValues, + pivotMeta, + fields, + sortedDimensionValues, + queryType = QueryDataType.All, + } = params; if (!existDimensionTotalGroup(dimensionValues)) { return [dimensionValues]; } - let queries = [dimensionValues]; - let hadMultiBefore = false; - for (let i = 0; i < fields.length; i++) { - const field = fields[i]; - const value = dimensionValues[i]; - if (value === MULTI_VALUE) { - hadMultiBefore = true; - } else if (hadMultiBefore) { - const temp = []; - const allSortedDimensionValues = sortedDimensionValues[field]; - for (const sortedValues of allSortedDimensionValues) { - for (const query of queries) { - if (satisfyDimensionValues(query, sortedValues, i, queryType)) { - const newQuery = sortedValues - .split(ID_SEPARATOR) - .slice(0, i) - .concat(query.slice(i)); - temp.push(newQuery); - } - } - } - queries = temp; - } - } - return queries; + const metaValues = getSatisfiedPivotMetaValues({ + pivotMeta, + dimensionValues, + fieldIdx: dimensionValues.length - 1, + queryType, + fields, + sortedDimensionValues, + }); + + return metaValues.map((v) => v.id.split(ID_SEPARATOR)); } -export function getFlattenDimensionValues(params: { - rowDimensionValues: string[]; - colDimensionValues: string[]; - rowFields: string[]; - colFields: string[]; - sortedDimensionValue: SortedDimensionValues; - queryType: QueryDataType; -}) { +export function getFlattenDimensionValues( + params: PickEssential & { + sortedDimensionValues: SortedDimensionValues; + queryType: QueryDataType; + }, +) { const { rowFields, rowDimensionValues, + rowPivotMeta, colFields, colDimensionValues, - sortedDimensionValue, + colPivotMeta, queryType, + sortedDimensionValues, } = params; - const rowQueries = flattenDimensionValues( - rowFields, - rowDimensionValues, - sortedDimensionValue, + const rowQueries = flattenDimensionValues({ + dimensionValues: rowDimensionValues, + pivotMeta: rowPivotMeta, + fields: rowFields, + sortedDimensionValues, queryType, - ); - const colQueries = flattenDimensionValues( - colFields, - colDimensionValues, - sortedDimensionValue, + }); + const colQueries = flattenDimensionValues({ + dimensionValues: colDimensionValues, + pivotMeta: colPivotMeta, + fields: colFields, + sortedDimensionValues, queryType, - ); + }); return { rowQueries, colQueries, diff --git a/packages/s2-core/src/utils/sort-action.ts b/packages/s2-core/src/utils/sort-action.ts index 99c50a7850..1d533400dd 100644 --- a/packages/s2-core/src/utils/sort-action.ts +++ b/packages/s2-core/src/utils/sort-action.ts @@ -2,6 +2,7 @@ import { compact, concat, endsWith, + flatMap, includes, indexOf, isEmpty, @@ -9,6 +10,7 @@ import { isNil, keys, map, + sortBy, split, toUpper, uniq, @@ -21,7 +23,13 @@ import { } from '../common/constant'; import type { Fields, SortMethod, SortParam } from '../common/interface'; import type { PivotDataSet } from '../data-set'; -import type { DataType, SortActionParams } from '../data-set/interface'; +import type { + DataType, + PivotMeta, + PivotMetaValue, + SortActionParams, + SortPivotMetaParams, +} from '../data-set/interface'; import { getListBySorted, sortByItems } from '../utils/data-set-operate'; import { getDimensionsWithParentPath } from '../utils/dataset/pivot-data-set'; import { getLeafColumnsWithKey } from '../facet/utils'; @@ -30,7 +38,7 @@ export const isAscSort = (sortMethod) => toUpper(sortMethod) === 'ASC'; export const isDescSort = (sortMethod) => toUpper(sortMethod) === 'DESC'; -const canTobeNumber = (a?: string | number) => !isNaN(Number(a)); +const couldConvertToNumber = (a?: string | number) => !isNaN(Number(a)); /** * 执行排序 @@ -52,7 +60,7 @@ export const sortAction = ( if (key) { a = pre[key] as string | number; b = next[key] as string | number; - if (canTobeNumber(a) && canTobeNumber(b)) { + if (couldConvertToNumber(a) && couldConvertToNumber(b)) { return (Number(a) - Number(b)) * sort; } if (a && specialValues?.includes(a?.toString())) { @@ -374,3 +382,34 @@ export const getSortTypeIcon = (sortParam: SortParam, isSortCell?: boolean) => { return 'SortDown'; } }; + +/** + * 对 pivot meta 中的内容进行排序,返回新的 sorted pivot meta + */ +export const getSortedPivotMeta = (params: SortPivotMetaParams) => { + const { pivotMeta, dimensions, sortedDimensionValues, sortFieldId } = params; + const rootContainer = { + children: pivotMeta, + } as PivotMetaValue; + let metaValueList = [rootContainer]; + + for (const dimension of dimensions) { + if (dimension !== sortFieldId) { + metaValueList = flatMap(metaValueList, (metaValue) => { + return [...metaValue.children.values()]; + }); + } else { + metaValueList.forEach((metaValue) => { + const values = [...metaValue.children.values()]; + + const entities = sortBy(values, (value) => { + return indexOf(sortedDimensionValues, value.id); + }).map((value) => [value.value, value] as [string, PivotMetaValue]); + + metaValue.children = new Map(entities) as PivotMeta; + }); + break; + } + } + return rootContainer.children; +}; diff --git a/s2-site/docs/manual/basic/sort/advanced.zh.md b/s2-site/docs/manual/basic/sort/advanced.zh.md index 1f32f98f4d..5a89885de1 100644 --- a/s2-site/docs/manual/basic/sort/advanced.zh.md +++ b/s2-site/docs/manual/basic/sort/advanced.zh.md @@ -112,7 +112,7 @@ advancedSortCfg: { | 属性 | 类型 | 必选 | 默认值 | 功能描述 | | ------- | ------------------------------------------ | --- | ----- | --------- | | label | `string` | | ✓ | 规则名称 | -| value | `'sortMethod' | 'sortBy' | 'sortByMeasure'` | ✓ | | 规则值 | +| value | `'sortMethod' \| 'sortBy' \| 'sortByMeasure'` | ✓ | | 规则值 | | children | `RuleOption[]` | | ✓ | 规则子列表 | row