diff --git a/src/Charts/PrometheusChart.jsx b/src/Charts/PrometheusChart.jsx index a80eafb..c4f4bd0 100644 --- a/src/Charts/PrometheusChart.jsx +++ b/src/Charts/PrometheusChart.jsx @@ -14,7 +14,7 @@ const PrometheusChart = ({ metricName }) => { useEffect(() => { const fetchData = async () => { 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; // Проверяем структуру данных diff --git a/src/Components/Layout/Dashboard.jsx b/src/Components/Layout/Dashboard.jsx index 2791d02..8a409a7 100644 --- a/src/Components/Layout/Dashboard.jsx +++ b/src/Components/Layout/Dashboard.jsx @@ -5,20 +5,29 @@ import "../../Style/Dashboard.css"; import ErrorIndicator from "../UI/ErrorIndicator"; import tabContentData from "../TreeChart/tabContent"; import Tabs from "../UI/Tabs"; -import menuData from "../TreeChart//menuData.json"; // Загружаем новое меню -import TableComponent from '../UI/TreeTable'; +import menuData from "../TreeChart/menuData.json"; // Исходные данные меню import TreeTable from "../UI/TreeTable"; - +import { updateStatuses } from "../TreeChart/dataUtils"; // Функция обновления статусов const Dashboard = () => { const [tabs, setTabs] = useState([]); const [activeTab, setActiveTab] = useState("Главная"); const [tabContent, setTabContent] = useState({}); - const [treeData, setTreeData] = useState(null); + const [treeData, setTreeData] = useState(menuData); // Загружаем меню в state + // Обновление treeData каждые 10 секунд useEffect(() => { 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) => { @@ -42,7 +51,7 @@ const Dashboard = () => {

Общий мониторинг

- + {/* Теперь используем актуальные данные */}
); } else if (activeTab === "Визуализация") { @@ -55,8 +64,7 @@ const Dashboard = () => { return (
- - + {/* Передаём обновлённые данные */}
{ ); }; -export default Dashboard; \ No newline at end of file +export default Dashboard; diff --git a/src/Components/Layout/SidebarMenu.jsx b/src/Components/Layout/SidebarMenu.jsx index f32cbaf..6e927eb 100644 --- a/src/Components/Layout/SidebarMenu.jsx +++ b/src/Components/Layout/SidebarMenu.jsx @@ -1,19 +1,6 @@ import React, { useState } from "react"; import "../../Style/SidebarMenu.css"; -import menuData from "../TreeChart/menuData.json"; - -const getStatusColor = (status) => { - switch (status) { - case "green": - return "#4CAF50"; // Зеленый - case "yellow": - return "#FFEB3B"; // Желтый - case "red": - return "#F44336"; // Красный - default: - return "#3d74c7"; // Белый (или любой другой стандартный цвет) - } -}; +import { getStatusColor } from "../TreeChart/dataUtils"; // Импортируем только нужную функцию const MenuItem = ({ item, onSelectItem }) => { const [isOpen, setIsOpen] = useState(false); @@ -45,15 +32,15 @@ const MenuItem = ({ item, onSelectItem }) => { ); }; -function SidebarMenu({ onOpenTab }) { +function SidebarMenu({ data, onOpenTab }) { // Теперь получаем `data` из пропсов const handleSelectItem = (item) => { - onOpenTab(item.id, item.title); // Передаем id и title + onOpenTab(item.id, item.title); }; return (

Меню

- +
); } diff --git a/src/Components/TreeChart/TreeChart.jsx b/src/Components/TreeChart/TreeChart.jsx index fc61b48..51c05b6 100644 --- a/src/Components/TreeChart/TreeChart.jsx +++ b/src/Components/TreeChart/TreeChart.jsx @@ -1,71 +1,75 @@ import React, { useRef, useEffect } from "react"; import * as d3 from "d3"; +import { getStatusColor } from "./dataUtils"; const TreeChart = ({ data, onNodeClick }) => { const chartRef = useRef(); useEffect(() => { - if (!data) return; + if (!data || !data.items) return; - // Очищаем старый граф перед отрисовкой - d3.select(chartRef.current).selectAll("*").remove(); - - const width = 928; + const width = 1000; const height = 1000; const root = d3.hierarchy(data, (d) => d.items); const links = root.links(); const nodes = root.descendants(); - 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()); + const svg = d3.select(chartRef.current); + svg.selectAll("*").remove(); - const svg = d3 - .select(chartRef.current) + svg .attr("width", width) .attr("height", height) .attr("viewBox", [-width / 2, -height / 2, width, height]) .attr("style", "max-width: 100%; height: auto;"); - const link = svg - .append("g") + const link = svg.append("g") .attr("stroke", "#999") .attr("stroke-opacity", 0.6) .selectAll("line") .data(links) - .join("line"); + .join("line"); // Используем join для обновления связей - const node = svg - .append("g") + const node = svg.append("g") .attr("stroke", "#000") .attr("stroke-width", 1.5) .selectAll("circle") .data(nodes) - .join("circle") - .attr("fill", (d) => { - // Окрашиваем узлы в зависимости от статуса - switch (d.data.status) { - case "green": - return "#4CAF50"; // Зеленый - case "yellow": - return "#FFEB3B"; // Желтый - case "red": - return "#F44336"; // Красный - default: - return "#555"; // Серый по умолчанию - } - }) + .join("circle") // Используем join для обновления узлов + .attr("fill", (d) => getStatusColor(d.data.status)) .attr("stroke", "#fff") .attr("r", 7) - .call(drag(simulation)); + .call(drag()); - // Добавляем текстовые подписи - const text = svg - .append("g") + // Обновляем только те узлы, которые нуждаются в изменении + node.each(function (d) { + 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("font-family", "Arial") .attr("font-size", 12) @@ -81,10 +85,16 @@ const TreeChart = ({ data, onNodeClick }) => { node.on("click", (event, d) => { 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", () => { link .attr("x1", (d) => d.source.x) @@ -101,14 +111,11 @@ const TreeChart = ({ data, onNodeClick }) => { .attr("y", (d) => d.y + 4); }); - return () => { - simulation.stop(); - }; + return () => simulation.stop(); }, [data, onNodeClick]); - const drag = (simulation) => { + const drag = () => { function dragstarted(event, d) { - if (!event.active) simulation.alphaTarget(0.3).restart(); d.fx = d.x; d.fy = d.y; } @@ -119,7 +126,6 @@ const TreeChart = ({ data, onNodeClick }) => { } function dragended(event, d) { - if (!event.active) simulation.alphaTarget(0); d.fx = null; d.fy = null; } @@ -130,4 +136,4 @@ const TreeChart = ({ data, onNodeClick }) => { return ; }; -export default TreeChart; \ No newline at end of file +export default TreeChart; diff --git a/src/Components/TreeChart/dataUtils.jsx b/src/Components/TreeChart/dataUtils.jsx new file mode 100644 index 0000000..c506f21 --- /dev/null +++ b/src/Components/TreeChart/dataUtils.jsx @@ -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 }; \ No newline at end of file diff --git a/src/Components/TreeChart/menuData.json b/src/Components/TreeChart/menuData.json index aff02c9..9e9a2e8 100644 --- a/src/Components/TreeChart/menuData.json +++ b/src/Components/TreeChart/menuData.json @@ -1,35 +1,28 @@ { "title": "Сервис ВКС", - "status": "red", "items": [ { "title": "Функциональные задачи", - "status": "red", "items": [ { "id": "system_control", - "title": "Контроль системы", - "status": "red" + "title": "Контроль системы" }, { "id": "system_management", - "title": "Система управления", - "status": "green" + "title": "Система управления" }, { "id": "conference", - "title": "Проведение ВКС", - "status": "green" + "title": "Проведение ВКС" }, { "id": "backup", - "title": "Резервное копирование", - "status": "green" + "title": "Резервное копирование" }, { "id": "relay_info", - "title": "Ретрансляция информации", - "status": "green" + "title": "Ретрансляция информации" } ] }, @@ -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": [ { "title": "Аппаратное обеспечение", @@ -128,7 +309,7 @@ ] }, { - "title": "Сервер системы управления", + "title": "Сервер систем", "items": [ { "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": "ПО" - } - ] - } - ] } ] } \ No newline at end of file diff --git a/src/Components/TreeChart/tabContent.jsx b/src/Components/TreeChart/tabContent.jsx index 922e777..47dc890 100644 --- a/src/Components/TreeChart/tabContent.jsx +++ b/src/Components/TreeChart/tabContent.jsx @@ -12,7 +12,7 @@ const tabContent = { backup: { title: "Резервное копирование", content:

Резервное копирование

Процесс резервного копирования.

}, relay_info: { title: "Ретрансляция информации", content:

Ретрансляция информации

Детали ретрансляции.

}, - // Медиа сервер + // Медиа сервер 1 media_system_software_1: { title: "Центральный процессор", content:

Центральный процессор

Описание центрального процессора медиа сервера.

}, media_system_software_2: { title: "Оперативная память", content:

Оперативная память

Описание оперативной памяти медиа сервера.

}, media_system_software_3: { title: "Жесткий диск", content:

Жесткий диск

Описание жесткого диска медиа сервера.

}, @@ -22,6 +22,46 @@ const tabContent = { media_software_3: { title: "ПО", content:

Программное обеспечение медиа сервера

Описание ПО медиа сервера.

}, media_software_4: { title: "ПО", content:

Программное обеспечение медиа сервера

Описание ПО медиа сервера.

}, + // Медиа сервер 2 + media_system_software_1_2: { title: "Центральный процессор", content:

Центральный процессор

Описание центрального процессора медиа сервера.

}, + media_system_software_2_2: { title: "Оперативная память", content:

Оперативная память

Описание оперативной памяти медиа сервера.

}, + media_system_software_3_2: { title: "Жесткий диск", content:

Жесткий диск

Описание жесткого диска медиа сервера.

}, + media_system_software_4_2: { title: "Сетевые адаптеры", content:

Сетевые адаптеры

Описание сетевых адаптеров медиа сервера.

}, + media_software_1_2: { title: "ПО", content:

Программное обеспечение медиа сервера

}, + media_software_2_2: { title: "ПО", content:

Программное обеспечение медиа сервера

Описание ПО медиа сервера.

}, + media_software_3_2: { title: "ПО", content:

Программное обеспечение медиа сервера

Описание ПО медиа сервера.

}, + media_software_4_2: { title: "ПО", content:

Программное обеспечение медиа сервера

Описание ПО медиа сервера.

}, + + // Медиа сервер 3 + media_system_software_1_3: { title: "Центральный процессор", content:

Центральный процессор

Описание центрального процессора медиа сервера.

}, + media_system_software_2_3: { title: "Оперативная память", content:

Оперативная память

Описание оперативной памяти медиа сервера.

}, + media_system_software_3_3: { title: "Жесткий диск", content:

Жесткий диск

Описание жесткого диска медиа сервера.

}, + media_system_software_4_3: { title: "Сетевые адаптеры", content:

Сетевые адаптеры

Описание сетевых адаптеров медиа сервера.

}, + media_software_1_3: { title: "ПО", content:

Программное обеспечение медиа сервера

}, + media_software_2_3: { title: "ПО", content:

Программное обеспечение медиа сервера

Описание ПО медиа сервера.

}, + media_software_3_3: { title: "ПО", content:

Программное обеспечение медиа сервера

Описание ПО медиа сервера.

}, + media_software_4_3: { title: "ПО", content:

Программное обеспечение медиа сервера

Описание ПО медиа сервера.

}, + + // Медиа сервер 4 + media_system_software_1_4: { title: "Центральный процессор", content:

Центральный процессор

Описание центрального процессора медиа сервера.

}, + media_system_software_2_4: { title: "Оперативная память", content:

Оперативная память

Описание оперативной памяти медиа сервера.

}, + media_system_software_3_4: { title: "Жесткий диск", content:

Жесткий диск

Описание жесткого диска медиа сервера.

}, + media_system_software_4_4: { title: "Сетевые адаптеры", content:

Сетевые адаптеры

Описание сетевых адаптеров медиа сервера.

}, + media_software_1_4: { title: "ПО", content:

Программное обеспечение медиа сервера

}, + media_software_2_4: { title: "ПО", content:

Программное обеспечение медиа сервера

Описание ПО медиа сервера.

}, + media_software_3_4: { title: "ПО", content:

Программное обеспечение медиа сервера

Описание ПО медиа сервера.

}, + media_software_4_4: { title: "ПО", content:

Программное обеспечение медиа сервера

Описание ПО медиа сервера.

}, + + // Медиа сервер 5 + media_system_software_1_5: { title: "Центральный процессор", content:

Центральный процессор

Описание центрального процессора медиа сервера.

}, + media_system_software_2_5: { title: "Оперативная память", content:

Оперативная память

Описание оперативной памяти медиа сервера.

}, + media_system_software_3_5: { title: "Жесткий диск", content:

Жесткий диск

Описание жесткого диска медиа сервера.

}, + media_system_software_4_5: { title: "Сетевые адаптеры", content:

Сетевые адаптеры

Описание сетевых адаптеров медиа сервера.

}, + media_software_1_5: { title: "ПО", content:

Программное обеспечение медиа сервера

}, + media_software_2_5: { title: "ПО", content:

Программное обеспечение медиа сервера

Описание ПО медиа сервера.

}, + media_software_3_5: { title: "ПО", content:

Программное обеспечение медиа сервера

Описание ПО медиа сервера.

}, + media_software_4_5: { title: "ПО", content:

Программное обеспечение медиа сервера

Описание ПО медиа сервера.

}, + // Сервер резервного копирования copy_system_software_1: { title: "Центральный процессор", content:

Центральный процессор

Описание центрального процессора сервера резервного копирования.

}, copy_system_software_2: { title: "Оперативная память", content:

Оперативная память

Описание оперативной памяти сервера резервного копирования.

}, diff --git a/src/Components/UI/TreeTable.jsx b/src/Components/UI/TreeTable.jsx index 60a52d3..9f392d4 100644 --- a/src/Components/UI/TreeTable.jsx +++ b/src/Components/UI/TreeTable.jsx @@ -1,36 +1,76 @@ import React from "react"; -import "../../Style/TreeTable.css"; // Подключаем стили +import "../../Style/TreeTable.css"; +import { getStatusColor } from "../TreeChart/dataUtils"; // Импортируем функцию const TreeTable = ({ data }) => { - return ( -
- {/* Первый уровень заголовков */} -
- {data.map((item, index) => ( -
-
{item.title}
-
- ))} -
+ // Фильтруем данные, чтобы убрать "Функциональные задачи" + const filteredData = data.filter((item) => item.title !== "Функциональные задачи"); - {/* Вложенные элементы */} -
- {data.map((item, index) => ( -
- {item.items && ( -
- {item.items.map((child, childIndex) => ( -
- {child.title} -
- ))} -
- )} -
- ))} -
+ return ( +
+ + + {/* Первый уровень: Заголовки "Медиа сервер" */} + + {filteredData.map((item, index) => ( + + ))} + + {/* Второй уровень: "АО" и "ПО" */} + + {filteredData.map((item, index) => ( + + + + + ))} + + + + {/* Третий уровень: Вложенные элементы "АО" и "ПО" */} + {/*renderRows(filteredData)*/} + +
+ {item.title} +
+ АО + + ПО +
); }; -export default TreeTable; +// Функция для отображения строк с вложенными элементами +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( + + {data.map((item, index) => ( + + + {item.items[0]?.items[i]?.title || ""} + + + {item.items[1]?.items[i]?.title || ""} + + + ))} + + ); + } + + return rows; +}; + +export default TreeTable; \ No newline at end of file diff --git a/src/Style/TreeTable.css b/src/Style/TreeTable.css index 4549aee..d2093b1 100644 --- a/src/Style/TreeTable.css +++ b/src/Style/TreeTable.css @@ -1,67 +1,58 @@ -.tree-table { - max-width: 90vw; /* Ограничение ширины таблицы, чтобы не растягивалась */ - min-width: 400px; /* Минимальная ширина для нормального отображения */ - margin: 0 auto; /* Центрирование */ +/* Контейнер для таблицы с прокруткой */ +.table-container { + width: 100%; + /* Занимает всю доступную ширину */ overflow-x: auto; - padding: 10px; - box-sizing: border-box; + /* Горизонтальная прокрутка при необходимости */ + margin: 0 auto; + /* Центрирование контейнера */ } -.tree-table-header { - display: flex; - justify-content: space-around; - width: 100%; - padding-bottom: 10px; - border-bottom: 2px solid #ccc; +/* Стили для таблицы */ +.tree-table { + width: auto; + /* Автоматическая ширина, чтобы таблица могла расширяться */ + min-width: 95%; + /* Минимальная ширина таблицы */ + border-collapse: collapse; + margin: 0 auto; + /* Центрирование таблицы */ } -.tree-table-column { - flex: 1; - text-align: center; - min-width: 150px; - max-width: 100%; -} - -.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; +/* Заголовки таблицы (первый уровень) */ +.tree-table th { + border: 1px solid #ddd; + padding: 8px; + text-align: left; white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - padding: 10px; - background-color: white; - border-radius: 5px; - box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); + /* Запрет на перенос текста */ + font-weight: bold; + /* Жирный шрифт для заголовков */ } -.tree-table-item:hover { - transform: scale(1.05); - background: #f1f1f1; +/* Подзаголовки (второй уровень: "АО" и "ПО") */ +.tree-table-subheader { + 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; } \ No newline at end of file diff --git a/vite.config.js b/vite.config.js index 6bb98ac..b034150 100755 --- a/vite.config.js +++ b/vite.config.js @@ -5,6 +5,7 @@ import react from '@vitejs/plugin-react' export default defineConfig({ plugins: [react()], server: { - host: true + host: true, + allowedHosts: ['dev.msf.enode'] } })