Добавил таблицу, статусы в виде зашлушек и их рандомную генерацию

pull/8/head
DmitriyA 2025-02-27 09:54:04 -05:00
parent 9ba64c71d5
commit eaf706ddfe
10 changed files with 480 additions and 220 deletions

View File

@ -14,7 +14,7 @@ const PrometheusChart = ({ metricName }) => {
useEffect(() => { useEffect(() => {
const fetchData = async () => { const fetchData = async () => {
try { try {
const response = await axios.get(`http://192.168.2.33:3000/metrics?metric=prometheus_target_metadata_cache_bytes`); const response = await axios.get(`http://192.168.2.39:3000/metrics?metric=zvks_apiforsnmp_ifOutUnicastPacket1`);
const result = response.data; const result = response.data;
// Проверяем структуру данных // Проверяем структуру данных

View File

@ -5,20 +5,29 @@ import "../../Style/Dashboard.css";
import ErrorIndicator from "../UI/ErrorIndicator"; import ErrorIndicator from "../UI/ErrorIndicator";
import tabContentData from "../TreeChart/tabContent"; import tabContentData from "../TreeChart/tabContent";
import Tabs from "../UI/Tabs"; import Tabs from "../UI/Tabs";
import menuData from "../TreeChart//menuData.json"; // Загружаем новое меню import menuData from "../TreeChart/menuData.json"; // Исходные данные меню
import TableComponent from '../UI/TreeTable';
import TreeTable from "../UI/TreeTable"; import TreeTable from "../UI/TreeTable";
import { updateStatuses } from "../TreeChart/dataUtils"; // Функция обновления статусов
const Dashboard = () => { const Dashboard = () => {
const [tabs, setTabs] = useState([]); const [tabs, setTabs] = useState([]);
const [activeTab, setActiveTab] = useState("Главная"); const [activeTab, setActiveTab] = useState("Главная");
const [tabContent, setTabContent] = useState({}); const [tabContent, setTabContent] = useState({});
const [treeData, setTreeData] = useState(null); const [treeData, setTreeData] = useState(menuData); // Загружаем меню в state
// Обновление treeData каждые 10 секунд
useEffect(() => { useEffect(() => {
setTabContent(tabContentData); setTabContent(tabContentData);
setTreeData(menuData);
const interval = setInterval(() => {
setTreeData((prevData) => {
const updatedData = JSON.parse(JSON.stringify(prevData)); // Клонируем данные
updateStatuses(updatedData); // Обновляем статусы
return updatedData;
});
}, 10000);
return () => clearInterval(interval);
}, []); }, []);
const handleOpenTab = (id, title) => { const handleOpenTab = (id, title) => {
@ -42,7 +51,7 @@ const Dashboard = () => {
<div> <div>
<h2>Общий мониторинг</h2> <h2>Общий мониторинг</h2>
<ErrorIndicator /> <ErrorIndicator />
<TreeTable data={menuData.items} /> <TreeTable data={treeData.items} /> {/* Теперь используем актуальные данные */}
</div> </div>
); );
} else if (activeTab === "Визуализация") { } else if (activeTab === "Визуализация") {
@ -55,8 +64,7 @@ const Dashboard = () => {
return ( return (
<div className="dashboard-container"> <div className="dashboard-container">
<SidebarMenu onOpenTab={handleOpenTab} /> <SidebarMenu data={treeData} onOpenTab={handleOpenTab} /> {/* Передаём обновлённые данные */}
<div className="main-content"> <div className="main-content">
<Tabs <Tabs
tabs={tabs} tabs={tabs}

View File

@ -1,19 +1,6 @@
import React, { useState } from "react"; import React, { useState } from "react";
import "../../Style/SidebarMenu.css"; import "../../Style/SidebarMenu.css";
import menuData from "../TreeChart/menuData.json"; import { getStatusColor } from "../TreeChart/dataUtils"; // Импортируем только нужную функцию
const getStatusColor = (status) => {
switch (status) {
case "green":
return "#4CAF50"; // Зеленый
case "yellow":
return "#FFEB3B"; // Желтый
case "red":
return "#F44336"; // Красный
default:
return "#3d74c7"; // Белый (или любой другой стандартный цвет)
}
};
const MenuItem = ({ item, onSelectItem }) => { const MenuItem = ({ item, onSelectItem }) => {
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
@ -45,15 +32,15 @@ const MenuItem = ({ item, onSelectItem }) => {
); );
}; };
function SidebarMenu({ onOpenTab }) { function SidebarMenu({ data, onOpenTab }) { // Теперь получаем `data` из пропсов
const handleSelectItem = (item) => { const handleSelectItem = (item) => {
onOpenTab(item.id, item.title); // Передаем id и title onOpenTab(item.id, item.title);
}; };
return ( return (
<div className="sidebar"> <div className="sidebar">
<h2 className="sidebar-title">Меню</h2> <h2 className="sidebar-title">Меню</h2>
<MenuItem item={menuData} onSelectItem={handleSelectItem} /> <MenuItem item={data} onSelectItem={handleSelectItem} />
</div> </div>
); );
} }

View File

@ -1,71 +1,75 @@
import React, { useRef, useEffect } from "react"; import React, { useRef, useEffect } from "react";
import * as d3 from "d3"; import * as d3 from "d3";
import { getStatusColor } from "./dataUtils";
const TreeChart = ({ data, onNodeClick }) => { const TreeChart = ({ data, onNodeClick }) => {
const chartRef = useRef(); const chartRef = useRef();
useEffect(() => { useEffect(() => {
if (!data) return; if (!data || !data.items) return;
// Очищаем старый граф перед отрисовкой const width = 1000;
d3.select(chartRef.current).selectAll("*").remove();
const width = 928;
const height = 1000; const height = 1000;
const root = d3.hierarchy(data, (d) => d.items); const root = d3.hierarchy(data, (d) => d.items);
const links = root.links(); const links = root.links();
const nodes = root.descendants(); const nodes = root.descendants();
const simulation = d3 const svg = d3.select(chartRef.current);
.forceSimulation(nodes) svg.selectAll("*").remove();
.force("link", d3.forceLink(links).id((d) => d.data.title).distance(80).strength(1))
.force("charge", d3.forceManyBody().strength(-500))
.force("x", d3.forceX())
.force("y", d3.forceY());
const svg = d3 svg
.select(chartRef.current)
.attr("width", width) .attr("width", width)
.attr("height", height) .attr("height", height)
.attr("viewBox", [-width / 2, -height / 2, width, height]) .attr("viewBox", [-width / 2, -height / 2, width, height])
.attr("style", "max-width: 100%; height: auto;"); .attr("style", "max-width: 100%; height: auto;");
const link = svg const link = svg.append("g")
.append("g")
.attr("stroke", "#999") .attr("stroke", "#999")
.attr("stroke-opacity", 0.6) .attr("stroke-opacity", 0.6)
.selectAll("line") .selectAll("line")
.data(links) .data(links)
.join("line"); .join("line"); // Используем join для обновления связей
const node = svg const node = svg.append("g")
.append("g")
.attr("stroke", "#000") .attr("stroke", "#000")
.attr("stroke-width", 1.5) .attr("stroke-width", 1.5)
.selectAll("circle") .selectAll("circle")
.data(nodes) .data(nodes)
.join("circle") .join("circle") // Используем join для обновления узлов
.attr("fill", (d) => { .attr("fill", (d) => getStatusColor(d.data.status))
// Окрашиваем узлы в зависимости от статуса
switch (d.data.status) {
case "green":
return "#4CAF50"; // Зеленый
case "yellow":
return "#FFEB3B"; // Желтый
case "red":
return "#F44336"; // Красный
default:
return "#555"; // Серый по умолчанию
}
})
.attr("stroke", "#fff") .attr("stroke", "#fff")
.attr("r", 7) .attr("r", 7)
.call(drag(simulation)); .call(drag());
// Добавляем текстовые подписи // Обновляем только те узлы, которые нуждаются в изменении
const text = svg node.each(function (d) {
.append("g") if (d.data.status === "red") {
d3.select(this)
.transition()
.duration(500)
.ease(d3.easeLinear)
.style("opacity", 0.3)
.transition()
.duration(500)
.ease(d3.easeLinear)
.style("opacity", 1)
.on("end", function repeat() {
d3.select(this)
.transition()
.duration(500)
.ease(d3.easeLinear)
.style("opacity", 0.3)
.transition()
.duration(500)
.ease(d3.easeLinear)
.style("opacity", 1)
.on("end", repeat);
});
}
});
const text = svg.append("g")
.attr("fill", "#000") .attr("fill", "#000")
.attr("font-family", "Arial") .attr("font-family", "Arial")
.attr("font-size", 12) .attr("font-size", 12)
@ -81,10 +85,16 @@ const TreeChart = ({ data, onNodeClick }) => {
node.on("click", (event, d) => { node.on("click", (event, d) => {
if (onNodeClick) { if (onNodeClick) {
onNodeClick(d.data.id, d.data.title); // Передаем id и title onNodeClick(d.data.id, d.data.title);
} }
}); });
const simulation = d3.forceSimulation(nodes)
.force("link", d3.forceLink(links).id((d) => d.data.title).distance(80).strength(1))
.force("charge", d3.forceManyBody().strength(-500))
.force("x", d3.forceX())
.force("y", d3.forceY());
simulation.on("tick", () => { simulation.on("tick", () => {
link link
.attr("x1", (d) => d.source.x) .attr("x1", (d) => d.source.x)
@ -101,14 +111,11 @@ const TreeChart = ({ data, onNodeClick }) => {
.attr("y", (d) => d.y + 4); .attr("y", (d) => d.y + 4);
}); });
return () => { return () => simulation.stop();
simulation.stop();
};
}, [data, onNodeClick]); }, [data, onNodeClick]);
const drag = (simulation) => { const drag = () => {
function dragstarted(event, d) { function dragstarted(event, d) {
if (!event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x; d.fx = d.x;
d.fy = d.y; d.fy = d.y;
} }
@ -119,7 +126,6 @@ const TreeChart = ({ data, onNodeClick }) => {
} }
function dragended(event, d) { function dragended(event, d) {
if (!event.active) simulation.alphaTarget(0);
d.fx = null; d.fx = null;
d.fy = null; d.fy = null;
} }

View File

@ -0,0 +1,53 @@
// Функция для генерации случайных статусов
const getRandomStatus = () => {
const statuses = [
"green", "green", "green", "green", "green", "green", "green", // 7/10 chance
"yellow", // 1/10 chance
"orange", // 1/10 chance
"red", // 1/10 chance
];
return statuses[Math.floor(Math.random() * statuses.length)];
};
// Функция для обновления статусов в дереве
const updateStatuses = (data) => {
if (!data.items || data.items.length === 0) {
// Если это элемент нижнего уровня, генерируем случайный статус
data.status = getRandomStatus();
return data.status;
}
// Рекурсивно обновляем статусы для всех дочерних элементов
let childStatuses = data.items.map((child) => updateStatuses(child));
// Определяем статус текущего элемента на основе статусов дочерних элементов
if (childStatuses.includes("red")) {
data.status = "red";
} else if (childStatuses.includes("orange")) {
data.status = "orange";
} else if (childStatuses.includes("yellow")) {
data.status = "yellow";
} else {
data.status = "green";
}
return data.status;
};
// Функция для получения цвета по статусу
const getStatusColor = (status) => {
switch (status) {
case "green":
return "#4CAF50"; // Зеленый
case "yellow":
return "#FFEB3B"; // Желтый
case "orange":
return "#FF9800"; // Оранжевый
case "red":
return "#F44336"; // Красный
default:
return "#3d74c7"; // Синий (или любой другой стандартный цвет)
}
};
export { getRandomStatus, updateStatuses, getStatusColor };

View File

@ -1,35 +1,28 @@
{ {
"title": "Сервис ВКС", "title": "Сервис ВКС",
"status": "red",
"items": [ "items": [
{ {
"title": "Функциональные задачи", "title": "Функциональные задачи",
"status": "red",
"items": [ "items": [
{ {
"id": "system_control", "id": "system_control",
"title": "Контроль системы", "title": "Контроль системы"
"status": "red"
}, },
{ {
"id": "system_management", "id": "system_management",
"title": "Система управления", "title": "Система управления"
"status": "green"
}, },
{ {
"id": "conference", "id": "conference",
"title": "Проведение ВКС", "title": "Проведение ВКС"
"status": "green"
}, },
{ {
"id": "backup", "id": "backup",
"title": "Резервное копирование", "title": "Резервное копирование"
"status": "green"
}, },
{ {
"id": "relay_info", "id": "relay_info",
"title": "Ретрансляция информации", "title": "Ретрансляция информации"
"status": "green"
} }
] ]
}, },
@ -81,7 +74,195 @@
] ]
}, },
{ {
"title": "Сервер резервного копирования", "title": "Медиа сервер",
"items": [
{
"title": "Аппаратное обеспечение",
"items": [
{
"id": "media_system_software_1_2",
"title": "Центральный процессор"
},
{
"id": "media_system_software_2_2",
"title": "Оперативная память"
},
{
"id": "media_system_software_3_2",
"title": "Жесткий диск"
},
{
"id": "media_system_software_4_2",
"title": "Сетевые адаптеры"
}
]
},
{
"title": "Программное обеспечение",
"items": [
{
"id": "media_software_1_2",
"title": "ПО"
},
{
"id": "media_software_2_2",
"title": "ПО"
},
{
"id": "media_software_3_2",
"title": "ПО"
},
{
"id": "media_software_4_2",
"title": "ПО"
}
]
}
]
},
{
"title": "Медиа сервер",
"items": [
{
"title": "Аппаратное обеспечение",
"items": [
{
"id": "media_system_software_1_3",
"title": "Центральный процессор"
},
{
"id": "media_system_software_2_3",
"title": "Оперативная память"
},
{
"id": "media_system_software_3_3",
"title": "Жесткий диск"
},
{
"id": "media_system_software_4_3",
"title": "Сетевые адаптеры"
}
]
},
{
"title": "Программное обеспечение",
"items": [
{
"id": "media_software_1_3",
"title": "ПО"
},
{
"id": "media_software_2_3",
"title": "ПО"
},
{
"id": "media_software_3_3",
"title": "ПО"
},
{
"id": "media_software_4_3",
"title": "ПО"
}
]
}
]
},
{
"title": "Медиа сервер",
"items": [
{
"title": "Аппаратное обеспечение",
"items": [
{
"id": "media_system_software_1_4",
"title": "Центральный процессор"
},
{
"id": "media_system_software_2_4",
"title": "Оперативная память"
},
{
"id": "media_system_software_3_4",
"title": "Жесткий диск"
},
{
"id": "media_system_software_4_4",
"title": "Сетевые адаптеры"
}
]
},
{
"title": "Программное обеспечение",
"items": [
{
"id": "media_software_1_4",
"title": "ПО"
},
{
"id": "media_software_2_4",
"title": "ПО"
},
{
"id": "media_software_3_4",
"title": "ПО"
},
{
"id": "media_software_4_4",
"title": "ПО"
}
]
}
]
},
{
"title": "Медиа сервер",
"items": [
{
"title": "Аппаратное обеспечение",
"items": [
{
"id": "media_system_software_1_5",
"title": "Центральный процессор"
},
{
"id": "media_system_software_2_5",
"title": "Оперативная память"
},
{
"id": "media_system_software_3_5",
"title": "Жесткий диск"
},
{
"id": "media_system_software_4_5",
"title": "Сетевые адаптеры"
}
]
},
{
"title": "Программное обеспечение",
"items": [
{
"id": "media_software_1_5",
"title": "ПО"
},
{
"id": "media_software_2_5",
"title": "ПО"
},
{
"id": "media_software_3_5",
"title": "ПО"
},
{
"id": "media_software_4_5",
"title": "ПО"
}
]
}
]
},
{
"title": "Сервер систем",
"items": [ "items": [
{ {
"title": "Аппаратное обеспечение", "title": "Аппаратное обеспечение",
@ -128,7 +309,7 @@
] ]
}, },
{ {
"title": "Сервер системы управления", "title": "Сервер систем",
"items": [ "items": [
{ {
"title": "Аппаратное обеспечение", "title": "Аппаратное обеспечение",
@ -173,53 +354,6 @@
] ]
} }
] ]
},
{
"title": "Сервер сбора и ретрансляции информации",
"items": [
{
"title": "Аппаратное обеспечение",
"items": [
{
"id": "system_software_1",
"title": "Центральный процессор"
},
{
"id": "system_software_2",
"title": "Оперативная память"
},
{
"id": "system_software_3",
"title": "Жесткий диск"
},
{
"id": "system_software_4",
"title": "Сетевые адаптеры"
}
]
},
{
"title": "Программное обеспечение",
"items": [
{
"id": "software_1",
"title": "ПО"
},
{
"id": "software_2",
"title": "ПО"
},
{
"id": "software_3",
"title": "ПО"
},
{
"id": "software_4",
"title": "ПО"
}
]
}
]
} }
] ]
} }

View File

@ -12,7 +12,7 @@ const tabContent = {
backup: { title: "Резервное копирование", content: <div><h2>Резервное копирование</h2><p>Процесс резервного копирования.</p></div> }, backup: { title: "Резервное копирование", content: <div><h2>Резервное копирование</h2><p>Процесс резервного копирования.</p></div> },
relay_info: { title: "Ретрансляция информации", content: <div><h2>Ретрансляция информации</h2><p>Детали ретрансляции.</p></div> }, relay_info: { title: "Ретрансляция информации", content: <div><h2>Ретрансляция информации</h2><p>Детали ретрансляции.</p></div> },
// Медиа сервер // Медиа сервер 1
media_system_software_1: { title: "Центральный процессор", content: <div><h2>Центральный процессор</h2><p>Описание центрального процессора медиа сервера.</p></div> }, media_system_software_1: { title: "Центральный процессор", content: <div><h2>Центральный процессор</h2><p>Описание центрального процессора медиа сервера.</p></div> },
media_system_software_2: { title: "Оперативная память", content: <div><h2>Оперативная память</h2><p>Описание оперативной памяти медиа сервера.</p></div> }, media_system_software_2: { title: "Оперативная память", content: <div><h2>Оперативная память</h2><p>Описание оперативной памяти медиа сервера.</p></div> },
media_system_software_3: { title: "Жесткий диск", content: <div><h2>Жесткий диск</h2><p>Описание жесткого диска медиа сервера.</p></div> }, media_system_software_3: { title: "Жесткий диск", content: <div><h2>Жесткий диск</h2><p>Описание жесткого диска медиа сервера.</p></div> },
@ -22,6 +22,46 @@ const tabContent = {
media_software_3: { title: "ПО", content: <div><h2>Программное обеспечение медиа сервера</h2><p>Описание ПО медиа сервера.</p></div> }, media_software_3: { title: "ПО", content: <div><h2>Программное обеспечение медиа сервера</h2><p>Описание ПО медиа сервера.</p></div> },
media_software_4: { title: "ПО", content: <div><h2>Программное обеспечение медиа сервера</h2><p>Описание ПО медиа сервера.</p></div> }, media_software_4: { title: "ПО", content: <div><h2>Программное обеспечение медиа сервера</h2><p>Описание ПО медиа сервера.</p></div> },
// Медиа сервер 2
media_system_software_1_2: { title: "Центральный процессор", content: <div><h2>Центральный процессор</h2><p>Описание центрального процессора медиа сервера.</p></div> },
media_system_software_2_2: { title: "Оперативная память", content: <div><h2>Оперативная память</h2><p>Описание оперативной памяти медиа сервера.</p></div> },
media_system_software_3_2: { title: "Жесткий диск", content: <div><h2>Жесткий диск</h2><p>Описание жесткого диска медиа сервера.</p></div> },
media_system_software_4_2: { title: "Сетевые адаптеры", content: <div><h2>Сетевые адаптеры</h2><p>Описание сетевых адаптеров медиа сервера.</p></div> },
media_software_1_2: { title: "ПО", content: <div><h2>Программное обеспечение медиа сервера</h2><PrometheusChart /></div> },
media_software_2_2: { title: "ПО", content: <div><h2>Программное обеспечение медиа сервера</h2><p>Описание ПО медиа сервера.</p></div> },
media_software_3_2: { title: "ПО", content: <div><h2>Программное обеспечение медиа сервера</h2><p>Описание ПО медиа сервера.</p></div> },
media_software_4_2: { title: "ПО", content: <div><h2>Программное обеспечение медиа сервера</h2><p>Описание ПО медиа сервера.</p></div> },
// Медиа сервер 3
media_system_software_1_3: { title: "Центральный процессор", content: <div><h2>Центральный процессор</h2><p>Описание центрального процессора медиа сервера.</p></div> },
media_system_software_2_3: { title: "Оперативная память", content: <div><h2>Оперативная память</h2><p>Описание оперативной памяти медиа сервера.</p></div> },
media_system_software_3_3: { title: "Жесткий диск", content: <div><h2>Жесткий диск</h2><p>Описание жесткого диска медиа сервера.</p></div> },
media_system_software_4_3: { title: "Сетевые адаптеры", content: <div><h2>Сетевые адаптеры</h2><p>Описание сетевых адаптеров медиа сервера.</p></div> },
media_software_1_3: { title: "ПО", content: <div><h2>Программное обеспечение медиа сервера</h2><PrometheusChart /></div> },
media_software_2_3: { title: "ПО", content: <div><h2>Программное обеспечение медиа сервера</h2><p>Описание ПО медиа сервера.</p></div> },
media_software_3_3: { title: "ПО", content: <div><h2>Программное обеспечение медиа сервера</h2><p>Описание ПО медиа сервера.</p></div> },
media_software_4_3: { title: "ПО", content: <div><h2>Программное обеспечение медиа сервера</h2><p>Описание ПО медиа сервера.</p></div> },
// Медиа сервер 4
media_system_software_1_4: { title: "Центральный процессор", content: <div><h2>Центральный процессор</h2><p>Описание центрального процессора медиа сервера.</p></div> },
media_system_software_2_4: { title: "Оперативная память", content: <div><h2>Оперативная память</h2><p>Описание оперативной памяти медиа сервера.</p></div> },
media_system_software_3_4: { title: "Жесткий диск", content: <div><h2>Жесткий диск</h2><p>Описание жесткого диска медиа сервера.</p></div> },
media_system_software_4_4: { title: "Сетевые адаптеры", content: <div><h2>Сетевые адаптеры</h2><p>Описание сетевых адаптеров медиа сервера.</p></div> },
media_software_1_4: { title: "ПО", content: <div><h2>Программное обеспечение медиа сервера</h2><PrometheusChart /></div> },
media_software_2_4: { title: "ПО", content: <div><h2>Программное обеспечение медиа сервера</h2><p>Описание ПО медиа сервера.</p></div> },
media_software_3_4: { title: "ПО", content: <div><h2>Программное обеспечение медиа сервера</h2><p>Описание ПО медиа сервера.</p></div> },
media_software_4_4: { title: "ПО", content: <div><h2>Программное обеспечение медиа сервера</h2><p>Описание ПО медиа сервера.</p></div> },
// Медиа сервер 5
media_system_software_1_5: { title: "Центральный процессор", content: <div><h2>Центральный процессор</h2><p>Описание центрального процессора медиа сервера.</p></div> },
media_system_software_2_5: { title: "Оперативная память", content: <div><h2>Оперативная память</h2><p>Описание оперативной памяти медиа сервера.</p></div> },
media_system_software_3_5: { title: "Жесткий диск", content: <div><h2>Жесткий диск</h2><p>Описание жесткого диска медиа сервера.</p></div> },
media_system_software_4_5: { title: "Сетевые адаптеры", content: <div><h2>Сетевые адаптеры</h2><p>Описание сетевых адаптеров медиа сервера.</p></div> },
media_software_1_5: { title: "ПО", content: <div><h2>Программное обеспечение медиа сервера</h2><PrometheusChart /></div> },
media_software_2_5: { title: "ПО", content: <div><h2>Программное обеспечение медиа сервера</h2><p>Описание ПО медиа сервера.</p></div> },
media_software_3_5: { title: "ПО", content: <div><h2>Программное обеспечение медиа сервера</h2><p>Описание ПО медиа сервера.</p></div> },
media_software_4_5: { title: "ПО", content: <div><h2>Программное обеспечение медиа сервера</h2><p>Описание ПО медиа сервера.</p></div> },
// Сервер резервного копирования // Сервер резервного копирования
copy_system_software_1: { title: "Центральный процессор", content: <div><h2>Центральный процессор</h2><p>Описание центрального процессора сервера резервного копирования.</p></div> }, copy_system_software_1: { title: "Центральный процессор", content: <div><h2>Центральный процессор</h2><p>Описание центрального процессора сервера резервного копирования.</p></div> },
copy_system_software_2: { title: "Оперативная память", content: <div><h2>Оперативная память</h2><p>Описание оперативной памяти сервера резервного копирования.</p></div> }, copy_system_software_2: { title: "Оперативная память", content: <div><h2>Оперативная память</h2><p>Описание оперативной памяти сервера резервного копирования.</p></div> },

View File

@ -1,36 +1,76 @@
import React from "react"; import React from "react";
import "../../Style/TreeTable.css"; // Подключаем стили import "../../Style/TreeTable.css";
import { getStatusColor } from "../TreeChart/dataUtils"; // Импортируем функцию
const TreeTable = ({ data }) => { const TreeTable = ({ data }) => {
return ( // Фильтруем данные, чтобы убрать "Функциональные задачи"
<div className="tree-table"> const filteredData = data.filter((item) => item.title !== "Функциональные задачи");
{/* Первый уровень заголовков */}
<div className="tree-table-header">
{data.map((item, index) => (
<div key={index} className="tree-table-column">
<div className="tree-table-title">{item.title}</div>
</div>
))}
</div>
{/* Вложенные элементы */} return (
<div className="tree-table-body"> <div className="table-container">
{data.map((item, index) => ( <table className="tree-table">
<div key={index} className="tree-table-column"> <thead>
{item.items && ( {/* Первый уровень: Заголовки "Медиа сервер" */}
<div className="tree-table-items"> <tr>
{item.items.map((child, childIndex) => ( {filteredData.map((item, index) => (
<div key={childIndex} className="tree-table-item"> <th key={index} colSpan="2" className="tree-table-header" style={{ backgroundColor: getStatusColor(item.status) }}>
{child.title} {item.title}
</div> </th>
))} ))}
</div> </tr>
)} {/* Второй уровень: "АО" и "ПО" */}
</div> <tr>
))} {filteredData.map((item, index) => (
</div> <React.Fragment key={index}>
<td className="tree-table-subheader" style={{ backgroundColor: getStatusColor(item.items[0]?.status) }}>
АО
</td>
<td className="tree-table-subheader" style={{ backgroundColor: getStatusColor(item.items[1]?.status) }}>
ПО
</td>
</React.Fragment>
))}
</tr>
</thead>
<tbody>
{/* Третий уровень: Вложенные элементы "АО" и "ПО" */}
{/*renderRows(filteredData)*/}
</tbody>
</table>
</div> </div>
); );
}; };
// Функция для отображения строк с вложенными элементами
const renderRows = (data) => {
const maxItems = Math.max(
...data.map((item) =>
Math.max(
item.items[0]?.items?.length || 0, // АО
item.items[1]?.items?.length || 0 // ПО
)
)
);
const rows = [];
for (let i = 0; i < maxItems; i++) {
rows.push(
<tr key={i} className="tree-table-row">
{data.map((item, index) => (
<React.Fragment key={index}>
<td className="tree-table-cell" style={{ backgroundColor: getStatusColor(item.items[0]?.items[i]?.status) }}>
{item.items[0]?.items[i]?.title || ""}
</td>
<td className="tree-table-cell" style={{ backgroundColor: getStatusColor(item.items[1]?.items[i]?.status) }}>
{item.items[1]?.items[i]?.title || ""}
</td>
</React.Fragment>
))}
</tr>
);
}
return rows;
};
export default TreeTable; export default TreeTable;

View File

@ -1,67 +1,58 @@
.tree-table { /* Контейнер для таблицы с прокруткой */
max-width: 90vw; /* Ограничение ширины таблицы, чтобы не растягивалась */ .table-container {
min-width: 400px; /* Минимальная ширина для нормального отображения */ width: 100%;
margin: 0 auto; /* Центрирование */ /* Занимает всю доступную ширину */
overflow-x: auto; overflow-x: auto;
padding: 10px; /* Горизонтальная прокрутка при необходимости */
box-sizing: border-box; margin: 0 auto;
/* Центрирование контейнера */
} }
.tree-table-header { /* Стили для таблицы */
display: flex; .tree-table {
justify-content: space-around; width: auto;
width: 100%; /* Автоматическая ширина, чтобы таблица могла расширяться */
padding-bottom: 10px; min-width: 95%;
border-bottom: 2px solid #ccc; /* Минимальная ширина таблицы */
border-collapse: collapse;
margin: 0 auto;
/* Центрирование таблицы */
} }
.tree-table-column { /* Заголовки таблицы (первый уровень) */
flex: 1; .tree-table th {
text-align: center; border: 1px solid #ddd;
min-width: 150px; padding: 8px;
max-width: 100%; text-align: left;
}
.tree-table-title {
font-weight: bold;
font-size: 18px;
color: #333;
}
.tree-table-body {
display: flex;
justify-content: space-around;
width: 100%;
margin-top: 15px;
}
.tree-table-items {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
width: 100%;
}
/* Ограничение по ширине, чтобы элементы не растягивали таблицу */
.tree-table-item {
width: 170px;
/* Фиксированная ширина для всех элементов */
height: 40px;
/* Фиксированная высота */
display: flex;
align-items: center;
justify-content: center;
white-space: nowrap; white-space: nowrap;
overflow: hidden; /* Запрет на перенос текста */
text-overflow: ellipsis; font-weight: bold;
padding: 10px; /* Жирный шрифт для заголовков */
background-color: white;
border-radius: 5px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
} }
.tree-table-item:hover { /* Подзаголовки (второй уровень: "АО" и "ПО") */
transform: scale(1.05); .tree-table-subheader {
background: #f1f1f1; font-weight: 500;
/* Жирный шрифт для подзаголовков */
}
/* Ячейки таблицы */
.tree-table td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
white-space: nowrap;
/* Запрет на перенос текста */
font-weight: normal;
/* Обычный шрифт для ячеек */
}
/* Цвет фона для заголовков */
.tree-table-header {
background-color: #f4f4f4;
}
/* Чередование цвета строк */
.tree-table-row:nth-child(even) {
background-color: #f9f9f9;
} }

View File

@ -5,6 +5,7 @@ import react from '@vitejs/plugin-react'
export default defineConfig({ export default defineConfig({
plugins: [react()], plugins: [react()],
server: { server: {
host: true host: true,
allowedHosts: ['dev.msf.enode']
} }
}) })