diff --git a/package.json b/package.json
index f803480..d107aba 100755
--- a/package.json
+++ b/package.json
@@ -28,7 +28,9 @@
"vite-plugin-svgr": "^4.3.0",
"react-scripts": "^5.0.1",
"socket.io-client": "^4.8.1",
- "antd": "^5.24.7"
+ "antd": "^5.24.7",
+ "react-window": "1.8.11",
+ "react-virtualized-auto-sizer": "1.0.26"
},
"devDependencies": {
"@eslint/js": "^9.17.0",
diff --git a/src/Components/Layout/SettingsComponents/MetricRangeEditor.jsx b/src/Components/Layout/SettingsComponents/MetricRangeEditor.jsx
index 62b4471..d9135b6 100644
--- a/src/Components/Layout/SettingsComponents/MetricRangeEditor.jsx
+++ b/src/Components/Layout/SettingsComponents/MetricRangeEditor.jsx
@@ -1,4 +1,4 @@
-import React, { useState, useEffect, useCallback } from 'react';
+import React, { useState, useEffect, useCallback, useMemo } from 'react';
import {
TextField, Box, Typography, IconButton, Divider,
CircularProgress, Alert, Collapse, Tooltip, Button
@@ -7,6 +7,81 @@ import DeleteIcon from '@mui/icons-material/Delete';
import AddIcon from '@mui/icons-material/Add';
import SearchIcon from '@mui/icons-material/Search';
import axios from 'axios';
+import { VariableSizeList as List } from 'react-window';
+import AutoSizer from 'react-virtualized-auto-sizer';
+
+const MetricItem = React.memo(({ metric, index, updateRange, addRange, deleteRange }) => {
+ return (
+
+
+ {metric.name}
+
+
+ {metric.ranges.map((r, j) => (
+ *': { flex: 1 }
+ }}
+ >
+ updateRange(index, j, 'min', e.target.value)}
+ size="small"
+ variant="standard"
+ />
+ updateRange(index, j, 'max', e.target.value)}
+ size="small"
+ variant="standard"
+ />
+ updateRange(index, j, 'status', e.target.value)}
+ size="small"
+ variant="standard"
+ />
+
+ deleteRange(index, j)}
+ size="small"
+ sx={{ flex: 'none' }}
+ >
+
+
+
+
+ ))}
+
+
+
+ );
+});
const MetricRangeEditor = ({ onSave }) => {
const [ranges, setRanges] = useState([]);
@@ -17,7 +92,6 @@ const MetricRangeEditor = ({ onSave }) => {
const [hasChanges, setHasChanges] = useState(false);
const [success, setSuccess] = useState(false);
- // Загрузка данных
const loadRanges = useCallback(async () => {
try {
setLoading(true);
@@ -41,31 +115,53 @@ const MetricRangeEditor = ({ onSave }) => {
loadRanges();
}, [loadRanges]);
- // Обновление диапазона
- const updateRange = (metricIndex, rangeIndex, field, value) => {
- const newRanges = [...ranges];
- newRanges[metricIndex].ranges[rangeIndex][field] = Number(value);
- setRanges(newRanges);
+ const updateRange = useCallback((metricIndex, rangeIndex, field, value) => {
+ setRanges(prev => {
+ const newRanges = [...prev];
+ newRanges[metricIndex] = {
+ ...newRanges[metricIndex],
+ ranges: [...newRanges[metricIndex].ranges]
+ };
+ newRanges[metricIndex].ranges[rangeIndex] = {
+ ...newRanges[metricIndex].ranges[rangeIndex],
+ [field]: Number(value)
+ };
+ return newRanges;
+ });
setHasChanges(true);
+ }, []);
+
+ const getItemSize = (index) => {
+ const baseHeight = 80;
+ const rangeCount = filtered[index].ranges.length;
+ return baseHeight + rangeCount * 56 + 40;
};
- // Добавление диапазона
- const addRange = (metricIndex) => {
- const newRanges = [...ranges];
- newRanges[metricIndex].ranges.push({ min: 0, max: 100, status: 1 });
- setRanges(newRanges);
+ const addRange = useCallback((metricIndex) => {
+ setRanges(prev => {
+ const newRanges = [...prev];
+ newRanges[metricIndex] = {
+ ...newRanges[metricIndex],
+ ranges: [...newRanges[metricIndex].ranges, { min: 0, max: 100, status: 1 }]
+ };
+ return newRanges;
+ });
setHasChanges(true);
- };
+ }, []);
- // Удаление диапазона
- const deleteRange = (metricIndex, rangeIndex) => {
- const newRanges = [...ranges];
- newRanges[metricIndex].ranges.splice(rangeIndex, 1);
- setRanges(newRanges);
+ const deleteRange = useCallback((metricIndex, rangeIndex) => {
+ setRanges(prev => {
+ const newRanges = [...prev];
+ newRanges[metricIndex] = {
+ ...newRanges[metricIndex],
+ ranges: newRanges[metricIndex].ranges.filter((_, i) => i !== rangeIndex)
+ };
+ return newRanges;
+ });
setHasChanges(true);
- };
+ }, []);
- const saveChanges = async () => {
+ const saveChanges = useCallback(async () => {
try {
setLoading(true);
await axios.post(`${import.meta.env.VITE_BACK_URL}/api/ranges/update`, ranges);
@@ -87,10 +183,9 @@ const MetricRangeEditor = ({ onSave }) => {
} finally {
setLoading(false);
}
- };
+ }, [ranges, onSave]);
- // Добавление новой метрики
- const addNewMetric = () => {
+ const addNewMetric = useCallback(() => {
if (!newMetricName.trim()) {
setError('Введите название метрики');
return;
@@ -99,18 +194,26 @@ const MetricRangeEditor = ({ onSave }) => {
setError('Метрика с таким именем уже существует');
return;
}
- setRanges([...ranges, {
+ setRanges(prev => [...prev, {
name: newMetricName,
ranges: [{ min: 0, max: 100, status: 1 }]
}]);
setNewMetricName('');
setHasChanges(true);
setError(null);
- };
+ }, [newMetricName, ranges]);
- const filtered = filter
- ? ranges.filter(r => r.name.toLowerCase().includes(filter.toLowerCase()))
- : ranges;
+ const filtered = useMemo(() => {
+ return filter
+ ? ranges.filter(r => r.name.toLowerCase().includes(filter.toLowerCase()))
+ : ranges;
+ }, [filter, ranges]);
+
+ useEffect(() => {
+ if (onSave) {
+ onSave({ hasChanges, saveChanges });
+ }
+ }, [hasChanges, onSave, saveChanges]);
return (
@@ -166,76 +269,30 @@ const MetricRangeEditor = ({ onSave }) => {
- {filtered.map((metric, i) => (
-
-
- {metric.name}
-
-
- {metric.ranges.map((r, j) => (
- *': { flex: 1 }
- }}
+
+
+ {({ height, width }) => (
+
- updateRange(i, j, 'min', e.target.value)}
- size="small"
- />
- updateRange(i, j, 'max', e.target.value)}
- size="small"
- />
- updateRange(i, j, 'status', e.target.value)}
- size="small"
- />
-
- deleteRange(i, j)}
- size="small"
- sx={{ flex: 'none' }}
- >
-
-
-
-
- ))}
-
-
-
- ))}
+ {({ index, style }) => (
+
+
+
+ )}
+
+ )}
+
+
{filtered.length === 0 && (
@@ -244,15 +301,8 @@ const MetricRangeEditor = ({ onSave }) => {
)}
>
)}
-
- {/* Передаем состояние изменений в родительский компонент */}
- {useEffect(() => {
- if (onSave) {
- onSave({ hasChanges, saveChanges });
- }
- }, [hasChanges, onSave])}
);
};
-export default MetricRangeEditor;
\ No newline at end of file
+export default React.memo(MetricRangeEditor);
\ No newline at end of file