@visactor/vtable-sheet 1.22.7 → 1.22.8-alpha.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/cjs/components/sheet-tab-event-handler.js +3 -2
  2. package/cjs/components/sheet-tab-event-handler.js.map +1 -1
  3. package/cjs/components/vtable-sheet.d.ts +2 -1
  4. package/cjs/components/vtable-sheet.js +9 -1
  5. package/cjs/components/vtable-sheet.js.map +1 -1
  6. package/cjs/core/WorkSheet.d.ts +1 -1
  7. package/cjs/core/WorkSheet.js.map +1 -1
  8. package/cjs/core/table-plugins.js +7 -5
  9. package/cjs/core/table-plugins.js.map +1 -1
  10. package/cjs/formula/cell-highlight-manager.d.ts +1 -0
  11. package/cjs/formula/cell-highlight-manager.js +27 -22
  12. package/cjs/formula/cell-highlight-manager.js.map +1 -1
  13. package/cjs/formula/cross-sheet-data-synchronizer.d.ts +48 -0
  14. package/cjs/formula/cross-sheet-data-synchronizer.js +159 -0
  15. package/cjs/formula/cross-sheet-data-synchronizer.js.map +1 -0
  16. package/cjs/formula/cross-sheet-formula-handler.d.ts +58 -0
  17. package/cjs/formula/cross-sheet-formula-handler.js +240 -0
  18. package/cjs/formula/cross-sheet-formula-handler.js.map +1 -0
  19. package/cjs/formula/cross-sheet-formula-manager.d.ts +52 -0
  20. package/cjs/formula/cross-sheet-formula-manager.js +235 -0
  21. package/cjs/formula/cross-sheet-formula-manager.js.map +1 -0
  22. package/cjs/formula/cross-sheet-formula-validator.d.ts +56 -0
  23. package/cjs/formula/cross-sheet-formula-validator.js +271 -0
  24. package/cjs/formula/cross-sheet-formula-validator.js.map +1 -0
  25. package/cjs/formula/formula-engine.d.ts +4 -1
  26. package/cjs/formula/formula-engine.js +132 -25
  27. package/cjs/formula/formula-engine.js.map +1 -1
  28. package/cjs/formula/formula-paste-processor.d.ts +3 -3
  29. package/cjs/formula/formula-paste-processor.js.map +1 -1
  30. package/cjs/formula/formula-reference-adjustor.d.ts +1 -1
  31. package/cjs/formula/formula-reference-adjustor.js.map +1 -1
  32. package/cjs/formula/index.d.ts +8 -0
  33. package/cjs/formula/index.js +40 -3
  34. package/cjs/formula/index.js.map +1 -1
  35. package/cjs/index.d.ts +1 -1
  36. package/cjs/index.js +1 -1
  37. package/cjs/index.js.map +1 -1
  38. package/cjs/managers/formula-manager.d.ts +17 -2
  39. package/cjs/managers/formula-manager.js +150 -12
  40. package/cjs/managers/formula-manager.js.map +1 -1
  41. package/cjs/styles/menu.js +2 -1
  42. package/cjs/styles/sheet-tab.js +1 -2
  43. package/cjs/tools/index.js +2 -1
  44. package/dist/vtable-sheet.js +11322 -331
  45. package/dist/vtable-sheet.min.js +1 -1
  46. package/es/components/sheet-tab-event-handler.js +3 -2
  47. package/es/components/sheet-tab-event-handler.js.map +1 -1
  48. package/es/components/vtable-sheet.d.ts +2 -1
  49. package/es/components/vtable-sheet.js +9 -1
  50. package/es/components/vtable-sheet.js.map +1 -1
  51. package/es/core/WorkSheet.d.ts +1 -1
  52. package/es/core/WorkSheet.js.map +1 -1
  53. package/es/core/table-plugins.js +7 -5
  54. package/es/core/table-plugins.js.map +1 -1
  55. package/es/formula/cell-highlight-manager.d.ts +1 -0
  56. package/es/formula/cell-highlight-manager.js +27 -22
  57. package/es/formula/cell-highlight-manager.js.map +1 -1
  58. package/es/formula/cross-sheet-data-synchronizer.d.ts +48 -0
  59. package/es/formula/cross-sheet-data-synchronizer.js +151 -0
  60. package/es/formula/cross-sheet-data-synchronizer.js.map +1 -0
  61. package/es/formula/cross-sheet-formula-handler.d.ts +58 -0
  62. package/es/formula/cross-sheet-formula-handler.js +236 -0
  63. package/es/formula/cross-sheet-formula-handler.js.map +1 -0
  64. package/es/formula/cross-sheet-formula-manager.d.ts +52 -0
  65. package/es/formula/cross-sheet-formula-manager.js +227 -0
  66. package/es/formula/cross-sheet-formula-manager.js.map +1 -0
  67. package/es/formula/cross-sheet-formula-validator.d.ts +56 -0
  68. package/es/formula/cross-sheet-formula-validator.js +263 -0
  69. package/es/formula/cross-sheet-formula-validator.js.map +1 -0
  70. package/es/formula/formula-engine.d.ts +4 -1
  71. package/es/formula/formula-engine.js +132 -25
  72. package/es/formula/formula-engine.js.map +1 -1
  73. package/es/formula/formula-paste-processor.d.ts +3 -3
  74. package/es/formula/formula-paste-processor.js.map +1 -1
  75. package/es/formula/formula-reference-adjustor.d.ts +1 -1
  76. package/es/formula/formula-reference-adjustor.js.map +1 -1
  77. package/es/formula/index.d.ts +8 -0
  78. package/es/formula/index.js +8 -0
  79. package/es/formula/index.js.map +1 -1
  80. package/es/index.d.ts +1 -1
  81. package/es/index.js +1 -1
  82. package/es/index.js.map +1 -1
  83. package/es/managers/formula-manager.d.ts +17 -2
  84. package/es/managers/formula-manager.js +152 -11
  85. package/es/managers/formula-manager.js.map +1 -1
  86. package/es/styles/menu.js +2 -1
  87. package/es/styles/sheet-tab.js +1 -2
  88. package/es/tools/index.js +2 -1
  89. package/package.json +5 -5
@@ -0,0 +1,271 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: !0
5
+ }), exports.CrossSheetFormulaValidator = void 0;
6
+
7
+ class CrossSheetFormulaValidator {
8
+ constructor(formulaEngine, crossSheetManager, sheetManager) {
9
+ this.validationCache = new Map, this.CACHE_TTL = 5e3, this.defaultOptions = {
10
+ checkCircularReferences: !0,
11
+ checkMissingReferences: !0,
12
+ checkCellBounds: !0,
13
+ maxRecursionDepth: 100
14
+ }, this.formulaEngine = formulaEngine, this.crossSheetManager = crossSheetManager,
15
+ this.sheetManager = sheetManager;
16
+ }
17
+ validateSheet(sheet, options) {
18
+ const validationOptions = Object.assign(Object.assign({}, this.defaultOptions), options), cacheKey = this.getCacheKey(sheet, validationOptions), cached = this.validationCache.get(cacheKey);
19
+ if (cached && Date.now() - this.getCacheTimestamp(cacheKey) < this.CACHE_TTL) return cached;
20
+ const errors = [];
21
+ try {
22
+ const formulas = this.formulaEngine.exportFormulas(sheet);
23
+ for (const [cellRef, formula] of Object.entries(formulas)) {
24
+ const cell = this.parseA1Notation(cellRef), cellWithSheet = {
25
+ sheet: sheet,
26
+ row: cell.row,
27
+ col: cell.col
28
+ }, formulaErrors = this.validateFormula(formula, cellWithSheet, validationOptions);
29
+ errors.push(...formulaErrors);
30
+ }
31
+ if (validationOptions.checkCircularReferences) {
32
+ const circularErrors = this.checkCircularReferences(sheet);
33
+ errors.push(...circularErrors);
34
+ }
35
+ if (validationOptions.checkMissingReferences) {
36
+ const missingErrors = this.checkMissingReferences(sheet);
37
+ errors.push(...missingErrors);
38
+ }
39
+ } catch (error) {
40
+ errors.push({
41
+ type: "MISSING_REFERENCE",
42
+ message: `Validation failed: ${error instanceof Error ? error.message : "Unknown error"}`,
43
+ sheet: sheet,
44
+ cell: {
45
+ sheet: sheet,
46
+ row: 0,
47
+ col: 0
48
+ }
49
+ });
50
+ }
51
+ const result = {
52
+ valid: 0 === errors.length,
53
+ errors: errors,
54
+ warnings: []
55
+ };
56
+ return this.validationCache.set(cacheKey, result), result;
57
+ }
58
+ validateFormula(formula, cell, options) {
59
+ const errors = [];
60
+ if (!formula.startsWith("=")) return errors;
61
+ try {
62
+ const crossSheetRefs = this.extractCrossSheetReferences(formula);
63
+ for (const ref of crossSheetRefs) if (this.isValidSheet(ref.targetSheet)) {
64
+ if (options.checkCellBounds) {
65
+ const cellErrors = this.validateCellReferences(ref, cell);
66
+ errors.push(...cellErrors);
67
+ }
68
+ } else errors.push({
69
+ type: "INVALID_SHEET",
70
+ message: `Invalid sheet reference: ${ref.targetSheet}`,
71
+ sheet: cell.sheet,
72
+ cell: cell,
73
+ target: ref.targetSheet
74
+ });
75
+ } catch (error) {
76
+ errors.push({
77
+ type: "MISSING_REFERENCE",
78
+ message: `Formula validation failed: ${error instanceof Error ? error.message : "Unknown error"}`,
79
+ sheet: cell.sheet,
80
+ cell: cell
81
+ });
82
+ }
83
+ return errors;
84
+ }
85
+ extractCrossSheetReferences(formula) {
86
+ const references = [], sheetRefPattern = /'?([^'!!]+)'?[!!]([A-Z]+[0-9]+)(?:\s*:\s*(?:'?([^'!!]+)'?[!!])?([A-Z]+[0-9]+))?/g;
87
+ let match;
88
+ for (;null !== (match = sheetRefPattern.exec(formula)); ) {
89
+ const targetSheet = match[1], startRef = match[2], endSheetMaybe = match[3], endRef = match[4], targetCells = [];
90
+ if (endRef) {
91
+ if (endSheetMaybe && endSheetMaybe.toLowerCase() !== targetSheet.toLowerCase()) continue;
92
+ const startCell = this.parseA1Notation(startRef), endCell = this.parseA1Notation(endRef);
93
+ for (let row = Math.min(startCell.row, endCell.row); row <= Math.max(startCell.row, endCell.row); row++) for (let col = Math.min(startCell.col, endCell.col); col <= Math.max(startCell.col, endCell.col); col++) targetCells.push({
94
+ sheet: targetSheet,
95
+ row: row,
96
+ col: col
97
+ });
98
+ } else {
99
+ const cell = this.parseA1Notation(startRef);
100
+ targetCells.push({
101
+ sheet: targetSheet,
102
+ row: cell.row,
103
+ col: cell.col
104
+ });
105
+ }
106
+ references.push({
107
+ targetSheet: targetSheet,
108
+ targetCells: targetCells
109
+ });
110
+ }
111
+ return references;
112
+ }
113
+ validateCellReferences(ref, sourceCell) {
114
+ const errors = [];
115
+ for (const targetCell of ref.targetCells) try {
116
+ const cellValue = this.formulaEngine.getCellValue(targetCell);
117
+ if (cellValue.error) {
118
+ const targetRef = `${ref.targetSheet}!${this.getA1Notation(targetCell.row, targetCell.col)}`;
119
+ errors.push({
120
+ type: "INVALID_CELL",
121
+ message: `Invalid cell reference: ${targetRef} - ${cellValue.error}`,
122
+ sheet: sourceCell.sheet,
123
+ cell: sourceCell,
124
+ target: targetRef
125
+ });
126
+ }
127
+ } catch (error) {
128
+ errors.push({
129
+ type: "INVALID_CELL",
130
+ message: `Cell validation failed: ${ref.targetSheet}!${this.getA1Notation(targetCell.row, targetCell.col)}`,
131
+ sheet: sourceCell.sheet,
132
+ cell: sourceCell,
133
+ target: `${ref.targetSheet}!${this.getA1Notation(targetCell.row, targetCell.col)}`
134
+ });
135
+ }
136
+ return errors;
137
+ }
138
+ checkCircularReferences(sheet) {
139
+ const errors = [], visited = new Set, recursionStack = new Set, dfs = (currentSheet, path) => {
140
+ const key = currentSheet;
141
+ if (recursionStack.has(key)) {
142
+ const cycleStart = path.indexOf(currentSheet), cycle = path.slice(cycleStart).concat([ currentSheet ]);
143
+ return errors.push({
144
+ type: "CIRCULAR_REFERENCE",
145
+ message: `Circular reference detected: ${cycle.join(" -> ")}`,
146
+ sheet: currentSheet,
147
+ cell: {
148
+ sheet: currentSheet,
149
+ row: 0,
150
+ col: 0
151
+ }
152
+ }), !0;
153
+ }
154
+ if (visited.has(key)) return !1;
155
+ visited.add(key), recursionStack.add(key);
156
+ const dependencies = this.crossSheetManager.getCrossSheetDependencies(currentSheet);
157
+ for (const dep of dependencies) if (dfs(dep.precedentSheet, [ ...path, currentSheet ])) return !0;
158
+ return recursionStack.delete(key), !1;
159
+ };
160
+ return dfs(sheet, []), errors;
161
+ }
162
+ checkMissingReferences(sheet) {
163
+ const errors = [], allSheets = this.formulaEngine.getAllSheets(), existingSheets = new Set(allSheets.map((s => s.key))), formulas = this.formulaEngine.exportFormulas(sheet);
164
+ for (const [cellRef, formula] of Object.entries(formulas)) {
165
+ const cell = this.parseA1Notation(cellRef), cellWithSheet = {
166
+ sheet: sheet,
167
+ row: cell.row,
168
+ col: cell.col
169
+ }, crossSheetRefs = this.extractCrossSheetReferences(formula);
170
+ for (const ref of crossSheetRefs) existingSheets.has(ref.targetSheet) || errors.push({
171
+ type: "MISSING_REFERENCE",
172
+ message: `Missing sheet reference: ${ref.targetSheet}`,
173
+ sheet: sheet,
174
+ cell: cellWithSheet,
175
+ target: ref.targetSheet
176
+ });
177
+ }
178
+ return errors;
179
+ }
180
+ validateAllSheets(options) {
181
+ const results = new Map, allSheets = this.formulaEngine.getAllSheets();
182
+ for (const sheetInfo of allSheets) {
183
+ const result = this.validateSheet(sheetInfo.key, options);
184
+ results.set(sheetInfo.key, result);
185
+ }
186
+ return results;
187
+ }
188
+ validateFormulaSyntax(formula) {
189
+ try {
190
+ if (!formula.startsWith("=")) return {
191
+ valid: !0
192
+ };
193
+ const expression = formula.substring(1).trim();
194
+ this.validateExpressionStructure(expression);
195
+ const crossSheetPattern = /([A-Za-z0-9_一-龥]+)[!!]([A-Z]+[0-9]+(?::[A-Z]+[0-9]+)?)/g;
196
+ let match;
197
+ for (;null !== (match = crossSheetPattern.exec(expression)); ) {
198
+ const sheetName = match[1], cellRef = match[2];
199
+ if (!this.isValidSheetName(sheetName)) return {
200
+ valid: !1,
201
+ error: `Invalid sheet name: ${sheetName}`
202
+ };
203
+ if (!this.isValidSheet(sheetName)) return {
204
+ valid: !1,
205
+ error: `Invalid sheet name: ${sheetName}`
206
+ };
207
+ if (!this.isValidCellReference(cellRef)) return {
208
+ valid: !1,
209
+ error: `Invalid cell reference: ${cellRef}`
210
+ };
211
+ }
212
+ return {
213
+ valid: !0
214
+ };
215
+ } catch (error) {
216
+ return {
217
+ valid: !1,
218
+ error: error instanceof Error ? error.message : "Syntax validation failed"
219
+ };
220
+ }
221
+ }
222
+ validateExpressionStructure(expression) {
223
+ if ((expression.match(/\(/g) || []).length !== (expression.match(/\)/g) || []).length) throw new Error("Unmatched parentheses");
224
+ if ((expression.match(/"/g) || []).length % 2 != 0) throw new Error("Unmatched quotes");
225
+ }
226
+ isValidSheetName(sheetName) {
227
+ return /^[A-Za-z_一-龥][A-Za-z0-9_一-龥]*$/.test(sheetName);
228
+ }
229
+ isValidCellReference(cellRef) {
230
+ return /^[A-Z]+[0-9]+$/.test(cellRef) || /^[A-Z]+[0-9]+:[A-Z]+[0-9]+$/.test(cellRef);
231
+ }
232
+ isValidSheet(sheetName) {
233
+ if (this.sheetManager) {
234
+ return this.sheetManager.getAllSheets().some((sheet => sheet.sheetTitle.toLowerCase() === sheetName.toLowerCase()));
235
+ }
236
+ return this.formulaEngine.getAllSheets().some((sheet => sheet.title.toLowerCase() === sheetName.toLowerCase()));
237
+ }
238
+ parseA1Notation(a1Notation) {
239
+ const match = a1Notation.match(/^([A-Z]+)([0-9]+)$/);
240
+ if (!match) throw new Error(`Invalid cell reference: ${a1Notation}`);
241
+ const colLetters = match[1], rowNumber = parseInt(match[2], 10);
242
+ let col = 0;
243
+ for (let i = 0; i < colLetters.length; i++) col = 26 * col + (colLetters.charCodeAt(i) - 65);
244
+ return {
245
+ row: rowNumber - 1,
246
+ col: col
247
+ };
248
+ }
249
+ getA1Notation(row, col) {
250
+ let colStr = "", tempCol = col;
251
+ do {
252
+ colStr = String.fromCharCode(65 + tempCol % 26) + colStr, tempCol = Math.floor(tempCol / 26) - 1;
253
+ } while (tempCol >= 0);
254
+ return `${colStr}${row + 1}`;
255
+ }
256
+ getCacheKey(sheet, options) {
257
+ return `${sheet}_${JSON.stringify(options)}`;
258
+ }
259
+ getCacheTimestamp(_cacheKey) {
260
+ return Date.now();
261
+ }
262
+ clearCache() {
263
+ this.validationCache.clear();
264
+ }
265
+ destroy() {
266
+ this.clearCache();
267
+ }
268
+ }
269
+
270
+ exports.CrossSheetFormulaValidator = CrossSheetFormulaValidator;
271
+ //# sourceMappingURL=cross-sheet-formula-validator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/formula/cross-sheet-formula-validator.ts"],"names":[],"mappings":"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","file":"cross-sheet-formula-validator.js","sourcesContent":["/**\n * 跨sheet公式验证器\n * 验证跨sheet引用的有效性和完整性\n */\n\nimport type { FormulaCell } from '../ts-types/formula';\nimport type { FormulaEngine } from './formula-engine';\nimport type { CrossSheetFormulaManager } from './cross-sheet-formula-manager';\n\nexport interface ValidationError {\n type: 'INVALID_SHEET' | 'INVALID_CELL' | 'CIRCULAR_REFERENCE' | 'MISSING_REFERENCE';\n message: string;\n sheet: string;\n cell: FormulaCell;\n target?: string;\n}\n\nexport interface ValidationResult {\n valid: boolean;\n errors: ValidationError[];\n warnings: string[];\n}\n\nexport interface ValidationOptions {\n checkCircularReferences: boolean;\n checkMissingReferences: boolean;\n checkCellBounds: boolean;\n maxRecursionDepth: number;\n}\n\nexport class CrossSheetFormulaValidator {\n private formulaEngine: FormulaEngine;\n private crossSheetManager: CrossSheetFormulaManager;\n private sheetManager: { getAllSheets: () => Array<{ sheetKey: string; sheetTitle: string }> } | undefined; // SheetManager reference\n\n private validationCache: Map<string, ValidationResult> = new Map();\n private readonly CACHE_TTL = 5000; // 5秒缓存\n\n private defaultOptions: ValidationOptions = {\n checkCircularReferences: true,\n checkMissingReferences: true,\n checkCellBounds: true,\n maxRecursionDepth: 100\n };\n\n constructor(\n formulaEngine: FormulaEngine,\n crossSheetManager: CrossSheetFormulaManager,\n sheetManager?: { getAllSheets: () => Array<{ sheetKey: string; sheetTitle: string }> }\n ) {\n this.formulaEngine = formulaEngine;\n this.crossSheetManager = crossSheetManager;\n this.sheetManager = sheetManager;\n }\n\n /**\n * 验证指定sheet的所有跨sheet公式\n */\n validateSheet(sheet: string, options?: Partial<ValidationOptions>): ValidationResult {\n const validationOptions = { ...this.defaultOptions, ...options };\n const cacheKey = this.getCacheKey(sheet, validationOptions);\n\n // 检查缓存\n const cached = this.validationCache.get(cacheKey);\n if (cached && Date.now() - this.getCacheTimestamp(cacheKey) < this.CACHE_TTL) {\n return cached;\n }\n\n const errors: ValidationError[] = [];\n const warnings: string[] = [];\n\n try {\n // 获取sheet的所有公式\n const formulas = this.formulaEngine.exportFormulas(sheet);\n\n // 验证每个公式\n for (const [cellRef, formula] of Object.entries(formulas)) {\n const cell = this.parseA1Notation(cellRef);\n const cellWithSheet: FormulaCell = { sheet, row: cell.row, col: cell.col };\n\n const formulaErrors = this.validateFormula(formula, cellWithSheet, validationOptions);\n errors.push(...formulaErrors);\n }\n\n // 检查循环依赖\n if (validationOptions.checkCircularReferences) {\n const circularErrors = this.checkCircularReferences(sheet);\n errors.push(...circularErrors);\n }\n\n // 检查缺失的引用\n if (validationOptions.checkMissingReferences) {\n const missingErrors = this.checkMissingReferences(sheet);\n errors.push(...missingErrors);\n }\n } catch (error) {\n errors.push({\n type: 'MISSING_REFERENCE',\n message: `Validation failed: ${error instanceof Error ? error.message : 'Unknown error'}`,\n sheet,\n cell: { sheet, row: 0, col: 0 }\n });\n }\n\n const result: ValidationResult = {\n valid: errors.length === 0,\n errors,\n warnings\n };\n\n // 缓存结果\n this.validationCache.set(cacheKey, result);\n\n return result;\n }\n\n /**\n * 验证单个公式\n */\n private validateFormula(formula: string, cell: FormulaCell, options: ValidationOptions): ValidationError[] {\n const errors: ValidationError[] = [];\n\n if (!formula.startsWith('=')) {\n return errors;\n }\n\n try {\n // 提取跨sheet引用\n const crossSheetRefs = this.extractCrossSheetReferences(formula);\n\n for (const ref of crossSheetRefs) {\n // 验证sheet是否存在\n if (!this.isValidSheet(ref.targetSheet)) {\n errors.push({\n type: 'INVALID_SHEET',\n message: `Invalid sheet reference: ${ref.targetSheet}`,\n sheet: cell.sheet,\n cell,\n target: ref.targetSheet\n });\n continue;\n }\n\n // 验证单元格是否存在\n if (options.checkCellBounds) {\n const cellErrors = this.validateCellReferences(ref, cell);\n errors.push(...cellErrors);\n }\n }\n } catch (error) {\n errors.push({\n type: 'MISSING_REFERENCE',\n message: `Formula validation failed: ${error instanceof Error ? error.message : 'Unknown error'}`,\n sheet: cell.sheet,\n cell\n });\n }\n\n return errors;\n }\n\n /**\n * 提取跨sheet引用\n */\n private extractCrossSheetReferences(formula: string): Array<{\n targetSheet: string;\n targetCells: FormulaCell[];\n }> {\n const references: Array<{\n targetSheet: string;\n targetCells: FormulaCell[];\n }> = [];\n\n // 匹配带sheet前缀的引用,如:\n // - Sheet1!A1\n // - Sheet1!A1:B2\n // - Sheet1!A1:Sheet1!B2\n // - Sheet1!A1:Sheet1!B2\n // - 'My Sheet'!A1:'My Sheet'!B2\n const sheetRefPattern = /'?([^'!!]+)'?[!!]([A-Z]+[0-9]+)(?:\\s*:\\s*(?:'?([^'!!]+)'?[!!])?([A-Z]+[0-9]+))?/g;\n let match: RegExpExecArray | null;\n\n while ((match = sheetRefPattern.exec(formula)) !== null) {\n const targetSheet = match[1];\n const startRef = match[2];\n const endSheetMaybe = match[3];\n const endRef = match[4];\n\n const targetCells: FormulaCell[] = [];\n\n if (endRef) {\n // 范围引用\n // 若右侧带了sheet前缀,仅支持与左侧相同(否则为 3D 引用,当前不展开)\n if (endSheetMaybe && endSheetMaybe.toLowerCase() !== targetSheet.toLowerCase()) {\n // 3D 引用:Sheet1!A1:Sheet2!B2(当前不支持)\n continue;\n }\n const startCell = this.parseA1Notation(startRef);\n const endCell = this.parseA1Notation(endRef);\n\n for (let row = Math.min(startCell.row, endCell.row); row <= Math.max(startCell.row, endCell.row); row++) {\n for (let col = Math.min(startCell.col, endCell.col); col <= Math.max(startCell.col, endCell.col); col++) {\n targetCells.push({ sheet: targetSheet, row, col });\n }\n }\n } else {\n // 单个单元格引用,如 A1\n const cell = this.parseA1Notation(startRef);\n targetCells.push({ sheet: targetSheet, row: cell.row, col: cell.col });\n }\n\n references.push({\n targetSheet,\n targetCells\n });\n }\n\n return references;\n }\n\n /**\n * 验证单元格引用\n */\n private validateCellReferences(\n ref: {\n targetSheet: string;\n targetCells: FormulaCell[];\n },\n sourceCell: FormulaCell\n ): ValidationError[] {\n const errors: ValidationError[] = [];\n\n for (const targetCell of ref.targetCells) {\n try {\n // 检查单元格是否有效\n const cellValue = this.formulaEngine.getCellValue(targetCell);\n\n if (cellValue.error) {\n const targetRef = `${ref.targetSheet}!${this.getA1Notation(targetCell.row, targetCell.col)}`;\n errors.push({\n type: 'INVALID_CELL',\n message: `Invalid cell reference: ${targetRef} - ${cellValue.error}`,\n sheet: sourceCell.sheet,\n cell: sourceCell,\n target: targetRef\n });\n }\n } catch (error) {\n errors.push({\n type: 'INVALID_CELL',\n message: `Cell validation failed: ${ref.targetSheet}!${this.getA1Notation(targetCell.row, targetCell.col)}`,\n sheet: sourceCell.sheet,\n cell: sourceCell,\n target: `${ref.targetSheet}!${this.getA1Notation(targetCell.row, targetCell.col)}`\n });\n }\n }\n\n return errors;\n }\n\n /**\n * 检查循环依赖\n */\n private checkCircularReferences(sheet: string): ValidationError[] {\n const errors: ValidationError[] = [];\n const visited = new Set<string>();\n const recursionStack = new Set<string>();\n\n const dfs = (currentSheet: string, path: string[]): boolean => {\n const key = currentSheet;\n\n if (recursionStack.has(key)) {\n // 发现循环依赖\n const cycleStart = path.indexOf(currentSheet);\n const cycle = path.slice(cycleStart).concat([currentSheet]);\n\n errors.push({\n type: 'CIRCULAR_REFERENCE',\n message: `Circular reference detected: ${cycle.join(' -> ')}`,\n sheet: currentSheet,\n cell: { sheet: currentSheet, row: 0, col: 0 }\n });\n\n return true;\n }\n\n if (visited.has(key)) {\n return false;\n }\n\n visited.add(key);\n recursionStack.add(key);\n\n const dependencies = this.crossSheetManager.getCrossSheetDependencies(currentSheet);\n\n for (const dep of dependencies) {\n if (dfs(dep.precedentSheet, [...path, currentSheet])) {\n return true;\n }\n }\n\n recursionStack.delete(key);\n return false;\n };\n\n dfs(sheet, []);\n return errors;\n }\n\n /**\n * 检查缺失的引用\n */\n private checkMissingReferences(sheet: string): ValidationError[] {\n const errors: ValidationError[] = [];\n const allSheets = this.formulaEngine.getAllSheets();\n const existingSheets = new Set(allSheets.map(s => s.key));\n\n const formulas = this.formulaEngine.exportFormulas(sheet);\n\n for (const [cellRef, formula] of Object.entries(formulas)) {\n const cell = this.parseA1Notation(cellRef);\n const cellWithSheet: FormulaCell = { sheet, row: cell.row, col: cell.col };\n\n const crossSheetRefs = this.extractCrossSheetReferences(formula);\n\n for (const ref of crossSheetRefs) {\n if (!existingSheets.has(ref.targetSheet)) {\n errors.push({\n type: 'MISSING_REFERENCE',\n message: `Missing sheet reference: ${ref.targetSheet}`,\n sheet,\n cell: cellWithSheet,\n target: ref.targetSheet\n });\n }\n }\n }\n\n return errors;\n }\n\n /**\n * 验证所有sheet的跨sheet公式\n */\n validateAllSheets(options?: Partial<ValidationOptions>): Map<string, ValidationResult> {\n const results = new Map<string, ValidationResult>();\n const allSheets = this.formulaEngine.getAllSheets();\n\n for (const sheetInfo of allSheets) {\n const result = this.validateSheet(sheetInfo.key, options);\n results.set(sheetInfo.key, result);\n }\n\n return results;\n }\n\n /**\n * 验证公式语法(不执行计算)\n */\n validateFormulaSyntax(formula: string): { valid: boolean; error?: string } {\n try {\n if (!formula.startsWith('=')) {\n return { valid: true };\n }\n\n const expression = formula.substring(1).trim();\n\n // 基本语法检查\n this.validateExpressionStructure(expression);\n\n // 检查跨sheet引用格式 - 支持中英文、数字、下划线,以及中英文感叹号\n const crossSheetPattern = /([A-Za-z0-9_一-龥]+)[!!]([A-Z]+[0-9]+(?::[A-Z]+[0-9]+)?)/g;\n let match;\n\n while ((match = crossSheetPattern.exec(expression)) !== null) {\n const sheetName = match[1];\n const cellRef = match[2];\n\n // 验证sheet名称格式\n if (!this.isValidSheetName(sheetName)) {\n return { valid: false, error: `Invalid sheet name: ${sheetName}` };\n }\n\n // 验证sheet是否存在\n if (!this.isValidSheet(sheetName)) {\n return { valid: false, error: `Invalid sheet name: ${sheetName}` };\n }\n\n // 验证单元格引用格式\n if (!this.isValidCellReference(cellRef)) {\n return { valid: false, error: `Invalid cell reference: ${cellRef}` };\n }\n }\n\n return { valid: true };\n } catch (error) {\n return {\n valid: false,\n error: error instanceof Error ? error.message : 'Syntax validation failed'\n };\n }\n }\n\n /**\n * 验证表达式结构\n */\n private validateExpressionStructure(expression: string): void {\n // 检查括号匹配\n const openParenCount = (expression.match(/\\(/g) || []).length;\n const closeParenCount = (expression.match(/\\)/g) || []).length;\n\n if (openParenCount !== closeParenCount) {\n throw new Error('Unmatched parentheses');\n }\n\n // 检查引号匹配\n const doubleQuoteCount = (expression.match(/\"/g) || []).length;\n if (doubleQuoteCount % 2 !== 0) {\n throw new Error('Unmatched quotes');\n }\n }\n\n /**\n * 验证sheet名称\n */\n private isValidSheetName(sheetName: string): boolean {\n // Sheet名称规则:支持中英文、数字、下划线,不能以数字开头\n const validPattern = /^[A-Za-z_一-龥][A-Za-z0-9_一-龥]*$/;\n return validPattern.test(sheetName);\n }\n\n /**\n * 验证单元格引用格式\n */\n private isValidCellReference(cellRef: string): boolean {\n // 支持单个单元格如 A1 或范围如 A1:B2\n const singleCellPattern = /^[A-Z]+[0-9]+$/;\n const rangePattern = /^[A-Z]+[0-9]+:[A-Z]+[0-9]+$/;\n\n return singleCellPattern.test(cellRef) || rangePattern.test(cellRef);\n }\n\n /**\n * 验证sheet是否存在(使用sheetTitle而不是sheetKey)\n * 检查所有存在的sheet,包括未激活的sheet\n */\n private isValidSheet(sheetName: string): boolean {\n // 优先使用sheetManager检查所有存在的sheet(包括未激活的)\n if (this.sheetManager) {\n const allSheets = this.sheetManager.getAllSheets();\n return allSheets.some(sheet => sheet.sheetTitle.toLowerCase() === sheetName.toLowerCase());\n }\n\n // 回退到formulaEngine中已注册的sheet\n const allSheets = this.formulaEngine.getAllSheets();\n return allSheets.some(sheet => sheet.title.toLowerCase() === sheetName.toLowerCase());\n }\n\n /**\n * 工具方法:A1表示法解析\n */\n private parseA1Notation(a1Notation: string): { row: number; col: number } {\n const match = a1Notation.match(/^([A-Z]+)([0-9]+)$/);\n if (!match) {\n throw new Error(`Invalid cell reference: ${a1Notation}`);\n }\n\n const colLetters = match[1];\n const rowNumber = parseInt(match[2], 10);\n\n let col = 0;\n for (let i = 0; i < colLetters.length; i++) {\n col = col * 26 + (colLetters.charCodeAt(i) - 65);\n }\n\n return { row: rowNumber - 1, col };\n }\n\n /**\n * 工具方法:A1表示法生成\n */\n private getA1Notation(row: number, col: number): string {\n let colStr = '';\n let tempCol = col;\n\n do {\n colStr = String.fromCharCode(65 + (tempCol % 26)) + colStr;\n tempCol = Math.floor(tempCol / 26) - 1;\n } while (tempCol >= 0);\n\n return `${colStr}${row + 1}`;\n }\n\n /**\n * 获取缓存键\n */\n private getCacheKey(sheet: string, options: ValidationOptions): string {\n return `${sheet}_${JSON.stringify(options)}`;\n }\n\n /**\n * 获取缓存时间戳\n */\n private getCacheTimestamp(_cacheKey: string): number {\n // 简单的实现,实际应该存储时间戳\n return Date.now();\n }\n\n /**\n * 清除验证缓存\n */\n clearCache(): void {\n this.validationCache.clear();\n }\n\n /**\n * 销毁验证器\n */\n destroy(): void {\n this.clearCache();\n }\n}\n"]}
@@ -14,6 +14,7 @@ export interface FormulaEngineConfig {
14
14
  export declare class FormulaEngine {
15
15
  private sheets;
16
16
  private reverseSheets;
17
+ private sheetTitles;
17
18
  private sheetData;
18
19
  private formulaCells;
19
20
  private dependencies;
@@ -24,6 +25,7 @@ export declare class FormulaEngine {
24
25
  setActiveSheet(sheetKey: string): void;
25
26
  getActiveSheet(): string | null;
26
27
  addSheet(sheetKey: string, data?: unknown[][]): number;
28
+ setSheetTitle(sheetKey: string, sheetTitle: string): void;
27
29
  updateSheetData(sheetKey: string, data: unknown[][]): void;
28
30
  private normalizeData;
29
31
  getSheetId(sheetKey: string): number;
@@ -87,10 +89,11 @@ export declare class FormulaEngine {
87
89
  private validateExpressionStructure;
88
90
  release(): void;
89
91
  exportFormulas(sheetKey: string): Record<string, string>;
92
+ getSheetTitle(sheetKey: string): string | undefined;
90
93
  getAllSheets(): Array<{
91
94
  key: string;
92
95
  id: number;
93
- name: string;
96
+ title: string;
94
97
  }>;
95
98
  sortFormulasByDependency(sheetKey: string, formulas: Record<string, string>): [string, string][];
96
99
  removeSheet(sheetKey: string): void;
@@ -8,8 +8,9 @@ const formula_helper_1 = require("./formula-helper");
8
8
 
9
9
  class FormulaEngine {
10
10
  constructor(_config = {}) {
11
- this.sheets = new Map, this.reverseSheets = new Map, this.sheetData = new Map, this.formulaCells = new Map,
12
- this.dependencies = new Map, this.dependents = new Map, this.nextSheetId = 0, this.activeSheetKey = null;
11
+ this.sheets = new Map, this.reverseSheets = new Map, this.sheetTitles = new Map,
12
+ this.sheetData = new Map, this.formulaCells = new Map, this.dependencies = new Map,
13
+ this.dependents = new Map, this.nextSheetId = 0, this.activeSheetKey = null;
13
14
  }
14
15
  setActiveSheet(sheetKey) {
15
16
  this.sheets.has(sheetKey) && (this.activeSheetKey = sheetKey);
@@ -24,6 +25,9 @@ class FormulaEngine {
24
25
  const sheetData = data || [ [ "" ] ];
25
26
  return this.sheetData.set(sheetId, sheetData), sheetId;
26
27
  }
28
+ setSheetTitle(sheetKey, sheetTitle) {
29
+ this.sheets.has(sheetKey) && this.sheetTitles.set(sheetKey, sheetTitle);
30
+ }
27
31
  updateSheetData(sheetKey, data) {
28
32
  const sheetId = this.sheets.get(sheetKey);
29
33
  null != sheetId && this.sheetData.set(sheetId, data);
@@ -120,7 +124,11 @@ class FormulaEngine {
120
124
  };
121
125
  }
122
126
  parseCellKey(cellKey) {
123
- const parts = cellKey.split("!");
127
+ let parts;
128
+ if (cellKey.includes("!")) parts = cellKey.split("!"); else {
129
+ if (!cellKey.includes("!")) return null;
130
+ parts = cellKey.split("!");
131
+ }
124
132
  if (2 !== parts.length) return null;
125
133
  const [sheet, a1Notation] = parts;
126
134
  try {
@@ -159,7 +167,26 @@ class FormulaEngine {
159
167
  const expression = formula.substring(1);
160
168
  let inQuotes = !1, quoteChar = "", i = 0;
161
169
  for (;i < expression.length; ) {
162
- const char = expression[i];
170
+ const char = expression[i], quotedSheetCellMatchCN = expression.substring(i).match(/^'([A-Za-z0-9_\s一-龥]+)'\s*!\s*([A-Za-z]+[0-9]+)/);
171
+ if (quotedSheetCellMatchCN) {
172
+ const fullRef = quotedSheetCellMatchCN[0], sheetNameMatch = fullRef.match(/^'([^']+)'\s*!\s*(.+)$/);
173
+ if (sheetNameMatch) {
174
+ const originalSheetName = sheetNameMatch[1], cellRef = sheetNameMatch[2], correctedSheetName = this.findOriginalSheetName(originalSheetName), letters = cellRef.replace(/[0-9]/g, ""), numbers = cellRef.replace(/[A-Za-z]/g, "");
175
+ corrected += "'" + (correctedSheetName || originalSheetName) + "'!" + letters.toUpperCase() + numbers,
176
+ i += fullRef.length;
177
+ continue;
178
+ }
179
+ }
180
+ const quotedSheetCellMatch = expression.substring(i).match(/^'([A-Za-z0-9_\s一-龥]+)'![A-Za-z]+[0-9]+/);
181
+ if (quotedSheetCellMatch) {
182
+ const fullRef = quotedSheetCellMatch[0], sheetNameMatch = fullRef.match(/^'([^']+)'!(.+)$/);
183
+ if (sheetNameMatch) {
184
+ const originalSheetName = sheetNameMatch[1], cellRef = sheetNameMatch[2], correctedSheetName = this.findOriginalSheetName(originalSheetName), letters = cellRef.replace(/[0-9]/g, ""), numbers = cellRef.replace(/[A-Za-z]/g, "");
185
+ corrected += "'" + (correctedSheetName || originalSheetName) + "'!" + letters.toUpperCase() + numbers,
186
+ i += fullRef.length;
187
+ continue;
188
+ }
189
+ }
163
190
  if (!('"' !== char && "'" !== char || 0 !== i && "\\" === expression[i - 1])) {
164
191
  inQuotes ? char === quoteChar && (inQuotes = !1, quoteChar = "") : (inQuotes = !0,
165
192
  quoteChar = char), corrected += char, i++;
@@ -175,13 +202,37 @@ class FormulaEngine {
175
202
  for (corrected += funcName.toUpperCase() + "(", i += funcName.length + 1; i < expression.length && " " === expression[i]; ) i++;
176
203
  continue;
177
204
  }
178
- const sheetCellMatch = expression.substring(i).match(/^[A-Za-z0-9_]+![A-Za-z]+[0-9]+/);
205
+ const sheetCellMatchCN = expression.substring(i).match(/^[A-Za-z0-9_\s一-龥]+!([A-Za-z]+[0-9]+)/);
206
+ if (sheetCellMatchCN) {
207
+ const fullRef = sheetCellMatchCN[0], [sheetName, cellRef] = fullRef.split("!"), originalSheetName = this.findOriginalSheetName(sheetName), letters = cellRef.replace(/[0-9]/g, ""), numbers = cellRef.replace(/[A-Za-z]/g, "");
208
+ corrected += (originalSheetName || sheetName) + "!" + letters.toUpperCase() + numbers,
209
+ i += fullRef.length;
210
+ continue;
211
+ }
212
+ const sheetCellMatch = expression.substring(i).match(/^[A-Za-z0-9_\s一-龥]+![A-Za-z]+[0-9]+/);
179
213
  if (sheetCellMatch) {
180
214
  const fullRef = sheetCellMatch[0], [sheetName, cellRef] = fullRef.split("!"), originalSheetName = this.findOriginalSheetName(sheetName), letters = cellRef.replace(/[0-9]/g, ""), numbers = cellRef.replace(/[A-Za-z]/g, "");
181
215
  corrected += (originalSheetName || sheetName) + "!" + letters.toUpperCase() + numbers,
182
216
  i += fullRef.length;
183
217
  continue;
184
218
  }
219
+ const quotedSheetRangeMatchCN = expression.substring(i).match(/^'([A-Za-z0-9_\s一-龥]+)'![A-Za-z]+[0-9]+:[A-Za-z]+[0-9]+/);
220
+ if (quotedSheetRangeMatchCN) {
221
+ const fullRangeRef = quotedSheetRangeMatchCN[0], sheetNameMatch = fullRangeRef.match(/^'([^']+)'!(.+)$/);
222
+ if (sheetNameMatch) {
223
+ const originalSheetName = sheetNameMatch[1], rangePart = sheetNameMatch[2], [startCell, endCell] = rangePart.split(":"), correctedSheetName = this.findOriginalSheetName(originalSheetName), startLetters = startCell.replace(/[0-9]/g, ""), startNumbers = startCell.replace(/[A-Za-z]/g, ""), newStartCell = startLetters.toUpperCase() + startNumbers, endLetters = endCell.replace(/[0-9]/g, ""), endNumbers = endCell.replace(/[A-Za-z]/g, "");
224
+ corrected += "'" + (correctedSheetName || originalSheetName) + "'!" + newStartCell + ":" + (endLetters.toUpperCase() + endNumbers),
225
+ i += fullRangeRef.length;
226
+ continue;
227
+ }
228
+ }
229
+ const sheetRangeMatchCN = expression.substring(i).match(/^[A-Za-z0-9_\s一-龥]+![A-Za-z]+[0-9]+:[A-Za-z]+[0-9]+/);
230
+ if (sheetRangeMatchCN) {
231
+ const fullRangeRef = sheetRangeMatchCN[0], [sheetPart, rangePart] = fullRangeRef.split("!"), [startCell, endCell] = rangePart.split(":"), originalSheetName = this.findOriginalSheetName(sheetPart), startLetters = startCell.replace(/[0-9]/g, ""), startNumbers = startCell.replace(/[A-Za-z]/g, ""), newStartCell = startLetters.toUpperCase() + startNumbers, endLetters = endCell.replace(/[0-9]/g, ""), endNumbers = endCell.replace(/[A-Za-z]/g, "");
232
+ corrected += (originalSheetName || sheetPart) + "!" + newStartCell + ":" + (endLetters.toUpperCase() + endNumbers),
233
+ i += fullRangeRef.length;
234
+ continue;
235
+ }
185
236
  const sheetRangeMatch = expression.substring(i).match(/^[A-Za-z0-9_]+![A-Za-z]+[0-9]+:[A-Za-z]+[0-9]+/);
186
237
  if (sheetRangeMatch) {
187
238
  const fullRangeRef = sheetRangeMatch[0], [sheetPart, rangePart] = fullRangeRef.split("!"), [startCell, endCell] = rangePart.split(":"), originalSheetName = this.findOriginalSheetName(sheetPart), startLetters = startCell.replace(/[0-9]/g, ""), startNumbers = startCell.replace(/[A-Za-z]/g, ""), newStartCell = startLetters.toUpperCase() + startNumbers, endLetters = endCell.replace(/[0-9]/g, ""), endNumbers = endCell.replace(/[A-Za-z]/g, "");
@@ -204,8 +255,10 @@ class FormulaEngine {
204
255
  return corrected;
205
256
  }
206
257
  findOriginalSheetName(sheetName) {
207
- if (this.sheets.has(sheetName)) return sheetName;
258
+ for (const sheetTitle of this.sheetTitles.values()) if (sheetTitle === sheetName) return sheetTitle;
208
259
  const lowerSheetName = sheetName.toLowerCase();
260
+ for (const sheetTitle of this.sheetTitles.values()) if (sheetTitle.toLowerCase() === lowerSheetName) return sheetTitle;
261
+ if (this.sheets.has(sheetName)) return sheetName;
209
262
  for (const [existingSheetName] of this.sheets.entries()) if (existingSheetName.toLowerCase() === lowerSheetName) return existingSheetName;
210
263
  return null;
211
264
  }
@@ -296,7 +349,7 @@ class FormulaEngine {
296
349
  value: expr.slice(1, -1),
297
350
  error: void 0
298
351
  };
299
- if (/^([A-Za-z0-9_]+!)?[A-Z]+[0-9]+$/.test(expr)) return {
352
+ if (/^([A-Za-z0-9_\s一-龥]+!)?[A-Z]+[0-9]+$/.test(expr)) return {
300
353
  value: this.getCellValueByA1(expr),
301
354
  error: void 0
302
355
  };
@@ -304,7 +357,7 @@ class FormulaEngine {
304
357
  value: Number(expr),
305
358
  error: void 0
306
359
  };
307
- if (/^([A-Za-z0-9_]+!)?[A-Z]+[0-9]+:[A-Z]+[0-9]+$/.test(expr)) {
360
+ if (/^([A-Za-z0-9_\s一-龥]+!)?[A-Z]+[0-9]+:[A-Z]+[0-9]+$/.test(expr)) {
308
361
  return {
309
362
  value: this.getRangeValuesFromExpr(expr),
310
363
  error: void 0
@@ -729,7 +782,7 @@ class FormulaEngine {
729
782
  };
730
783
  functionValues.push(funcResult.value), processedExpr = processedExpr.replace(funcMatch.match, `__FUNC_${functionValues.length - 1}__`);
731
784
  }
732
- const cellRefs = processedExpr.match(/[A-Z]+[0-9]+/g) || [];
785
+ const cellRefs = processedExpr.match(/('[^']+'!)?([A-Za-z0-9_\s一-龥]+!)?[A-Z]+[0-9]+/g) || [];
733
786
  for (const cellRef of cellRefs) {
734
787
  const value = this.getCellValueByA1(cellRef);
735
788
  processedExpr = processedExpr.replace(cellRef, String(value));
@@ -747,11 +800,21 @@ class FormulaEngine {
747
800
  }
748
801
  }
749
802
  getCellValueByA1(a1Notation) {
803
+ var _a, _b;
750
804
  try {
751
805
  let sheetKey = this.activeSheetKey || this.reverseSheets.get(0) || "Sheet1", cellRef = a1Notation;
752
- if (a1Notation.includes("!")) {
753
- const parts = a1Notation.split("!");
754
- 2 === parts.length && (sheetKey = parts[0], cellRef = parts[1]);
806
+ if (a1Notation.includes("!") || a1Notation.includes("!")) {
807
+ let parts;
808
+ if (parts = a1Notation.includes("!") ? a1Notation.split("!") : a1Notation.split("!"),
809
+ 2 === parts.length) {
810
+ let sheetTitle = parts[0];
811
+ sheetTitle.startsWith("'") && sheetTitle.endsWith("'") && (sheetTitle = sheetTitle.slice(1, -1));
812
+ let foundSheetKey = null === (_a = Array.from(this.sheetTitles.entries()).find((([_, value]) => value.toLowerCase() === sheetTitle.toLowerCase()))) || void 0 === _a ? void 0 : _a[0];
813
+ foundSheetKey || (foundSheetKey = null === (_b = Array.from(this.sheets.entries()).find((([key, _]) => key.toLowerCase() === sheetTitle.toLowerCase()))) || void 0 === _b ? void 0 : _b[0]),
814
+ !foundSheetKey && this.sheets && this.sheets.has(sheetTitle) && (foundSheetKey = sheetTitle),
815
+ foundSheetKey ? (sheetKey = foundSheetKey, cellRef = parts[1]) : (sheetKey = sheetTitle,
816
+ cellRef = parts[1]);
817
+ }
755
818
  }
756
819
  const {row: row, col: col} = this.parseA1Notation(cellRef), cell = {
757
820
  sheet: sheetKey,
@@ -759,19 +822,38 @@ class FormulaEngine {
759
822
  col: col
760
823
  };
761
824
  return this.getCellValue(cell).value;
762
- } catch (_a) {
825
+ } catch (_c) {
763
826
  return 0;
764
827
  }
765
828
  }
766
829
  getRangeValuesFromExpr(expr) {
767
830
  try {
768
831
  if (!expr.includes(":")) return [ this.getCellValueByA1(expr) ];
769
- let sheetKey = this.activeSheetKey || this.reverseSheets.get(0) || "Sheet1", rangeExpr = expr;
770
- if (expr.includes("!")) {
771
- const parts = expr.split("!");
772
- 2 === parts.length && (sheetKey = parts[0], rangeExpr = parts[1]);
773
- }
774
- const [start, end] = rangeExpr.split(":"), startCell = this.parseA1Notation(start.trim()), endCell = this.parseA1Notation(end.trim()), values = [];
832
+ const defaultSheetKey = this.activeSheetKey || this.reverseSheets.get(0) || "Sheet1", parseSheetAndCell = part => {
833
+ var _a, _b;
834
+ let sheetKey = defaultSheetKey, cellRef = part.trim(), hasSheetPrefix = !1;
835
+ const hasEn = cellRef.includes("!"), hasCn = cellRef.includes("!");
836
+ if (hasEn || hasCn) {
837
+ const sep = hasEn ? "!" : "!", parts = cellRef.split(sep);
838
+ if (2 === parts.length) {
839
+ hasSheetPrefix = !0;
840
+ let sheetTitle = parts[0].trim();
841
+ cellRef = parts[1].trim(), sheetTitle.startsWith("'") && sheetTitle.endsWith("'") && (sheetTitle = sheetTitle.slice(1, -1));
842
+ const foundSheetKeyFromTitles = null === (_a = Array.from(this.sheetTitles.entries()).find((([_, value]) => value.toLowerCase() === sheetTitle.toLowerCase()))) || void 0 === _a ? void 0 : _a[0], foundSheetKeyFromKeys = null === (_b = Array.from(this.sheets.entries()).find((([sheetKey]) => sheetKey.toLowerCase() === sheetTitle.toLowerCase()))) || void 0 === _b ? void 0 : _b[0];
843
+ let foundSheetKey = foundSheetKeyFromTitles || foundSheetKeyFromKeys;
844
+ !foundSheetKey && this.sheets && this.sheets.has(sheetTitle) && (foundSheetKey = sheetTitle),
845
+ sheetKey = foundSheetKey || sheetTitle;
846
+ }
847
+ }
848
+ return {
849
+ sheetKey: sheetKey,
850
+ cellRef: cellRef,
851
+ hasSheetPrefix: hasSheetPrefix
852
+ };
853
+ }, [startRaw, endRaw] = expr.split(":"), startParsed = parseSheetAndCell(startRaw), endParsed = parseSheetAndCell(endRaw);
854
+ if (startParsed.hasSheetPrefix && !endParsed.hasSheetPrefix ? endParsed.sheetKey = startParsed.sheetKey : !startParsed.hasSheetPrefix && endParsed.hasSheetPrefix && (startParsed.sheetKey = endParsed.sheetKey),
855
+ startParsed.sheetKey.toLowerCase() !== endParsed.sheetKey.toLowerCase()) return [];
856
+ const sheetKey = startParsed.sheetKey, startCell = this.parseA1Notation(startParsed.cellRef), endCell = this.parseA1Notation(endParsed.cellRef), values = [];
775
857
  for (let row = startCell.row; row <= endCell.row; row++) for (let col = startCell.col; col <= endCell.col; col++) {
776
858
  const cell = {
777
859
  sheet: sheetKey,
@@ -787,6 +869,10 @@ class FormulaEngine {
787
869
  }
788
870
  getCellValue(cell) {
789
871
  try {
872
+ if (!this.sheets) return {
873
+ value: null,
874
+ error: "FormulaEngine not properly initialized: sheets Map is undefined"
875
+ };
790
876
  const sheetId = this.sheets.get(cell.sheet);
791
877
  if (void 0 === sheetId) return {
792
878
  value: "",
@@ -897,14 +983,17 @@ class FormulaEngine {
897
983
  }
898
984
  return result;
899
985
  }
986
+ getSheetTitle(sheetKey) {
987
+ return this.sheetTitles.get(sheetKey);
988
+ }
900
989
  getAllSheets() {
901
990
  const result = [];
902
991
  for (const entry of Array.from(this.sheets.entries())) {
903
- const [sheetKey, sheetId] = entry;
992
+ const [sheetKey, sheetId] = entry, sheetTitle = this.sheetTitles.get(sheetKey) || sheetKey;
904
993
  result.push({
905
994
  key: sheetKey,
906
995
  id: sheetId,
907
- name: sheetKey
996
+ title: sheetTitle
908
997
  });
909
998
  }
910
999
  return result;
@@ -925,7 +1014,7 @@ class FormulaEngine {
925
1014
  if (tempVisited.has(cellKey)) return;
926
1015
  tempVisited.add(cellKey);
927
1016
  const deps = tempDependencies.get(cellKey) || new Set;
928
- for (const dep of deps) tempDependencies.has(dep) && visit(dep);
1017
+ for (const dep of deps) tempDependencies && tempDependencies.has(dep) && visit(dep);
929
1018
  tempVisited.delete(cellKey), visited.add(cellKey);
930
1019
  const cellRef = cellKey.substring(sheetKey.length + 1), formula = formulas[cellRef];
931
1020
  formula && result.push([ cellRef, formula ]);
@@ -1003,6 +1092,8 @@ class FormulaEngine {
1003
1092
  }
1004
1093
  updateDependencies(cellKey, formula) {
1005
1094
  var _a;
1095
+ if (!this.dependencies) return;
1096
+ if (!this.dependents) return;
1006
1097
  const oldDeps = this.dependencies.get(cellKey) || new Set;
1007
1098
  for (const dep of oldDeps) {
1008
1099
  const depDependents = this.dependents.get(dep) || new Set;
@@ -1024,10 +1115,27 @@ class FormulaEngine {
1024
1115
  return [ ...new Set(references) ];
1025
1116
  }
1026
1117
  extractReferencesFromExpression(expr, references, currentSheet = "Sheet1") {
1118
+ const repeatedSheetRangePattern = new RegExp("([A-Za-z0-9_\\s一-龥]+)[!!]([A-Z]+[0-9]+)\\s*:\\s*([A-Za-z0-9_\\s一-龥]+)[!!]([A-Z]+[0-9]+)", "g");
1119
+ let repeatedMatch;
1120
+ for (;null !== (repeatedMatch = repeatedSheetRangePattern.exec(expr)); ) {
1121
+ const sheet1 = repeatedMatch[1], startCell = repeatedMatch[2], sheet2 = repeatedMatch[3], endCell = repeatedMatch[4];
1122
+ if (sheet1.toLowerCase() === sheet2.toLowerCase()) {
1123
+ const expandedRefs = this.expandRangeToCells(sheet1, startCell, endCell);
1124
+ references.push(...expandedRefs);
1125
+ }
1126
+ }
1127
+ const chineseExclamationPattern = /([A-Za-z0-9_\s一-龥]+)!([A-Z]+[0-9]+)(?::([A-Z]+[0-9]+))?/g;
1128
+ let match;
1129
+ for (;null !== (match = chineseExclamationPattern.exec(expr)); ) {
1130
+ const sheetName = match[1], startCell = match[2], endCell = match[3];
1131
+ if (endCell) {
1132
+ const expandedRefs = this.expandRangeToCells(sheetName, startCell, endCell);
1133
+ references.push(...expandedRefs);
1134
+ } else references.push(`${sheetName}!${startCell}`);
1135
+ }
1027
1136
  let cleanExpr = expr.replace(/"[^"]*"/g, "");
1028
1137
  cleanExpr = cleanExpr.replace(/'[^']*'/g, "");
1029
1138
  const cellRefPattern = /(?:([A-Za-z0-9_]+)!)?([A-Z]+[0-9]+)(?::([A-Z]+[0-9]+))?/g;
1030
- let match;
1031
1139
  for (;null !== (match = cellRefPattern.exec(cleanExpr)); ) {
1032
1140
  const sheetName = match[1] || currentSheet, startCell = match[2], endCell = match[3];
1033
1141
  if (endCell) {
@@ -1483,5 +1591,4 @@ class FormulaError {
1483
1591
  constructor(message, type = "VALUE") {
1484
1592
  this.message = message, this.type = type;
1485
1593
  }
1486
- }
1487
- //# sourceMappingURL=formula-engine.js.map
1594
+ }