Изменил меню, улучшил наполнение вкладок, улучшил графики
parent
90d6565a5a
commit
a01d0972f5
|
|
@ -33,7 +33,7 @@ const DateRangeSelector = ({ onDateChange }) => {
|
|||
};
|
||||
|
||||
return (
|
||||
<div style={{ marginBottom: '20px' }}>
|
||||
<div style={{ marginBottom: '20px', position: 'relative' }}>
|
||||
<DatePicker
|
||||
selectsRange
|
||||
startDate={startDate}
|
||||
|
|
@ -41,6 +41,15 @@ const DateRangeSelector = ({ onDateChange }) => {
|
|||
onChange={handleDateChange}
|
||||
isClearable
|
||||
dateFormat="yyyy-MM-dd"
|
||||
popperPlacement="right-start" // Открывать справа от поля ввода
|
||||
popperModifiers={[
|
||||
{
|
||||
name: 'offset',
|
||||
options: {
|
||||
offset: [0, 10], // Смещение календаря относительно поля ввода
|
||||
},
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<div className="menu-item" style={{ width: sidebarWidth - 20 }}> {/* Динамическая ширина */}
|
||||
<div onClick={handleClick} className="menu-item-header">
|
||||
<div
|
||||
onClick={handleSingleClick} // Одинарный клик для разворачивания/сворачивания или открытия
|
||||
className="menu-item-header"
|
||||
>
|
||||
{/* Круглый индикатор статуса */}
|
||||
<div
|
||||
className={`status-indicator ${statusColor === "red" ? "blinking" : ""}`}
|
||||
style={{ backgroundColor: statusColor }}
|
||||
/>
|
||||
<span>{item.title}</span>
|
||||
|
||||
{/* Иконка для открытия родителя */}
|
||||
{hasChildren && (
|
||||
<span
|
||||
onClick={handleOpenParent}
|
||||
className="open-parent-icon"
|
||||
title="Открыть родителя"
|
||||
>
|
||||
📂
|
||||
</span>
|
||||
)}
|
||||
|
||||
{/* Иконка для разворачивания/сворачивания */}
|
||||
{hasChildren && <span>{isOpen ? "▲" : "▼"}</span>}
|
||||
</div>
|
||||
{isOpen && hasChildren && (
|
||||
|
|
@ -56,4 +79,4 @@ function SidebarMenu({ data, onOpenTab, sidebarWidth }) {
|
|||
);
|
||||
}
|
||||
|
||||
export default SidebarMenu;
|
||||
export default SidebarMenu;
|
||||
|
|
@ -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 = (
|
||||
<div>
|
||||
|
|
@ -38,13 +46,32 @@ const generateTabContent = (data) => {
|
|||
</div>
|
||||
);
|
||||
|
||||
// Если 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 (
|
||||
<div key={id}>
|
||||
<h3>{node.title} - {id}</h3>
|
||||
<Suspense fallback={<div>Загрузка графика...</div>}>
|
||||
<PrometheusChart metricName={metricName} />
|
||||
</Suspense>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
// Получаем метрику для текущего id
|
||||
const metricName = metricMapping[node.id];
|
||||
content = (
|
||||
<div>
|
||||
<h2>{node.title}</h2>
|
||||
<p>Контент для {node.title}.</p>
|
||||
{charts}
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
// Если у узла нет потомков, добавляем график для него
|
||||
|
||||
const metricName = getMetricName(node.id);
|
||||
content = (
|
||||
<div>
|
||||
<h2>{node.title}</h2>
|
||||
|
|
@ -75,4 +102,4 @@ const generateTabContent = (data) => {
|
|||
return tabContent;
|
||||
};
|
||||
|
||||
export default generateTabContent; // Экспортируем только функцию
|
||||
export default tabContent; // Экспортируем только функцию
|
||||
|
|
@ -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(
|
||||
<tr key={i} className="tree-table-row">
|
||||
|
|
@ -72,5 +73,4 @@ const renderRows = (data) => {
|
|||
|
||||
return rows;
|
||||
};
|
||||
|
||||
export default TreeTable;
|
||||
|
|
@ -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;
|
||||
}
|
||||
Loading…
Reference in New Issue