From a01d0972f53475a8e7a604f6f8aad6cf23b85415 Mon Sep 17 00:00:00 2001 From: DmitriyA Date: Thu, 6 Mar 2025 03:29:46 -0500 Subject: [PATCH] =?UTF-8?q?=D0=98=D0=B7=D0=BC=D0=B5=D0=BD=D0=B8=D0=BB=20?= =?UTF-8?q?=D0=BC=D0=B5=D0=BD=D1=8E,=20=D1=83=D0=BB=D1=83=D1=87=D1=88?= =?UTF-8?q?=D0=B8=D0=BB=20=D0=BD=D0=B0=D0=BF=D0=BE=D0=BB=D0=BD=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D0=B5=20=D0=B2=D0=BA=D0=BB=D0=B0=D0=B4=D0=BE=D0=BA,=20?= =?UTF-8?q?=D1=83=D0=BB=D1=83=D1=87=D1=88=D0=B8=D0=BB=20=D0=B3=D1=80=D0=B0?= =?UTF-8?q?=D1=84=D0=B8=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Charts/PrometheusChart.jsx | 11 +- src/Components/Layout/SidebarMenu.jsx | 33 +++++- src/Components/TreeChart/tabContent.jsx | 65 +++++++---- src/Components/UI/TreeTable.jsx | 16 +-- src/Style/DatePicker.css | 139 ++++++++++++++++++------ 5 files changed, 196 insertions(+), 68 deletions(-) diff --git a/src/Charts/PrometheusChart.jsx b/src/Charts/PrometheusChart.jsx index a5fc8e5..f6ce9be 100644 --- a/src/Charts/PrometheusChart.jsx +++ b/src/Charts/PrometheusChart.jsx @@ -33,7 +33,7 @@ const DateRangeSelector = ({ onDateChange }) => { }; return ( -
+
{ onChange={handleDateChange} isClearable dateFormat="yyyy-MM-dd" + popperPlacement="right-start" // Открывать справа от поля ввода + popperModifiers={[ + { + name: 'offset', + options: { + offset: [0, 10], // Смещение календаря относительно поля ввода + }, + }, + ]} />
); diff --git a/src/Components/Layout/SidebarMenu.jsx b/src/Components/Layout/SidebarMenu.jsx index c9300ec..5661716 100644 --- a/src/Components/Layout/SidebarMenu.jsx +++ b/src/Components/Layout/SidebarMenu.jsx @@ -7,23 +7,46 @@ const MenuItem = ({ item, onSelectItem, sidebarWidth }) => { const hasChildren = Array.isArray(item.items) && item.items.length > 0; const statusColor = getStatusColor(item.status); - const handleClick = () => { + // Обработчик одинарного клика (разворачивание/сворачивание или открытие элемента) + const handleSingleClick = () => { if (hasChildren) { - setIsOpen(!isOpen); + setIsOpen(!isOpen); // Разворачиваем/сворачиваем дочерние элементы } else { - onSelectItem(item); + onSelectItem(item); // Если нет потомков, открываем элемент как вкладку } }; + // Обработчик клика для открытия родителя + const handleOpenParent = (e) => { + e.stopPropagation(); // Останавливаем всплытие события, чтобы не сработал handleSingleClick + onSelectItem(item); // Открываем родителя + }; + return (
{/* Динамическая ширина */} -
+
{/* Круглый индикатор статуса */}
{item.title} + + {/* Иконка для открытия родителя */} + {hasChildren && ( + + 📂 + + )} + + {/* Иконка для разворачивания/сворачивания */} {hasChildren && {isOpen ? "▲" : "▼"}}
{isOpen && hasChildren && ( @@ -56,4 +79,4 @@ function SidebarMenu({ data, onOpenTab, sidebarWidth }) { ); } -export default SidebarMenu; +export default SidebarMenu; \ No newline at end of file diff --git a/src/Components/TreeChart/tabContent.jsx b/src/Components/TreeChart/tabContent.jsx index eecfcc2..26ab955 100644 --- a/src/Components/TreeChart/tabContent.jsx +++ b/src/Components/TreeChart/tabContent.jsx @@ -2,34 +2,42 @@ import React, { lazy, Suspense } from "react"; const PrometheusChart = lazy(() => import('../../Charts/PrometheusChart')); -// Вкладки, для которых нужно отобразить график -const tabsWithCharts = ["188", "189"]; - -// Маппинг id на метрики -const metricMapping = { - 188: "zvks_apiforsnmp_measure_277", - 189: "zvks_apiforsnmp_measure_36", +// Функция для генерации названия метрики на основе id +const getMetricName = (id) => { + return `zvks_apiforsnmp_measure_${id}`; }; -const generateTabContent = (data) => { +// Функция для рекурсивного сбора всех id потомков +const getAllChildIds = (node) => { + let ids = []; + if (node.id) { + ids.push(node.id); // Добавляем id текущего узла + } + if (node.items && node.items.length > 0) { + node.items.forEach((child) => { + ids = ids.concat(getAllChildIds(child)); // Рекурсивно собираем id потомков + }); + } + return ids; +}; + +const tabContent = (data) => { const tabContent = {}; - console.log("jsonData:", data); - console.log("jsonData.items:", data.items); const generateContent = (nodes) => { nodes.forEach((node) => { - console.log("Обрабатываем узел:", node); + // Если у узла есть вложенные элементы, рекурсивно обрабатываем их if (node.items && node.items.length > 0) { - console.log("Идём вглубь:", node.items); + generateContent(node.items); } // Если у узла есть id, добавляем его в tabContent if (node.id) { - console.log("Добавляем в tabContent:", node.id); + let content = (
@@ -38,13 +46,32 @@ const generateTabContent = (data) => {
); - // Если id узла есть в списке tabsWithCharts, добавляем график - if (tabsWithCharts.includes(node.id)) { - console.log("Добавляем график для:", node.id); + // Если у узла есть потомки, добавляем графики для всех потомков + if (node.items && node.items.length > 0) { + const childIds = getAllChildIds(node); // Получаем все id потомков + const charts = childIds.map((id) => { + const metricName = getMetricName(id); + return ( +
+

{node.title} - {id}

+ Загрузка графика...
}> + + +
+ ); + }); - // Получаем метрику для текущего id - const metricName = metricMapping[node.id]; + content = ( +
+

{node.title}

+

Контент для {node.title}.

+ {charts} +
+ ); + } else { + // Если у узла нет потомков, добавляем график для него + const metricName = getMetricName(node.id); content = (

{node.title}

@@ -75,4 +102,4 @@ const generateTabContent = (data) => { return tabContent; }; -export default generateTabContent; // Экспортируем только функцию \ No newline at end of file +export default tabContent; // Экспортируем только функцию \ No newline at end of file diff --git a/src/Components/UI/TreeTable.jsx b/src/Components/UI/TreeTable.jsx index 8a4618e..28b2105 100644 --- a/src/Components/UI/TreeTable.jsx +++ b/src/Components/UI/TreeTable.jsx @@ -43,16 +43,17 @@ const TreeTable = ({ data }) => { // Функция для отображения строк с вложенными элементами const renderRows = (data) => { + const rows = []; + + // Находим максимальное количество элементов среди всех "АО" и "ПО" const maxItems = Math.max( - ...data.map((item) => - Math.max( - item.items[0]?.items?.length || 0, // АО - item.items[1]?.items?.length || 0 // ПО - ) - ) + ...data.flatMap((item) => [ + item.items[0]?.items?.length || 0, // АО + item.items[1]?.items?.length || 0 // ПО + ]) ); - const rows = []; + // Генерируем строки for (let i = 0; i < maxItems; i++) { rows.push( @@ -72,5 +73,4 @@ const renderRows = (data) => { return rows; }; - export default TreeTable; \ No newline at end of file diff --git a/src/Style/DatePicker.css b/src/Style/DatePicker.css index 94a43f5..3b564d1 100644 --- a/src/Style/DatePicker.css +++ b/src/Style/DatePicker.css @@ -1,60 +1,129 @@ +/* DatePicker.css */ + .react-datepicker-wrapper { - width: 100%; + width: auto; + display: inline-block; +} + +.react-datepicker__input-container input { + width: 200px; + padding: 8px 12px; + border: 1px solid #ddd; + border-radius: 6px; + font-size: 14px; + color: #333; + background-color: #fff; + transition: border-color 0.2s ease; +} + +.react-datepicker__input-container input:focus { + border-color: #0078d4; + outline: none; } .react-datepicker { - position: absolute !important; - z-index: 1000 !important; - width: auto; - max-width: 300px; - background-color: white; - border: 1px solid #ccc; - border-radius: 4px; - box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); + font-family: 'Segoe UI', sans-serif; + border: 1px solid #e0e0e0; + border-radius: 8px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + background-color: #fff; + /* Непрозрачный фон */ + z-index: 1000; + /* Календарь поверх других элементов */ +} + +.react-datepicker-popper { + z-index: 1000; + /* Календарь поверх других элементов */ + pointer-events: auto; + /* Разрешить взаимодействие только с календарем */ } .react-datepicker__header { - background-color: #f0f0f0; - border-bottom: 1px solid #ccc; - border-radius: 4px 4px 0 0; - padding: 8px; + background-color: #f8f8f8; + /* Непрозрачный фон заголовка */ + border-bottom: 1px solid #e0e0e0; + border-radius: 8px 8px 0 0; + padding: 12px; } .react-datepicker__current-month { - font-size: 1.1em; - font-weight: bold; + font-size: 14px; + font-weight: 600; + color: #333; +} + +.react-datepicker__navigation { + top: 12px; + border: 0.45rem solid transparent; +} + +.react-datepicker__navigation--previous { + left: 12px; + border-right-color: #666; +} + +.react-datepicker__navigation--next { + right: 12px; + border-left-color: #666; +} + +.react-datepicker__day-names { + display: flex; + justify-content: space-between; + padding: 0 8px; + margin-top: 8px; +} + +.react-datepicker__day-name { + width: 28px; + line-height: 28px; + text-align: center; + font-size: 12px; + color: #666; +} + +.react-datepicker__month { + background-color: #fff; + /* Непрозрачный фон месяца */ + margin: 0; + padding: 8px; +} + +.react-datepicker__week { + display: flex; + justify-content: space-between; } .react-datepicker__day { - padding: 5px; - margin: 2px; - border-radius: 4px; -} - -.react-datepicker__day--selected, -.react-datepicker__day--keyboard-selected { - background-color: #3e95cd; - color: white; + width: 28px; + line-height: 28px; + text-align: center; + font-size: 12px; + color: #333; + cursor: pointer; + border-radius: 50%; + transition: background-color 0.2s ease, color 0.2s ease; } .react-datepicker__day:hover { background-color: #f0f0f0; } -.react-datepicker__navigation { - top: 10px; +.react-datepicker__day--selected { + background-color: #0078d4; + color: #fff; } -.react-datepicker__navigation--previous { - border-right-color: #333; +.react-datepicker__day--selected:hover { + background-color: #005bb5; } -.react-datepicker__navigation--next { - border-left-color: #333; +.react-datepicker__day--outside-month { + color: #ccc; } -/* Исправление странного появления в центре экрана */ -.react-datepicker-popper { - z-index: 9999 !important; - transform: translate3d(0px, 0px, 0px) !important; -} +.react-datepicker__day--disabled { + color: #ccc; + cursor: not-allowed; +} \ No newline at end of file