Skip to content

Commit

Permalink
fix: 修复下载数据重复 close #2718 (#2719)
Browse files Browse the repository at this point in the history
* fix: 修复下载数据重复 close #2718

* fix: 修复透视表导出报错问题

* test: 提交单测

* refactor: 修改变量名

* refactor: 抽取公用变量、方法到 BaseDataCellCopy 基类

---------

Co-authored-by: Harvey Wang <[email protected]>
  • Loading branch information
wyh888 and Harvey Wang authored May 14, 2024
1 parent 185b59c commit f0d5192
Show file tree
Hide file tree
Showing 6 changed files with 140 additions and 22 deletions.
37 changes: 37 additions & 0 deletions packages/s2-core/__tests__/unit/utils/export/export-pivot-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
TAB_SEPARATOR,
} from '../../../../src/common/constant';
import { asyncGetAllPlainData, PivotSheet } from '../../../../src';
import { generateRawData } from '../../../util';
import { CopyMIMEType } from '@/common/interface/export';

describe('PivotSheet Export Test', () => {
Expand Down Expand Up @@ -464,4 +465,40 @@ describe('PivotSheet Export Test', () => {
expect(data.split(LINE_SEPARATOR)).toMatchSnapshot();
},
);

// https://github.com/antvis/S2/issues/2718
it('should export the correct amount of data and have no duplicate data', async () => {
const typeCount = 100;
const subTypeCount = 100;

const bigData = generateRawData(
{ province: 10, city: 1 },
{ type: typeCount, subType: subTypeCount },
);

const sheet = new PivotSheet(
getContainer(),
assembleDataCfg({
data: bigData,
fields: {
rows: ['type', 'subType'],
columns: ['province', 'city'],
values: ['number'],
},
}),
assembleOptions({}),
);

await sheet.render();
const data = await asyncGetAllPlainData({
sheetInstance: sheet,
split: CSV_SEPARATOR,
formatOptions: true,
});

// row header count + data count
const count = typeCount * subTypeCount + 3;

expect(data.split(LINE_SEPARATOR)).toHaveLength(count);
});
});
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
/* eslint-disable jest/expect-expect */
import { slice } from 'lodash';
import { data as originData } from 'tests/data/mock-dataset.json';
import { assembleDataCfg, assembleOptions } from '../../../util';
import {
assembleDataCfg,
assembleOptions,
generateRawData,
} from '../../../util';
import {
createTableSheet,
expectMatchSnapshot,
Expand Down Expand Up @@ -433,4 +437,33 @@ describe('TableSheet Export Test', () => {
formatData: false,
});
});

// https://github.com/antvis/S2/issues/2718
it('should export the correct amount of data and have no duplicate data', async () => {
const bigData = generateRawData(
{ province: 10, city: 10 },
{ type: 10, sub_type: 10 },
);

const tableSheet = new TableSheet(
getContainer(),
assembleDataCfg({
data: bigData,
fields: {
columns: ['province', 'city', 'type', 'sub_type', 'number'],
},
}),
assembleOptions(),
);

await tableSheet.render();
const data = await asyncGetAllPlainData({
sheetInstance: tableSheet,
split: CSV_SEPARATOR,
formatOptions: true,
});

// The first line is the header, so the number of lines should be the same as the number of data plus one
expect(data.split(LINE_SEPARATOR)).toHaveLength(bigData.length + 1);
});
});
28 changes: 28 additions & 0 deletions packages/s2-core/__tests__/util/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,34 @@ export const assembleDataCfg = (...dataCfg: Partial<S2DataConfig>[]) => {
return customMerge<S2DataConfig>(DEFAULT_DATA_CONFIG, s2DataCfg, ...dataCfg);
};

export const generateRawData = (
row: Record<string, number>,
col: Record<string, number>,
) => {
const res: Record<string, any>[] = [];

const rowKeys = Object.keys(row);
const colKeys = Object.keys(col);

for (let i = 0; i < row[rowKeys[0]]; i++) {
for (let j = 0; j < row[rowKeys[1]]; j++) {
for (let m = 0; m < col[colKeys[0]]; m++) {
for (let n = 0; n < col[colKeys[1]]; n++) {
res.push({
province: `p${i}`,
city: `c${j}`,
type: `type${m}`,
subType: `subType${n}`,
number: i * n,
});
}
}
}
}

return res;
};

export const TOTALS_OPTIONS: S2Options['totals'] = {
row: {
showGrandTotals: true,
Expand Down
13 changes: 12 additions & 1 deletion packages/s2-core/src/utils/export/copy/base-data-cell-copy.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { TAB_SEPARATOR, type DataItem } from '../../../common';
import {
AsyncRenderThreshold,
TAB_SEPARATOR,
type DataItem,
} from '../../../common';
import type {
CopyableHTML,
CopyablePlain,
Expand All @@ -16,6 +20,13 @@ export abstract class BaseDataCellCopy {

protected config: CopyAndExportUnifyConfig;

protected idleCallbackCount: number;

protected initIdleCallbackCount(rowLength: number) {
this.idleCallbackCount =
rowLength >= AsyncRenderThreshold ? AsyncRenderThreshold : rowLength;
}

constructor(params: SheetCopyConstructorParams) {
const { spreadsheet, isExport = false, config } = params;

Expand Down
24 changes: 14 additions & 10 deletions packages/s2-core/src/utils/export/copy/pivot-data-cell-copy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
zip,
} from 'lodash';
import {
AsyncRenderThreshold,
CornerNodeType,
EXTRA_FIELD,
VALUE_FIELD,
Expand Down Expand Up @@ -153,18 +152,20 @@ export class PivotDataCellCopy extends BaseDataCellCopy {
// 因为每次 requestIdleCallback 执行的时间不一样,所以需要记录下当前执行到的 this.leafRowNodes 和 this.leafColNodes
const dataMatrixIdleCallback = (deadline: IdleDeadline) => {
const rowLength: number = this.leafRowNodes.length;

// requestIdleCallback 浏览器空闲时会多次执行, 只有一行数据时执行一次即可, 避免生成重复数据
let count =
rowLength >= AsyncRenderThreshold
? AsyncRenderThreshold
: rowLength;
this.initIdleCallbackCount(rowLength);

while (
(deadline.timeRemaining() > 0 || deadline.didTimeout) &&
rowIndex <= rowLength - 1 &&
count > 0
this.idleCallbackCount > 0
) {
for (let j = rowIndex; j < rowLength && count > 0; j++) {
for (
let j = rowIndex;
j < rowLength && this.idleCallbackCount > 0;
j++
) {
const row: DataItem[] = [];
const rowNode = this.leafRowNodes[j];

Expand All @@ -183,15 +184,18 @@ export class PivotDataCellCopy extends BaseDataCellCopy {

row.push(dataItem);
}
rowIndex = j;
rowIndex++;
matrix.push(row);
count--;
this.idleCallbackCount--;
}
}

if (rowIndex === rowLength - 1) {
if (rowIndex === rowLength) {
resolve(matrix);
} else {
// 重置 idleCallbackCount,避免下次 requestIdleCallback 时 idleCallbackCount 为 0
this.initIdleCallbackCount(rowLength);

requestIdleCallback(dataMatrixIdleCallback);
}
};
Expand Down
25 changes: 15 additions & 10 deletions packages/s2-core/src/utils/export/copy/table-copy.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { map, zip } from 'lodash';
import {
AsyncRenderThreshold,
SERIES_NUMBER_FIELD,
getDefaultSeriesNumberText,
type CellMeta,
Expand Down Expand Up @@ -94,20 +93,22 @@ class TableDataCellCopy extends BaseDataCellCopy {
try {
const dataMatrixIdleCallback = (deadline: IdleDeadline) => {
const rowLength = this.displayData.length;

// requestIdleCallback 浏览器空闲时会多次执行, 只有一行数据时执行一次即可, 避免生成重复数据
let count =
rowLength >= AsyncRenderThreshold
? AsyncRenderThreshold
: rowLength;
this.initIdleCallbackCount(rowLength);

while (
(deadline.timeRemaining() > 0 ||
deadline.didTimeout ||
process.env['NODE_ENV'] === 'test') &&
rowIndex <= rowLength - 1 &&
count > 0
this.idleCallbackCount > 0
) {
for (let j = rowIndex; j < rowLength && count > 0; j++) {
for (
let j = rowIndex;
j < rowLength && this.idleCallbackCount > 0;
j++
) {
const rowData = this.displayData[j];
const row: string[] = [];

Expand All @@ -131,15 +132,19 @@ class TableDataCellCopy extends BaseDataCellCopy {

row.push(dataItem as string);
}
rowIndex = j;
// 生成一行数据后,rowIndex + 1,下次 requestIdleCallback 时从下一行开始
rowIndex++;
result.push(row);
count--;
this.idleCallbackCount--;
}
}

if (rowIndex === rowLength - 1) {
if (rowIndex === rowLength) {
resolve(result);
} else {
// 重置 idleCallbackCount,避免下次 requestIdleCallback 时 idleCallbackCount 为 0
this.initIdleCallbackCount(rowLength);

requestIdleCallback(dataMatrixIdleCallback);
}
};
Expand Down

0 comments on commit f0d5192

Please sign in to comment.