perf(web): свод 2 — refine во время первой загрузки, «Найдено» без ошибки, измеренная высота строки
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
9ffb47d4e7
commit
d74f5286f9
@ -2,7 +2,7 @@ import RefreshRoundedIcon from '@mui/icons-material/RefreshRounded';
|
|||||||
import { Alert, Box, Button, Paper, Tab, Tabs, Typography } from '@mui/material';
|
import { Alert, Box, Button, Paper, Tab, Tabs, Typography } from '@mui/material';
|
||||||
import { MaterialReactTable, useMaterialReactTable } from 'material-react-table';
|
import { MaterialReactTable, useMaterialReactTable } from 'material-react-table';
|
||||||
import { MRT_Localization_RU } from 'material-react-table/locales/ru';
|
import { MRT_Localization_RU } from 'material-react-table/locales/ru';
|
||||||
import { useCallback, useMemo, useRef, useState } from 'react';
|
import { useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { HeaderSwitch } from '../../components/HeaderMenu/HeaderSwitch';
|
import { HeaderSwitch } from '../../components/HeaderMenu/HeaderSwitch';
|
||||||
@ -317,6 +317,7 @@ export default function Svod2Page() {
|
|||||||
|
|
||||||
const tableRef = useRef(null);
|
const tableRef = useRef(null);
|
||||||
const getScrollTop = useCallback(() => tableRef.current?.refs.tableContainerRef.current?.scrollTop ?? 0, []);
|
const getScrollTop = useCallback(() => tableRef.current?.refs.tableContainerRef.current?.scrollTop ?? 0, []);
|
||||||
|
const [rowHeight, setRowHeight] = useState(ROW_HEIGHT);
|
||||||
const { state, windowRows, padding, facetsMap, load, onScroll } = useSvod2Pages({
|
const { state, windowRows, padding, facetsMap, load, onScroll } = useSvod2Pages({
|
||||||
sheet,
|
sheet,
|
||||||
year,
|
year,
|
||||||
@ -324,7 +325,7 @@ export default function Svod2Page() {
|
|||||||
queryKey: currentQueryKey,
|
queryKey: currentQueryKey,
|
||||||
search,
|
search,
|
||||||
columnFilters,
|
columnFilters,
|
||||||
rowHeight: ROW_HEIGHT,
|
rowHeight,
|
||||||
getScrollTop,
|
getScrollTop,
|
||||||
});
|
});
|
||||||
const { loading, error } = state;
|
const { loading, error } = state;
|
||||||
@ -360,7 +361,7 @@ export default function Svod2Page() {
|
|||||||
enableBottomToolbar: false,
|
enableBottomToolbar: false,
|
||||||
enableSorting: false,
|
enableSorting: false,
|
||||||
getFacetedUniqueValues: (_table, columnId) => () => facetsMapRef.current.get(columnId) ?? EMPTY_FACET_MAP,
|
getFacetedUniqueValues: (_table, columnId) => () => facetsMapRef.current.get(columnId) ?? EMPTY_FACET_MAP,
|
||||||
rowVirtualizerOptions: { ...ROW_VIRTUALIZER_OPTIONS, measureElement: () => ROW_HEIGHT, ...padding },
|
rowVirtualizerOptions: { ...ROW_VIRTUALIZER_OPTIONS, estimateSize: () => rowHeight, measureElement: () => rowHeight, ...padding },
|
||||||
state: { isLoading: loading, columnFilters },
|
state: { isLoading: loading, columnFilters },
|
||||||
initialState: {
|
initialState: {
|
||||||
columnPinning: { left: ['smeta_type', 'smeta_direction', 'org_name', 'name'] },
|
columnPinning: { left: ['smeta_type', 'smeta_direction', 'org_name', 'name'] },
|
||||||
@ -398,6 +399,12 @@ export default function Svod2Page() {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
tableRef.current = table;
|
tableRef.current = table;
|
||||||
|
// Единая высота строки для окна страниц: реальная высота первой строки с данными (по умолчанию ROW_HEIGHT).
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
const element = tableRef.current?.refs.tableContainerRef.current?.querySelector('tbody tr:not([data-svod2-placeholder])');
|
||||||
|
const measured = element ? Math.round(element.offsetHeight) : 0;
|
||||||
|
if (measured > 0 && measured !== rowHeight) setRowHeight(measured);
|
||||||
|
});
|
||||||
let content = <MaterialReactTable table={table} />;
|
let content = <MaterialReactTable table={table} />;
|
||||||
if (!hasCurrentData && !loading && !error) {
|
if (!hasCurrentData && !loading && !error) {
|
||||||
content = (
|
content = (
|
||||||
@ -453,7 +460,7 @@ export default function Svod2Page() {
|
|||||||
emptyOrganizationsPlaceholder='Выберите ССП/РФ'
|
emptyOrganizationsPlaceholder='Выберите ССП/РФ'
|
||||||
allowSelectAll
|
allowSelectAll
|
||||||
additionalFilters={<DenominationSelect value={denomination} onChange={handleDenominationChange} minWidth={135} />}>
|
additionalFilters={<DenominationSelect value={denomination} onChange={handleDenominationChange} minWidth={135} />}>
|
||||||
{shouldShowFound({ hasCurrentData, loading, facets: state.facets }) && (
|
{shouldShowFound({ hasCurrentData, loading, facets: state.facets, error }) && (
|
||||||
<Typography variant='caption' color='text.secondary' sx={{ flexShrink: 0, whiteSpace: 'nowrap' }}>
|
<Typography variant='caption' color='text.secondary' sx={{ flexShrink: 0, whiteSpace: 'nowrap' }}>
|
||||||
{`Найдено ${state.facets.search_total.toLocaleString('ru-RU')} из ${state.facets.total.toLocaleString('ru-RU')} строк`}
|
{`Найдено ${state.facets.search_total.toLocaleString('ru-RU')} из ${state.facets.total.toLocaleString('ru-RU')} строк`}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|||||||
@ -98,8 +98,16 @@ export function facetsToMaps(result) {
|
|||||||
return maps;
|
return maps;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function shouldShowFound({ hasCurrentData, loading, facets }) {
|
export function shouldShowFound({ hasCurrentData, loading, facets, error = '' }) {
|
||||||
return Boolean(hasCurrentData) && !loading && !!facets && facets.search_total !== facets.total;
|
return Boolean(hasCurrentData) && !loading && !error && !!facets && facets.search_total !== facets.total;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Нужно ли уточнение (refine) при смене поиска/колоночных фильтров: на загруженных данных
|
||||||
|
// либо пока для текущего queryKey ещё летит первая загрузка (иначе ответы станут «текущими» по старым условиям).
|
||||||
|
export function shouldRefine({ hasCurrentData, loading, pendingQueryKey, queryKey, prevRefineKey, refineKey }) {
|
||||||
|
if (prevRefineKey === refineKey) return false;
|
||||||
|
if (hasCurrentData) return true;
|
||||||
|
return Boolean(loading) && pendingQueryKey === queryKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isPlaceholderRow(row) {
|
export function isPlaceholderRow(row) {
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import {
|
|||||||
getWindowPages,
|
getWindowPages,
|
||||||
getWindowRange,
|
getWindowRange,
|
||||||
isPlaceholderRow,
|
isPlaceholderRow,
|
||||||
|
shouldRefine,
|
||||||
shouldShowFound,
|
shouldShowFound,
|
||||||
svod2PagingReducer,
|
svod2PagingReducer,
|
||||||
touchPage,
|
touchPage,
|
||||||
@ -279,3 +280,18 @@ test('refine поверх load: loading снимает settled нового по
|
|||||||
s = svod2PagingReducer(s, { type: 'settled', generation: 2 });
|
s = svod2PagingReducer(s, { type: 'settled', generation: 2 });
|
||||||
assert.equal(s.loading, false);
|
assert.equal(s.loading, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('«Найдено» не показывается рядом с ошибкой', () => {
|
||||||
|
assert.equal(shouldShowFound({ hasCurrentData: true, loading: false, error: 'x', facets: { total: 5, search_total: 1 } }), false);
|
||||||
|
});
|
||||||
|
test('shouldRefine: на загруженных данных при смене условий', () => {
|
||||||
|
const base = { hasCurrentData: true, loading: false, pendingQueryKey: 'q', queryKey: 'q', prevRefineKey: 'a', refineKey: 'b' };
|
||||||
|
assert.equal(shouldRefine(base), true);
|
||||||
|
assert.equal(shouldRefine({ ...base, refineKey: 'a' }), false);
|
||||||
|
});
|
||||||
|
test('shouldRefine: поиск изменён во время первой загрузки (T12.7)', () => {
|
||||||
|
const base = { hasCurrentData: false, loading: true, pendingQueryKey: 'q', queryKey: 'q', prevRefineKey: 'a', refineKey: 'b' };
|
||||||
|
assert.equal(shouldRefine(base), true);
|
||||||
|
assert.equal(shouldRefine({ ...base, pendingQueryKey: 'old' }), false);
|
||||||
|
assert.equal(shouldRefine({ ...base, loading: false }), false);
|
||||||
|
});
|
||||||
|
|||||||
@ -17,6 +17,7 @@ import {
|
|||||||
getVisiblePage,
|
getVisiblePage,
|
||||||
getWindowPages,
|
getWindowPages,
|
||||||
getWindowRange,
|
getWindowRange,
|
||||||
|
shouldRefine,
|
||||||
svod2PagingReducer,
|
svod2PagingReducer,
|
||||||
} from './svod2Paging';
|
} from './svod2Paging';
|
||||||
|
|
||||||
@ -235,7 +236,17 @@ export function useSvod2Pages({ sheet, year, orgIds, queryKey, search, columnFil
|
|||||||
syncWindow();
|
syncWindow();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (previous.refineKey !== refineKey && hasCurrentData) start('refine');
|
if (
|
||||||
|
shouldRefine({
|
||||||
|
hasCurrentData,
|
||||||
|
loading: sheetState.loading,
|
||||||
|
pendingQueryKey: sheetState.pendingQueryKey,
|
||||||
|
queryKey,
|
||||||
|
prevRefineKey: previous.refineKey,
|
||||||
|
refineKey,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
start('refine');
|
||||||
}, [sheet, refineKey]);
|
}, [sheet, refineKey]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user