Skip to content

Commit b84c4fc

Browse files
authored
Merge pull request #4565 from VisActor/fix/change-formula-dependency-address
Fix/change formula dependency address
2 parents 0114a1a + 881073a commit b84c4fc

33 files changed

Lines changed: 3854 additions & 368 deletions

packages/vtable-plugins/src/add-row-column.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -235,12 +235,14 @@ export class AddRowColumnPlugin implements pluginsDefinition.IVTablePlugin {
235235
// width: 100
236236
// });
237237
// this.table.updateColumns(columns);
238-
this.table.addColumn(
239-
{
240-
field: addColIndex,
241-
title: `New Column ${col}`,
242-
width: 100
243-
},
238+
this.table.addColumns(
239+
[
240+
{
241+
field: addColIndex,
242+
title: `New Column ${col}`,
243+
width: 100
244+
}
245+
],
244246
addColIndex,
245247
true
246248
);

packages/vtable-plugins/src/column-series.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export class ColumnSeriesPlugin implements pluginsDefinition.IVTablePlugin {
4343
const e = eventArgs.event;
4444
if (this.pluginOptions.autoExtendColumnTriggerKeys?.includes(e.key)) {
4545
if (this.table.stateManager.select.cellPos.col === this.table.colCount - 1) {
46-
this.table.addColumn(this.generateColumn(this.table.colCount - 1) as ColumnDefine);
46+
this.table.addColumns([this.generateColumn(this.table.colCount - 1) as ColumnDefine]);
4747
}
4848
}
4949
}

packages/vtable-plugins/src/contextmenu/handle-menu-helper.ts

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { ListTable } from '@visactor/vtable';
1+
import type { ColumnDefine, ListTable } from '@visactor/vtable';
22

33
/**
44
* 菜单辅助类
@@ -70,11 +70,13 @@ export class MenuHandler {
7070
if (colIndex === undefined) {
7171
return;
7272
}
73-
if (typeof (table as any).addColumn === 'function') {
73+
if (typeof (table as any).addColumns === 'function') {
74+
const toAddColumns: ColumnDefine[] = [];
7475
// 使用表格API插入列
7576
for (let i = 0; i < count; i++) {
76-
table.addColumn({ field: colIndex }, colIndex, true);
77+
toAddColumns.push({ field: colIndex - i });
7778
}
79+
table.addColumns(toAddColumns, colIndex, true);
7880
}
7981
}
8082

@@ -85,10 +87,12 @@ export class MenuHandler {
8587
if (colIndex === undefined) {
8688
return;
8789
}
88-
if (typeof (table as any).addColumn === 'function') {
90+
if (typeof (table as any).addColumns === 'function') {
91+
const toAddColumns: ColumnDefine[] = [];
8992
for (let i = 0; i < count; i++) {
90-
table.addColumn({ field: colIndex + 1 }, colIndex + 1, true);
93+
toAddColumns.push({ field: colIndex + 1 });
9194
}
95+
table.addColumns(toAddColumns, colIndex + 1, true);
9296
}
9397
}
9498

@@ -131,7 +135,7 @@ export class MenuHandler {
131135
if (colIndex === undefined) {
132136
return;
133137
}
134-
if (typeof (table as any).deleteColumn === 'function') {
138+
if (typeof (table as any).deleteColumns === 'function') {
135139
const selectRanges = table.stateManager.select.ranges;
136140
//处理selectRanges中的每个选择区域,记录到deleteColIndexs数组中,保证没给col值记录一次,且按倒序排序
137141
const deleteColIndexs: number[] = [];
@@ -143,10 +147,11 @@ export class MenuHandler {
143147
}
144148
}
145149
}
146-
deleteColIndexs.sort((a, b) => b - a);
147-
for (let i = 0; i < deleteColIndexs.length; i++) {
148-
(table as any).deleteColumn(deleteColIndexs[i]); //TODO 性能考虑的话 这样做不好
149-
}
150+
// deleteColIndexs.sort((a, b) => b - a);
151+
// for (let i = 0; i < deleteColIndexs.length; i++) {
152+
// (table as any).deleteColumn(deleteColIndexs[i]); //TODO 性能考虑的话 这样做不好
153+
// }
154+
table.deleteColumns(deleteColIndexs, true);
150155
}
151156
}
152157

packages/vtable-plugins/src/paste-add-row-column.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,13 @@ export class PasteAddRowColumnPlugin implements pluginsDefinition.IVTablePlugin
5656
if (this.pluginOptions?.addColumnCallback) {
5757
this.pluginOptions.addColumnCallback(newColIndex, this.table);
5858
} else {
59-
this.table.addColumn({
60-
field: `field_${newColIndex}`,
61-
title: `New Column ${newColIndex}`,
62-
width: 100
63-
});
59+
this.table.addColumns([
60+
{
61+
field: `field_${newColIndex}`,
62+
title: `New Column ${newColIndex}`,
63+
width: 100
64+
}
65+
]);
6466
}
6567
}
6668
}

packages/vtable-sheet/__tests__/active-sheet-race-condition.test.ts

Lines changed: 56 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,38 @@ const mockVTableSheet = {
1414
getActiveSheet: (): any => null
1515
} as unknown as VTableSheet;
1616

17+
// 测试用的基本标准化函数
18+
function normalizeTestData(data: unknown[][]): unknown[][] {
19+
if (!Array.isArray(data) || data.length === 0) {
20+
return [['']];
21+
}
22+
23+
const maxCols = Math.max(...data.map(row => (Array.isArray(row) ? row.length : 0)));
24+
25+
return data.map(row => {
26+
if (!Array.isArray(row)) {
27+
return Array(maxCols).fill('');
28+
}
29+
30+
const normalizedRow = row.map(cell => {
31+
if (typeof cell === 'string') {
32+
if (cell.startsWith('=')) {
33+
return cell; // 保持公式不变
34+
}
35+
const num = Number(cell);
36+
return !isNaN(num) && cell.trim() !== '' ? num : cell;
37+
}
38+
return cell ?? '';
39+
});
40+
41+
while (normalizedRow.length < maxCols) {
42+
normalizedRow.push('');
43+
}
44+
45+
return normalizedRow;
46+
});
47+
}
48+
1749
describe('Active Sheet Race Condition Fix', () => {
1850
let formulaManager: FormulaManager;
1951

@@ -26,24 +58,31 @@ describe('Active Sheet Race Condition Fix', () => {
2658
});
2759

2860
test('should only set first sheet as active, not subsequent sheets', () => {
29-
// Add first sheet
30-
formulaManager.addSheet('Sheet1', [['Data'], ['100']]);
61+
// Add first sheet with normalized data
62+
const sheet1Data = normalizeTestData([['Data'], ['100']]);
63+
formulaManager.addSheet('Sheet1', sheet1Data);
3164
expect(formulaManager.getActiveSheet()).toBe('Sheet1');
3265

3366
// Add second sheet - should NOT become active
34-
formulaManager.addSheet('Sheet2', [['Data'], ['200']]);
67+
const sheet2Data = normalizeTestData([['Data'], ['200']]);
68+
formulaManager.addSheet('Sheet2', sheet2Data);
3569
expect(formulaManager.getActiveSheet()).toBe('Sheet1'); // Should still be Sheet1
3670

3771
// Add third sheet - should NOT become active
38-
formulaManager.addSheet('Sheet3', [['Data'], ['300']]);
72+
const sheet3Data = normalizeTestData([['Data'], ['300']]);
73+
formulaManager.addSheet('Sheet3', sheet3Data);
3974
expect(formulaManager.getActiveSheet()).toBe('Sheet1'); // Should still be Sheet1
4075
});
4176

4277
test('should handle manual active sheet switching correctly', () => {
43-
// Add multiple sheets
44-
formulaManager.addSheet('Sheet1', [['Data'], ['100']]);
45-
formulaManager.addSheet('Sheet2', [['Data'], ['200']]);
46-
formulaManager.addSheet('Sheet3', [['Data'], ['300']]);
78+
// Add multiple sheets with normalized data
79+
const sheet1Data = normalizeTestData([['Data'], ['100']]);
80+
const sheet2Data = normalizeTestData([['Data'], ['200']]);
81+
const sheet3Data = normalizeTestData([['Data'], ['300']]);
82+
83+
formulaManager.addSheet('Sheet1', sheet1Data);
84+
formulaManager.addSheet('Sheet2', sheet2Data);
85+
formulaManager.addSheet('Sheet3', sheet3Data);
4786

4887
// Initially Sheet1 should be active
4988
expect(formulaManager.getActiveSheet()).toBe('Sheet1');
@@ -53,7 +92,8 @@ describe('Active Sheet Race Condition Fix', () => {
5392
expect(formulaManager.getActiveSheet()).toBe('Sheet2');
5493

5594
// Add another sheet - should NOT change active sheet
56-
formulaManager.addSheet('Sheet4', [['Data'], ['400']]);
95+
const sheet4Data = normalizeTestData([['Data'], ['400']]);
96+
formulaManager.addSheet('Sheet4', sheet4Data);
5797
expect(formulaManager.getActiveSheet()).toBe('Sheet2'); // Should still be Sheet2
5898

5999
// Switch to Sheet3
@@ -62,24 +102,27 @@ describe('Active Sheet Race Condition Fix', () => {
62102
});
63103

64104
test('should handle formulas correctly with proper active sheet context', () => {
65-
// Add multiple sheets with different data
66-
formulaManager.addSheet('Sheet1', [
105+
// Add multiple sheets with normalized data
106+
const sheet1Data = normalizeTestData([
67107
['A', 'B'],
68108
['10', ''],
69109
['', '']
70110
]);
111+
formulaManager.addSheet('Sheet1', sheet1Data);
71112

72-
formulaManager.addSheet('Sheet2', [
113+
const sheet2Data = normalizeTestData([
73114
['A', 'B'],
74115
['20', ''],
75116
['', '']
76117
]);
118+
formulaManager.addSheet('Sheet2', sheet2Data);
77119

78-
formulaManager.addSheet('Sheet3', [
120+
const sheet3Data = normalizeTestData([
79121
['A', 'B'],
80122
['30', ''],
81123
['', '']
82124
]);
125+
formulaManager.addSheet('Sheet3', sheet3Data);
83126

84127
// Initially Sheet1 is active
85128
expect(formulaManager.getActiveSheet()).toBe('Sheet1');
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// @ts-nocheck
2+
import { FormulaManager } from '../src/managers/formula-manager';
3+
import type VTableSheet from '../src/components/vtable-sheet';
4+
5+
// Mock VTableSheet for testing
6+
const mockVTableSheet = {
7+
getSheetManager: () => ({
8+
getSheet: (sheetKey: string) => ({
9+
sheetTitle: 'Test Sheet',
10+
sheetKey: sheetKey,
11+
showHeader: true,
12+
columnCount: 10,
13+
rowCount: 10,
14+
columns: [] as any[]
15+
})
16+
}),
17+
getActiveSheet: (): any => ({
18+
tableInstance: {
19+
changeCellValue: () => {
20+
/* Mock implementation */
21+
}
22+
}
23+
}),
24+
getSheet: (sheetKey: string) => ({
25+
columnCount: 10,
26+
rowCount: 10
27+
}),
28+
formulaManager: null // 这会在创建FormulaManager时自动设置
29+
} as unknown as VTableSheet;
30+
31+
describe('Column Debug Test', () => {
32+
let formulaManager: FormulaManager;
33+
34+
beforeEach(() => {
35+
formulaManager = new FormulaManager(mockVTableSheet);
36+
mockVTableSheet.formulaManager = formulaManager;
37+
});
38+
39+
afterEach(() => {
40+
formulaManager.release();
41+
});
42+
43+
test('debug column deletion logic', () => {
44+
const sheetKey = 'Sheet1';
45+
46+
// 添加包含数据的工作表
47+
formulaManager.addSheet(sheetKey, [
48+
['A', 'B', 'C'],
49+
['10', '20', '30'],
50+
['', '', '']
51+
]);
52+
53+
// 在C3中创建引用A2:B2的求和公式
54+
formulaManager.setCellContent({ sheet: sheetKey, row: 2, col: 2 }, '=SUM(A2:B2)');
55+
56+
// 检查公式和值
57+
const formula_before = formulaManager.getCellFormula({ sheet: sheetKey, row: 2, col: 2 });
58+
const value_before = formulaManager.getCellValue({ sheet: sheetKey, row: 2, col: 2 });
59+
60+
expect(formula_before).toBe('=SUM(A2:B2)');
61+
expect(value_before.value).toBe(30); // 10+20=30
62+
63+
// 模拟删除B列(索引1)
64+
const result = formulaManager.formulaEngine.adjustFormulaReferences(sheetKey, 'delete', 'column', 1, 1, 3, 3);
65+
66+
// 检查公式和值 after deletion
67+
const formula_after = formulaManager.getCellFormula({ sheet: sheetKey, row: 2, col: 1 });
68+
const value_after = formulaManager.getCellValue({ sheet: sheetKey, row: 2, col: 1 });
69+
70+
console.log('Formula after column deletion:', formula_after);
71+
console.log('Value after column deletion:', value_after);
72+
73+
// 期望:公式应该变成 =SUM(A2),值应该是10
74+
expect(formula_after).toBe('=SUM(A2)');
75+
expect(value_after.value).toBe(10); // 现在只有A2的值
76+
});
77+
});
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// @ts-nocheck
2+
import { FormulaManager } from '../src/managers/formula-manager';
3+
import type VTableSheet from '../src/components/vtable-sheet';
4+
5+
// Mock VTableSheet for testing
6+
const mockVTableSheet = {
7+
getSheetManager: () => ({
8+
getSheet: (sheetKey: string) => ({
9+
sheetTitle: 'Test Sheet',
10+
sheetKey: sheetKey,
11+
showHeader: true,
12+
columnCount: 10,
13+
rowCount: 10,
14+
columns: [] as any[]
15+
})
16+
}),
17+
getActiveSheet: (): any => ({
18+
tableInstance: {
19+
changeCellValue: () => {
20+
/* Mock implementation */
21+
}
22+
}
23+
}),
24+
getSheet: (sheetKey: string) => ({
25+
columnCount: 10,
26+
rowCount: 10
27+
}),
28+
formulaManager: null // 这会在创建FormulaManager时自动设置
29+
} as unknown as VTableSheet;
30+
31+
describe('Column Logic Debug Test', () => {
32+
let formulaManager: FormulaManager;
33+
34+
beforeEach(() => {
35+
formulaManager = new FormulaManager(mockVTableSheet);
36+
mockVTableSheet.formulaManager = formulaManager;
37+
});
38+
39+
afterEach(() => {
40+
formulaManager.release();
41+
});
42+
43+
test('understand column deletion logic step by step', () => {
44+
const sheetKey = 'Sheet1';
45+
46+
// 添加包含数据的工作表
47+
formulaManager.addSheet(sheetKey, [
48+
['A', 'B', 'C'],
49+
['10', '20', '30'],
50+
['', '', '']
51+
]);
52+
53+
// 在C3中创建引用A2:B2的求和公式
54+
formulaManager.setCellContent({ sheet: sheetKey, row: 2, col: 2 }, '=SUM(A2:B2)');
55+
56+
// 检查原始状态
57+
console.log('Before deletion:');
58+
console.log('Formula:', formulaManager.getCellFormula({ sheet: sheetKey, row: 2, col: 2 }));
59+
console.log('Value:', formulaManager.getCellValue({ sheet: sheetKey, row: 2, col: 2 }));
60+
console.log('A2:', formulaManager.getCellValue({ sheet: sheetKey, row: 1, col: 0 }));
61+
console.log('B2:', formulaManager.getCellValue({ sheet: sheetKey, row: 1, col: 1 }));
62+
63+
// 模拟删除B列(索引1)
64+
const result = formulaManager.formulaEngine.adjustFormulaReferences(sheetKey, 'delete', 'column', 1, 1, 3, 3);
65+
66+
console.log('Deletion result:', result);
67+
68+
// 检查最终状态
69+
console.log('After deletion:');
70+
console.log('Formula:', formulaManager.getCellFormula({ sheet: sheetKey, row: 2, col: 1 }));
71+
console.log('Value:', formulaManager.getCellValue({ sheet: sheetKey, row: 2, col: 1 }));
72+
console.log('A2:', formulaManager.getCellValue({ sheet: sheetKey, row: 1, col: 0 }));
73+
74+
// 期望:公式应该变成 =SUM(A2),值应该是10
75+
expect(formulaManager.getCellFormula({ sheet: sheetKey, row: 2, col: 1 })).toBe('=SUM(A2)');
76+
expect(formulaManager.getCellValue({ sheet: sheetKey, row: 2, col: 1 }).value).toBe(10);
77+
});
78+
});

0 commit comments

Comments
 (0)