Обновил структуру, добавил графики, улучшил интерфейс
parent
01f0be9cdd
commit
46a8bbb35d
|
|
@ -11,7 +11,9 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1"
|
||||
"react-dom": "^18.3.1",
|
||||
"chart.js": "^4.0.0",
|
||||
"react-chartjs-2": "^5.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.17.0",
|
||||
|
|
@ -25,4 +27,4 @@
|
|||
"globals": "^15.14.0",
|
||||
"vite": "^6.0.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
25
src/App.jsx
25
src/App.jsx
|
|
@ -1,13 +1,32 @@
|
|||
import React from "react";
|
||||
import SidebarMenu from "./SidebarMenu/SidebarMenu"; // Импорт компонента бокового меню
|
||||
|
||||
import CpuTemperatureChart from './Charts/CpuTemperatureChart';
|
||||
import ErrorIndicator from "./SidebarMenu/ErrorIndicator"; // Индикатор ошибок
|
||||
import GpuTemperatureChart from './Charts/GpuTemperatureChart';
|
||||
import RamUsageChart from './Charts/RamUsageChart'
|
||||
import Dashboard from "./SidebarMenu/Dashboard";
|
||||
function App() {
|
||||
return (
|
||||
<div style={{ display: "flex" }}>
|
||||
<SidebarMenu />
|
||||
<div style={{ padding: "20px", flex: 1 }}>Рабочая область</div>
|
||||
<div style={{ marginLeft: "250px" }}>
|
||||
<h1 className="text-xl font-bold">Мониторинг состояния системы</h1>
|
||||
<div className="indicator-container">
|
||||
{/* Индикатор ошибок */}
|
||||
<h2 className="sidebar-indicator">Индикатор ошибок: </h2>
|
||||
<ErrorIndicator criticalCount={3} warningCount={7} />
|
||||
</div>
|
||||
{/* График температуры CPU
|
||||
<div className="flex justify-center items-center h-96 bg-gray-100">
|
||||
<CpuTemperatureChart />
|
||||
<GpuTemperatureChart />
|
||||
<RamUsageChart />
|
||||
</div> */}
|
||||
<Dashboard/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
export default App;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,75 @@
|
|||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { Line } from "react-chartjs-2";
|
||||
import {
|
||||
Chart as ChartJS,
|
||||
LineElement,
|
||||
PointElement,
|
||||
LinearScale,
|
||||
CategoryScale,
|
||||
} from "chart.js";
|
||||
import ExpandableInfo from "../SidebarMenu/ExpandableInfo"
|
||||
|
||||
ChartJS.register(LineElement, PointElement, LinearScale, CategoryScale);
|
||||
|
||||
const CpuTemperatureChart = () => {
|
||||
const chartRef = useRef(null);
|
||||
const [data, setData] = useState({
|
||||
labels: Array(10).fill("").map((_, i) => i), // 20 точек по X
|
||||
datasets: [
|
||||
{
|
||||
label: "Температура CPU (°C)",
|
||||
data: Array(20).fill(50), // Начальные значения (например, 50°C)
|
||||
borderColor: "red",
|
||||
borderWidth: 2,
|
||||
fill: false,
|
||||
cubicInterpolationMode: "monotone", // Сглаживание
|
||||
tension: 0.4, // Делаем линию плавнее
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
setData((prevData) => {
|
||||
const newTemp = Math.floor(Math.random() * 20) + 50; // Генерируем новую температуру (50-70°C)
|
||||
const newLabels = [...prevData.labels.slice(1), prevData.labels[prevData.labels.length - 1] + 1]; // Сдвигаем ось X
|
||||
const newDataset = [...prevData.datasets[0].data.slice(1), newTemp]; // Сдвигаем данные влево
|
||||
|
||||
return {
|
||||
labels: newLabels,
|
||||
datasets: [{ ...prevData.datasets[0], data: newDataset }],
|
||||
};
|
||||
});
|
||||
}, 1000); // Обновление каждую секунду
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
// Пример данных для меню "Подробнее"
|
||||
const details = [
|
||||
{ label: "Использование", value: " 45%" },
|
||||
{ label: "Скорость", value: " 3.6 GHz" },
|
||||
{ label: "Процессы", value: " 120" },
|
||||
{ label: "Время работы", value: " 2 ч 30 мин" },
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="w-full max-w-2xl mx-auto p-4 flex flex-col">
|
||||
<h2 className="text-xl font-semibold mb-4">График температуры ЦП</h2>
|
||||
<Line
|
||||
ref={chartRef}
|
||||
data={data}
|
||||
options={{
|
||||
animation: false, // Отключаем анимацию обновления (чтобы был плавный сдвиг)
|
||||
scales: {
|
||||
x: { display: true },
|
||||
y: { min: 30, max: 80 }, // Ограничиваем Y (например, 30-80°C)
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<ExpandableInfo details={details} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CpuTemperatureChart;
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { Line } from "react-chartjs-2";
|
||||
import {
|
||||
Chart as ChartJS,
|
||||
LineElement,
|
||||
PointElement,
|
||||
LinearScale,
|
||||
CategoryScale,
|
||||
} from "chart.js";
|
||||
import ExpandableInfo from "../SidebarMenu/ExpandableInfo"
|
||||
|
||||
ChartJS.register(LineElement, PointElement, LinearScale, CategoryScale);
|
||||
|
||||
const GpuTemperatureChart = () => {
|
||||
const chartRef = useRef(null);
|
||||
const [data, setData] = useState({
|
||||
labels: Array(10).fill("").map((_, i) => i), // 20 точек по X
|
||||
datasets: [
|
||||
{
|
||||
label: "Температура GPU (°C)",
|
||||
data: Array(20).fill(50), // Начальные значения (например, 50°C)
|
||||
borderColor: "blue",
|
||||
borderWidth: 2,
|
||||
fill: false,
|
||||
cubicInterpolationMode: "monotone", // Сглаживание
|
||||
tension: 0.4, // Делаем линию плавнее
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
setData((prevData) => {
|
||||
const newTemp = Math.floor(Math.random() * 20) + 40; // Генерируем новую температуру (50-600°C)
|
||||
const newLabels = [...prevData.labels.slice(1), prevData.labels[prevData.labels.length - 1] + 1]; // Сдвигаем ось X
|
||||
const newDataset = [...prevData.datasets[0].data.slice(1), newTemp]; // Сдвигаем данные влево
|
||||
|
||||
return {
|
||||
labels: newLabels,
|
||||
datasets: [{ ...prevData.datasets[0], data: newDataset }],
|
||||
};
|
||||
});
|
||||
}, 1000); // Обновление каждую секунду
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
// Пример данных для меню "Подробнее"
|
||||
const details = [
|
||||
{ label: "Использование", value: " 20%" },
|
||||
{ label: "Оперативная память ГП", value: " 1,2/7,9 ГБ" },
|
||||
{ label: "Общая память ГП", value: " 1,2/7,9 ГБ" },
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="w-full max-w-2xl mx-auto p-4 flex flex-col">
|
||||
<h2 className="text-xl font-semibold mb-4">График температуры ГП</h2>
|
||||
<Line
|
||||
ref={chartRef}
|
||||
data={data}
|
||||
options={{
|
||||
animation: false, // Отключаем анимацию обновления (чтобы был плавный сдвиг)
|
||||
scales: {
|
||||
x: { display: true },
|
||||
y: { min: 30, max: 80 }, // Ограничиваем Y (например, 30-80°C)
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<ExpandableInfo details={details} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default GpuTemperatureChart;
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { Line } from "react-chartjs-2";
|
||||
import {
|
||||
Chart as ChartJS,
|
||||
LineElement,
|
||||
PointElement,
|
||||
LinearScale,
|
||||
CategoryScale,
|
||||
} from "chart.js";
|
||||
import ExpandableInfo from "../SidebarMenu/ExpandableInfo"
|
||||
|
||||
ChartJS.register(LineElement, PointElement, LinearScale, CategoryScale);
|
||||
|
||||
const RamUsageChart = () => {
|
||||
const chartRef = useRef(null);
|
||||
const [data, setData] = useState({
|
||||
labels: Array(10).fill("").map((_, i) => i), // 20 точек по X
|
||||
datasets: [
|
||||
{
|
||||
label: "Загруженность RAM (%)",
|
||||
data: Array(20).fill(50), // Начальные значения (например, 50%)
|
||||
borderColor: "green",
|
||||
borderWidth: 2,
|
||||
fill: false,
|
||||
cubicInterpolationMode: "monotone", // Сглаживание
|
||||
tension: 0.4, // Делаем линию плавнее
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
setData((prevData) => {
|
||||
const newTemp = Math.floor(Math.random() * 20) + 40; // Генерируем новую температуру (50-600°C)
|
||||
const newLabels = [...prevData.labels.slice(1), prevData.labels[prevData.labels.length - 1] + 1]; // Сдвигаем ось X
|
||||
const newDataset = [...prevData.datasets[0].data.slice(1), newTemp]; // Сдвигаем данные влево
|
||||
|
||||
return {
|
||||
labels: newLabels,
|
||||
datasets: [{ ...prevData.datasets[0], data: newDataset }],
|
||||
};
|
||||
});
|
||||
}, 1000); // Обновление каждую секунду
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
// Пример данных для меню "Подробнее"
|
||||
const details = [
|
||||
{ label: "Используется", value: " 6,2 ГБ" },
|
||||
{ label: "Доступно", value: " 9,5 ГБ" },
|
||||
{ label: "Выделено", value: " 6,8/18,2 ГБ" },
|
||||
{ label: "Скорость", value: " 3200 МГц" },
|
||||
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="w-full max-w-2xl mx-auto p-4 flex flex-col">
|
||||
<h2 className="text-xl font-semibold mb-4">График загруженности ОЗУ</h2>
|
||||
<Line
|
||||
ref={chartRef}
|
||||
data={data}
|
||||
options={{
|
||||
animation: false, // Отключаем анимацию обновления (чтобы был плавный сдвиг)
|
||||
scales: {
|
||||
x: { display: true },
|
||||
y: { min: 0, max: 100 }, // Ограничиваем Y (например, 30-80°C)
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<ExpandableInfo details={details} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default RamUsageChart;
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
import React from "react";
|
||||
import CpuTemperatureChart from "../Charts/CpuTemperatureChart";
|
||||
import GpuTemperatureChart from "../Charts/GpuTemperatureChart";
|
||||
import RamUsageChart from "../Charts/RamUsageChart";
|
||||
|
||||
import "../Style/Dashboard.css"; // Подключаем стили
|
||||
|
||||
const Dashboard = () => {
|
||||
return (
|
||||
<div className="dashboard-container">
|
||||
{/* Левая колонка (Графики) */}
|
||||
<div className="left-column">
|
||||
<CpuTemperatureChart />
|
||||
<GpuTemperatureChart /> {/* Можно заменить на другие графики */}
|
||||
<RamUsageChart />
|
||||
</div>
|
||||
|
||||
{/* Правая колонка (Информационный блок) */}
|
||||
<div className="right-column">
|
||||
<h2>Информационный блок</h2>
|
||||
<p>Здесь можно выводить любые данные о системе.</p>
|
||||
|
||||
<div className="info-item">
|
||||
<span className="label">Температура CPU:</span>
|
||||
<span className="value">65°C</span>
|
||||
</div>
|
||||
|
||||
<div className="info-item">
|
||||
<span className="label">Загрузка процессора:</span>
|
||||
<span className="value">45%</span>
|
||||
</div>
|
||||
|
||||
<div className="info-item">
|
||||
<span className="label">Ошибки аппаратного обеспечения:</span>
|
||||
<span className="value error">2</span>
|
||||
</div>
|
||||
<div className="info-item">
|
||||
<span className="label">Ошибки программного обеспечения:</span>
|
||||
<span className="value error">1</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Dashboard;
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
import React from "react";
|
||||
import criticalIcon from "../assets/images/critical.png"; // Красный треугольник
|
||||
import warningIcon from "../assets/images/warning.png"; // Желтый треугольник
|
||||
import "../Style/ErrorIndicator.css"; // Подключаем стили
|
||||
|
||||
const ErrorIndicator = ({ criticalCount, warningCount }) => {
|
||||
return (
|
||||
<div className="error-indicator">
|
||||
{/* Красный индикатор (критические ошибки) */}
|
||||
<div className="error-item critical">
|
||||
<img src={criticalIcon} alt="Критическая ошибка" />
|
||||
<span>{criticalCount}</span>
|
||||
</div>
|
||||
|
||||
{/* Желтый индикатор (предупреждения) */}
|
||||
<div className="error-item warning">
|
||||
<img src={warningIcon} alt="Предупреждение" />
|
||||
<span>{warningCount}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ErrorIndicator;
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
import React, { useState } from "react";
|
||||
import "../Style/Expandable.css"
|
||||
|
||||
const ExpandableInfo = ({ details }) => {
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
|
||||
const toggleExpand = () => {
|
||||
setIsExpanded(!isExpanded);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="expandable-info">
|
||||
<button onClick={toggleExpand} className="expand-button">
|
||||
{isExpanded ? "Скрыть" : "Подробнее"}
|
||||
</button>
|
||||
{isExpanded && (
|
||||
<div className="details-menu">
|
||||
{details.map((detail, index) => (
|
||||
<div key={index} className="detail-item">
|
||||
<span className="label">{detail.label}:</span>
|
||||
<span className="value">{detail.value}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ExpandableInfo;
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
import React, { useState } from "react";
|
||||
import "./SidebarMenu.css"; // Импортируем стили для компонента
|
||||
|
||||
import "../Style/SidebarMenu.css"; // Импортируем стили для компонента
|
||||
const menuItems = [
|
||||
{
|
||||
title: "Выбор сервиса",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
.dashboard-container {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.left-column {
|
||||
flex: 2;
|
||||
}
|
||||
|
||||
.right-column {
|
||||
flex: 1;
|
||||
background: #f3f3f3;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 20px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 10px 0;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.value {
|
||||
color: #007bff;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.value.error {
|
||||
color: red;
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
.error-indicator {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.error-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.error-item img {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.error-item span {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.critical span {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.warning span {
|
||||
color: orange;
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
.expandable-info {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.expand-button {
|
||||
background-color: #444;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.expand-button:hover {
|
||||
background-color: #333;
|
||||
}
|
||||
|
||||
.details-menu {
|
||||
margin-top: 10px;
|
||||
padding: 10px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.value {
|
||||
color: #555;
|
||||
}
|
||||
|
|
@ -1,7 +1,10 @@
|
|||
/* SidebarMenu.css */
|
||||
|
||||
.sidebar {
|
||||
width: 250px;
|
||||
position: fixed;
|
||||
height: 100vh;
|
||||
width: 250px;
|
||||
/* height: 100vh; */
|
||||
background-color: #333;
|
||||
color: white;
|
||||
padding: 20px;
|
||||
|
|
@ -14,6 +17,12 @@
|
|||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.sidebar-indicator {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.sidebar-section {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
|
@ -48,4 +57,10 @@
|
|||
|
||||
.sidebar-item:hover {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.indicator-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
Loading…
Reference in New Issue