2 menus
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
// Регулярные выражения для ссылок
|
||||
const QUALIFIED_REF_RE = /\b[A-Za-z0-9_]+![A-Z]+[0-9]+\b/g; // SheetName!A1
|
||||
const LOCAL_REF_RE = /\b[A-Z]+[0-9]+\b/g; // A1, B2, etc.
|
||||
const RANGE_RE = /\b[A-Z]+[0-9]+:[A-Z]+[0-9]+\b/g; // A1:B2, D5:D7, etc.
|
||||
const QUALIFIED_RANGE_RE = /\b[A-Za-z0-9_]+![A-Z]+[0-9]+:[A-Z]+[0-9]+\b/g; // SheetName!A1:B2
|
||||
|
||||
// Типы для значений ячеек
|
||||
export type CellValue = string | number | boolean | null | undefined;
|
||||
@@ -18,14 +20,29 @@ const DEFAULT_FUNCTIONS: Record<string, ExcelFunction> = {
|
||||
RAND: () => Math.random(),
|
||||
RANDBETWEEN: (bottom: number, top: number) =>
|
||||
Math.floor(Math.random() * (top - bottom + 1)) + bottom,
|
||||
SUM: (...args: number[]) => args.reduce((sum, val) => sum + (val || 0), 0),
|
||||
AVERAGE: (...args: number[]) => {
|
||||
const validArgs = args.filter(val => val !== null && val !== undefined);
|
||||
SUM: (...args: any[]) => {
|
||||
const flatArgs = args.flat();
|
||||
return flatArgs.reduce((sum, val) => sum + (typeof val === 'number' ? val : 0), 0);
|
||||
},
|
||||
AVERAGE: (...args: any[]) => {
|
||||
const flatArgs = args.flat();
|
||||
const validArgs = flatArgs.filter(val => typeof val === 'number');
|
||||
return validArgs.length > 0 ? validArgs.reduce((sum, val) => sum + val, 0) / validArgs.length : 0;
|
||||
},
|
||||
COUNT: (...args: any[]) => args.filter(val => typeof val === 'number').length,
|
||||
MAX: (...args: number[]) => Math.max(...args.filter(val => val !== null && val !== undefined)),
|
||||
MIN: (...args: number[]) => Math.min(...args.filter(val => val !== null && val !== undefined)),
|
||||
COUNT: (...args: any[]) => {
|
||||
const flatArgs = args.flat();
|
||||
return flatArgs.filter(val => typeof val === 'number').length;
|
||||
},
|
||||
MAX: (...args: any[]) => {
|
||||
const flatArgs = args.flat();
|
||||
const validArgs = flatArgs.filter(val => typeof val === 'number');
|
||||
return validArgs.length > 0 ? Math.max(...validArgs) : 0;
|
||||
},
|
||||
MIN: (...args: any[]) => {
|
||||
const flatArgs = args.flat();
|
||||
const validArgs = flatArgs.filter(val => typeof val === 'number');
|
||||
return validArgs.length > 0 ? Math.min(...validArgs) : 0;
|
||||
},
|
||||
IF: (condition: boolean, trueValue: CellValue, falseValue: CellValue) =>
|
||||
condition ? trueValue : falseValue,
|
||||
AND: (...args: boolean[]) => args.every(Boolean),
|
||||
@@ -83,6 +100,37 @@ export class Cell {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
// Функция для развертывания диапазона в массив значений
|
||||
private expandRange(range: string, sheetName?: string): number[] {
|
||||
const parts = range.split(':');
|
||||
if (parts.length !== 2) return [];
|
||||
|
||||
const startCell = parts[0];
|
||||
const endCell = parts[1];
|
||||
|
||||
const startCol = startCell.match(/[A-Z]+/)?.[0] || '';
|
||||
const startRow = parseInt(startCell.match(/[0-9]+/)?.[0] || '0');
|
||||
const endCol = endCell.match(/[A-Z]+/)?.[0] || '';
|
||||
const endRow = parseInt(endCell.match(/[0-9]+/)?.[0] || '0');
|
||||
|
||||
const startColIndex = startCol.charCodeAt(0) - 65;
|
||||
const endColIndex = endCol.charCodeAt(0) - 65;
|
||||
|
||||
const values: number[] = [];
|
||||
|
||||
for (let row = startRow; row <= endRow; row++) {
|
||||
for (let col = startColIndex; col <= endColIndex; col++) {
|
||||
const cellName = String.fromCharCode(65 + col) + row;
|
||||
const qualifiedName = sheetName ? `${sheetName}!${cellName}` : `${this.sheet.name}!${cellName}`;
|
||||
const cellValue = this.sheet.workbook.getCell(qualifiedName).evaluate();
|
||||
const numValue = typeof cellValue === 'number' ? cellValue : parseFloat(String(cellValue)) || 0;
|
||||
values.push(numValue);
|
||||
}
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
get qualifiedName(): string {
|
||||
return `${this.sheet.name}!${this.name}`;
|
||||
}
|
||||
@@ -147,6 +195,19 @@ export class Cell {
|
||||
// Заменяем ссылки на вычисленные значения
|
||||
let code = expr;
|
||||
|
||||
// ВАЖНО: Сначала заменяем диапазоны ячеек на массивы значений,
|
||||
// затем отдельные ячейки, чтобы избежать конфликтов
|
||||
code = code.replace(QUALIFIED_RANGE_RE, (match) => {
|
||||
const [sheetName, range] = match.split('!');
|
||||
const values = this.expandRange(range, sheetName);
|
||||
return `[${values.join(',')}]`;
|
||||
});
|
||||
|
||||
code = code.replace(RANGE_RE, (match) => {
|
||||
const values = this.expandRange(match);
|
||||
return `[${values.join(',')}]`;
|
||||
});
|
||||
|
||||
// Заменяем квалифицированные ссылки на ячейки
|
||||
code = code.replace(QUALIFIED_REF_RE, (match) => {
|
||||
const cellValue = this.sheet.workbook.getCell(match).evaluate();
|
||||
|
||||
63
src/lib/template-data.ts
Normal file
63
src/lib/template-data.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
// Пример шаблонных данных для отчета (только простые значения из Excel)
|
||||
export const exampleTemplateData = {
|
||||
// Заголовки
|
||||
A1: 'Отчет о продажах',
|
||||
A2: 'Период: Январь 2024',
|
||||
A3: '',
|
||||
|
||||
// Таблица данных
|
||||
A4: 'Товар',
|
||||
B4: 'Количество',
|
||||
C4: 'Цена',
|
||||
D4: 'Сумма',
|
||||
|
||||
A5: 'Товар A',
|
||||
B5: 100,
|
||||
C5: 1500,
|
||||
D5: 150000, // Простое значение вместо формулы
|
||||
|
||||
A6: 'Товар B',
|
||||
B6: 50,
|
||||
C6: 2000,
|
||||
D6: 100000, // Простое значение вместо формулы
|
||||
|
||||
A7: 'Товар C',
|
||||
B7: 75,
|
||||
C7: 1200,
|
||||
D7: 90000, // Простое значение вместо формулы
|
||||
|
||||
A8: '',
|
||||
A9: 'Итого:',
|
||||
D9: 340000, // Простое значение вместо формулы
|
||||
|
||||
// Дополнительные поля для расчетов
|
||||
A11: 'НДС (20%):',
|
||||
D11: 68000, // Простое значение вместо формулы
|
||||
|
||||
A12: 'Всего к оплате:',
|
||||
D12: 408000, // Простое значение вместо формулы
|
||||
|
||||
// Поля для ссылок на правый лист (пока пустые, формулы можно добавить поверх)
|
||||
A14: 'Скидка из расчетов:',
|
||||
D14: '', // Пустое значение, формулу можно добавить поверх
|
||||
|
||||
A15: 'Итого со скидкой:',
|
||||
D15: '' // Пустое значение, формулу можно добавить поверх
|
||||
};
|
||||
|
||||
// Пример данных для листа вычислений
|
||||
export const exampleCalculationsData = {
|
||||
A1: 'Расчеты',
|
||||
A2: 'Скидка:',
|
||||
B2: '=Report!D9*0.05', // 5% скидка от общей суммы
|
||||
|
||||
A4: 'Дополнительные расчеты:',
|
||||
A5: 'Средняя цена:',
|
||||
B5: '=AVERAGE(Report!C5:C7)',
|
||||
|
||||
A6: 'Максимальная сумма:',
|
||||
B6: '=MAX(Report!D5:D7)',
|
||||
|
||||
A7: 'Минимальная сумма:',
|
||||
B7: '=MIN(Report!D5:D7)',
|
||||
};
|
||||
Reference in New Issue
Block a user