```ts import { useState, useMemo, useCallback } from 'react' import { FieldInfo } from '../types/common' import {FiltersState, VisibilityState} from "../types/filters.ts"; /** * Хук для управления фильтрами и видимостью колонок таблицы. * @param schema Схема полей таблицы. * @param initialData * @returns Объекты состояния фильтров и видимости, а также функции для их изменения. */ export function useTableFilters(schema: FieldInfo[]| null, initialData: unknown[] | null) { const [filters, setFilters] = useState({}) const [visibleColumns, setVisibleColumns] = useState({}) // Инициализация видимости колонок useMemo(() => { if (!schema){ return; } const initialVisibility: VisibilityState = {} schema.forEach(field => { initialVisibility[field.name] = true }) setVisibleColumns(initialVisibility) }, [schema]) // Обновление фильтров const handleFilterChange = useCallback((name: string, filterPart: unknown) => { setFilters(prev => ({ ...prev, [name]: { ...prev[name], ...filterPart } })) }, []) // Переключатель видимости колонки const handleColumnToggle = useCallback((name: string) => { setVisibleColumns(prev => ({ ...prev, [name]: !prev[name] })) }, []) // Логика фильтрации данных, вынесенная из компонента const filteredData = useMemo(() => { if (!initialData || !schema){ return ; } return initialData.filter(item => { return schema.every(field => { const value = item[field.name] const filter = filters[field.name] if (!filter) return true // Обработка по типу if (field.type === 'string') { if (filter.searchText) { if (typeof value !== 'string') return false if (!value.toLowerCase().includes(filter.searchText.toLowerCase())) return false } if (filter.exactMatch && filter.searchText) { return value.toLowerCase() === filter.searchText.toLowerCase() } if (filter.empty !== undefined) { const isEmpty = value === '' || value === null || value === undefined if (filter.empty && !isEmpty) return false if (filter.notEmpty && isEmpty) return false } } else if (field.format === 'date') { const dateValue = new Date(value as string).getTime() if (filter.fromDate) { const fromTime = new Date(filter.fromDate).getTime() if (dateValue < fromTime) return false } if (filter.toDate) { const toTime = new Date(filter.toDate).getTime() if (dateValue > toTime) return false } } else if (field.type === 'number') { const numValue = Number(value) if (filter.gte !== undefined && numValue < filter.gte) return false if (filter.lte !== undefined && numValue > filter.lte) return false } return true }) }) }, [initialData, filters, schema]) return { filters, setFilters, visibleColumns, setVisibleColumns, handleFilterChange, handleColumnToggle, filteredData } } export function FilteredTable({ schema, filteredData, visibleColumns}: FilteredTableProps) { return (
{schema.map( (field) => visibleColumns[field.name] && ( ) )} {filteredData.length > 0 ? ( filteredData.map((item) => ( {schema.map( (field) => visibleColumns[field.name] && ( ) )} )) ) : ( )}
{field.title}
{item[field.name] ?? "-"}
Нет данных для отображения
) } ``` ```ts const { filters, handleFilterChange, visibleColumns, handleColumnToggle, filteredData } = useTableFilters(schema, dataPage.items) const handleExport = async () => { if (!filteredData || filteredData.length === 0) { alert('Нет данных для экспорта') return } const blob = new Blob([buffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', }) saveAs(blob, "Отчёт.") }; ``` в handleExport сохранять csv