perf(web): форматтер чисел свода 1 и сводных проектов создаётся один раз
AURORA-1835: Cell в scaleColumns создавал Intl.NumberFormat на каждую ячейку. Форматирование вынесено в formatSummaryAmount (summaryFormat.js) с модульным форматтером. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
2a287480b6
commit
2cb9006786
@ -5,6 +5,7 @@ import useDenomination from '../../components/common/useDenomination';
|
|||||||
import { blueColumn, greenColumn, orangeColumn } from '../../components/RealtimeTable/constants/columnColors';
|
import { blueColumn, greenColumn, orangeColumn } from '../../components/RealtimeTable/constants/columnColors';
|
||||||
import { sectionCodeColor } from '../../components/RealtimeTable/constants/columnConfig';
|
import { sectionCodeColor } from '../../components/RealtimeTable/constants/columnConfig';
|
||||||
import { buildRowHierarchy } from '../../components/RealtimeTable/utils/rowUtils';
|
import { buildRowHierarchy } from '../../components/RealtimeTable/utils/rowUtils';
|
||||||
|
import { formatSummaryAmount } from './summaryFormat';
|
||||||
|
|
||||||
const tableSize = (rem) => rem * 16;
|
const tableSize = (rem) => rem * 16;
|
||||||
const ROW_VIRTUALIZER_OPTIONS = {
|
const ROW_VIRTUALIZER_OPTIONS = {
|
||||||
@ -68,11 +69,10 @@ const scaleColumns = (columns, divisor) => columns.map((column) => {
|
|||||||
...(children ? { columns: children } : {}),
|
...(children ? { columns: children } : {}),
|
||||||
...(isAmount ? { Cell: ({ cell }) => {
|
...(isAmount ? { Cell: ({ cell }) => {
|
||||||
const value = cell.getValue();
|
const value = cell.getValue();
|
||||||
|
const formatted = formatSummaryAmount(value, divisor);
|
||||||
|
if (formatted !== null) return formatted;
|
||||||
if (value === null || value === undefined || value === '') return originalCell ? originalCell({ cell }) : '—';
|
if (value === null || value === undefined || value === '') return originalCell ? originalCell({ cell }) : '—';
|
||||||
const numeric = Number(value);
|
return originalCell ? originalCell({ cell }) : value;
|
||||||
if (!Number.isFinite(numeric)) return originalCell ? originalCell({ cell }) : value;
|
|
||||||
if (numeric === 0) return '-';
|
|
||||||
return new Intl.NumberFormat('ru-RU', { maximumFractionDigits: 2 }).format(numeric / divisor);
|
|
||||||
} } : {}),
|
} } : {}),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
10
web/src/pages/SvodPage/summaryFormat.js
Normal file
10
web/src/pages/SvodPage/summaryFormat.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
const amountFormatter = new Intl.NumberFormat('ru-RU', { maximumFractionDigits: 2 });
|
||||||
|
|
||||||
|
// Возвращает null, если значение не число: вызывающий отдаёт исходное значение.
|
||||||
|
export const formatSummaryAmount = (value, divisor) => {
|
||||||
|
if (value === null || value === undefined || value === '') return null;
|
||||||
|
const numeric = Number(value);
|
||||||
|
if (!Number.isFinite(numeric)) return null;
|
||||||
|
if (numeric === 0) return '-';
|
||||||
|
return amountFormatter.format(numeric / divisor);
|
||||||
|
};
|
||||||
37
web/src/pages/SvodPage/summaryFormat.test.js
Normal file
37
web/src/pages/SvodPage/summaryFormat.test.js
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import test from 'node:test';
|
||||||
|
|
||||||
|
const NBSP = ' ';
|
||||||
|
|
||||||
|
test('formatSummaryAmount: форматирование и делители', async () => {
|
||||||
|
const { formatSummaryAmount } = await import('./summaryFormat.js');
|
||||||
|
assert.equal(formatSummaryAmount(0, 1), '-');
|
||||||
|
assert.equal(formatSummaryAmount('0', 1000), '-');
|
||||||
|
assert.equal(formatSummaryAmount(1234.567, 1), `1${NBSP}234,57`);
|
||||||
|
assert.equal(formatSummaryAmount(5_000_000, 1000), `5${NBSP}000`);
|
||||||
|
assert.equal(formatSummaryAmount(5_000_000, 1e6), '5');
|
||||||
|
assert.equal(formatSummaryAmount('1500', 1), `1${NBSP}500`);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('formatSummaryAmount: нечисловые значения возвращают null', async () => {
|
||||||
|
const { formatSummaryAmount } = await import('./summaryFormat.js');
|
||||||
|
for (const v of [null, undefined, '', 'abc', Number.NaN, Number.POSITIVE_INFINITY]) {
|
||||||
|
assert.equal(formatSummaryAmount(v, 1), null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Intl.NumberFormat создаётся один раз на модуль', async () => {
|
||||||
|
const Original = Intl.NumberFormat;
|
||||||
|
let created = 0;
|
||||||
|
Intl.NumberFormat = function CountingNumberFormat(...args) {
|
||||||
|
created += 1;
|
||||||
|
return new Original(...args);
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
const { formatSummaryAmount } = await import('./summaryFormat.js?fresh');
|
||||||
|
for (let i = 1; i <= 50; i += 1) formatSummaryAmount(i * 100, 1000);
|
||||||
|
assert.equal(created, 1);
|
||||||
|
} finally {
|
||||||
|
Intl.NumberFormat = Original;
|
||||||
|
}
|
||||||
|
});
|
||||||
Loading…
x
Reference in New Issue
Block a user