fixed a bug with tabs

pull/40/head
DmitriyA 2025-04-23 08:48:09 -04:00
parent 40d8046617
commit 6fd5d1aed2
3 changed files with 74 additions and 49 deletions

View File

@ -1,4 +1,4 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect, useRef } from "react";
import { import {
Drawer, Drawer,
List, List,
@ -35,11 +35,10 @@ const SidebarResizer = styled('div')(({ theme }) => ({
const SidebarMenu = ({ data, onOpenTab, sidebarWidth, startResizing, collapsed, setCollapsed }) => { const SidebarMenu = ({ data, onOpenTab, sidebarWidth, startResizing, collapsed, setCollapsed }) => {
const [hovered, setHovered] = useState(false); const [hovered, setHovered] = useState(false);
const [menuData, setMenuData] = useState(data); const [menuData, setMenuData] = useState(data);
const contentCache = useRef({});
// Обновляем статусы при изменении данных
useEffect(() => { useEffect(() => {
if (data) { if (data) {
// Создаем глубокую копию данных, чтобы не мутировать исходные
const dataCopy = JSON.parse(JSON.stringify(data)); const dataCopy = JSON.parse(JSON.stringify(data));
statusManager1.updateStatuses(dataCopy); statusManager1.updateStatuses(dataCopy);
setMenuData(dataCopy); setMenuData(dataCopy);
@ -51,9 +50,16 @@ const SidebarMenu = ({ data, onOpenTab, sidebarWidth, startResizing, collapsed,
}; };
const handleSelectItem = (id, title, children) => { const handleSelectItem = (id, title, children) => {
onOpenTab(id, title, children); onOpenTab(id, title);
contentCache.current = tabContent({ items: children }, contentCache.current);
if (contentCache.current[id]) {
onOpenTab(id, title, contentCache.current[id].content);
}
}; };
const drawerWidth = collapsed ? 64 : sidebarWidth; const drawerWidth = collapsed ? 64 : sidebarWidth;
return ( return (

View File

@ -5,26 +5,23 @@ import Box from '@mui/material/Box';
const PrometheusChart = lazy(() => import('../../Charts/PrometheusChart')); const PrometheusChart = lazy(() => import('../../Charts/PrometheusChart'));
import LazyChartBatchRenderer from "../hooks/LazyChartBatchRender"; import LazyChartBatchRenderer from "../hooks/LazyChartBatchRender";
// Функция для генерации названия метрики на основе id
const getMetricName = (id) => { const getMetricName = (id) => {
return `zvks_apiforsnmp_measure_${id}`; return `zvks_apiforsnmp_measure_${id}`;
}; };
// Функция для рекурсивного сбора всех id потомков
const getAllChildIds = (node) => { const getAllChildIds = (node) => {
let ids = []; let ids = [];
if (node.id) { if (node.id) {
ids.push(node.id); // Добавляем id текущего узла ids.push(node.id);
} }
if (node.items && node.items.length > 0) { if (node.items && node.items.length > 0) {
node.items.forEach((child) => { node.items.forEach((child) => {
ids = ids.concat(getAllChildIds(child)); // Рекурсивно собираем id потомков ids = ids.concat(getAllChildIds(child));
}); });
} }
return ids; return ids;
}; };
// Компонент Skeleton для графика
const ChartSkeleton = () => ( const ChartSkeleton = () => (
<Box sx={{ width: '100%' }}> <Box sx={{ width: '100%' }}>
<Skeleton variant="text" width="60%" height={30} /> {/* Заголовок */} <Skeleton variant="text" width="60%" height={30} /> {/* Заголовок */}
@ -32,7 +29,6 @@ const ChartSkeleton = () => (
</Box> </Box>
); );
// Компонент Skeleton для родительского контейнера
const ContainerSkeleton = () => ( const ContainerSkeleton = () => (
<Box sx={{ width: '100%' }}> <Box sx={{ width: '100%' }}>
<Skeleton variant="text" width="40%" height={40} /> {/* Заголовок */} <Skeleton variant="text" width="40%" height={40} /> {/* Заголовок */}
@ -46,69 +42,52 @@ const ContainerSkeleton = () => (
</Box> </Box>
); );
const tabContent = (data) => { const tabContent = (data, existingContent = {}) => {
const tabContent = {}; const tabContent = { ...existingContent };
// Функция для рекурсивного обхода и сбора данных
// Функция для рекурсивного обхода и сбора данных
const generateContent = (nodes) => { const generateContent = (nodes) => {
nodes.forEach((node) => { nodes.forEach((node) => {
// Если у узла есть вложенные элементы, рекурсивно обрабатываем их if (tabContent[node.id]) return;
if (node.items && node.items.length > 0) { if (node.items && node.items.length > 0) {
// Создаем контент для родителя generateContent(node.items);
const childrenContent = generateContent(node.items);
const content = ( const content = (
<div> <div key={node.id}>
<h2>{node.title}</h2> <h2>{node.title}</h2>
<Suspense fallback={<ContainerSkeleton />}> <Suspense fallback={<ContainerSkeleton />}>
<LazyChartBatchRenderer charts={node.items.map((child) => tabContent[child.id].content)} /> <LazyChartBatchRenderer
charts={node.items.map(child => tabContent[child.id]?.content) || <ChartSkeleton />}
/>
</Suspense> </Suspense>
<p>Контент для {node.title}.</p>
{/*childrenContent*/}
</div> </div>
); );
// Сохраняем контент для текущего id
tabContent[node.id] = { tabContent[node.id] = {
title: node.title, title: node.title,
content: content, content: content,
}; };
} else { } else {
// Если у узла нет вложенных элементов, это самый нижний уровень
const metricName = getMetricName(node.id); const metricName = getMetricName(node.id);
const content = ( const content = (
<div key={node.id}> <div key={node.id}>
<h3>{node.title}</h3> {/* Используем title узла */} <h3>{node.title}</h3>
<Suspense fallback={<ChartSkeleton />}> <Suspense fallback={<ChartSkeleton />}>
<PrometheusChart metricName={metricName} /> <PrometheusChart metricName={metricName} />
</Suspense> </Suspense>
</div> </div>
); );
// Сохраняем контент для текущего id
tabContent[node.id] = { tabContent[node.id] = {
title: node.title, title: node.title,
content: content, content: content,
}; };
} }
}); });
// Возвращаем контент для всех потомков
return (
<div>
{nodes.map((node) => (
<div key={node.id}>{tabContent[node.id].content}</div>
))}
</div>
);
}; };
// Начинаем обработку с корневого уровня
if (data.items && data.items.length > 0) { if (data.items && data.items.length > 0) {
generateContent(data.items); generateContent(data.items);
} else {
console.warn("Данные отсутствуют или массив items пуст");
} }
return tabContent; return tabContent;

View File

@ -4,23 +4,63 @@ const useTabs = (initialTab) => {
const [tabs, setTabs] = useState([]); const [tabs, setTabs] = useState([]);
const [activeTab, setActiveTab] = useState(initialTab); const [activeTab, setActiveTab] = useState(initialTab);
const handleOpenTab = useCallback((id, title) => { const handleOpenTab = useCallback((id, title, content) => {
setTabs((prevTabs) => setTabs((prevTabs) => {
prevTabs.some((tab) => tab.id === id) const existingTabIndex = prevTabs.findIndex(tab => tab.id === id);
? prevTabs
: [...prevTabs, { id, title }] if (existingTabIndex >= 0) {
); return prevTabs.map((tab, index) => ({
...tab,
content: content || tab.content,
active: index === existingTabIndex
}));
}
// Добавляем новую вкладку
return [
...prevTabs.map(tab => ({ ...tab, active: false })),
{
id,
title,
content: content || <div>Loading...</div>,
active: true
}
];
});
setActiveTab(id); setActiveTab(id);
}, []); }, []);
const handleCloseTab = useCallback((id) => { const handleCloseTab = useCallback((id) => {
setTabs((prevTabs) => prevTabs.filter((tab) => tab.id !== id)); setTabs((prevTabs) => {
if (activeTab === id) { const newTabs = prevTabs.filter((tab) => tab.id !== id);
setActiveTab(tabs.length > 1 ? tabs[tabs.length - 2].id : initialTab);
}
}, [activeTab, tabs, initialTab]);
return { tabs, activeTab, handleOpenTab, handleCloseTab, setActiveTab }; if (activeTab === id) {
setActiveTab(newTabs.length > 0
? newTabs[newTabs.length - 1].id
: initialTab
);
}
return newTabs;
});
}, [activeTab, initialTab]);
const updateTabContent = useCallback((id, content) => {
setTabs(prevTabs =>
prevTabs.map(tab =>
tab.id === id ? { ...tab, content } : tab
)
);
}, []);
return {
tabs,
activeTab,
handleOpenTab,
handleCloseTab,
setActiveTab,
updateTabContent
};
}; };
export default useTabs; export default useTabs;