Compare commits
2 Commits
db539893ce
...
206b6915e5
| Author | SHA1 | Date | |
|---|---|---|---|
| 206b6915e5 | |||
| 5189fdb621 |
@@ -1,6 +1,5 @@
|
|||||||
import React from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
ResponsiveContainer,
|
|
||||||
LineChart,
|
LineChart,
|
||||||
Line,
|
Line,
|
||||||
XAxis,
|
XAxis,
|
||||||
@@ -15,8 +14,10 @@ import {
|
|||||||
Bar,
|
Bar,
|
||||||
} from 'recharts';
|
} from 'recharts';
|
||||||
|
|
||||||
|
// =========================
|
||||||
// 默认颜色池
|
// 默认颜色池
|
||||||
|
// =========================
|
||||||
|
|
||||||
const DEFAULT_COLORS = [
|
const DEFAULT_COLORS = [
|
||||||
'#1677ff',
|
'#1677ff',
|
||||||
'#52c41a',
|
'#52c41a',
|
||||||
@@ -29,11 +30,114 @@ const DEFAULT_COLORS = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
|
// =========================
|
||||||
|
// 通用自适应容器
|
||||||
|
// =========================
|
||||||
|
//
|
||||||
|
// 不再使用 ResponsiveContainer。
|
||||||
|
// 通过 ResizeObserver 获取真实尺寸,
|
||||||
|
// 只有 width / height > 0 时才渲染 Recharts。
|
||||||
|
// 这样可以彻底避免:
|
||||||
|
//
|
||||||
|
// width(-1) and height(-1)
|
||||||
|
//
|
||||||
|
// =========================
|
||||||
|
|
||||||
|
const ChartResizeContainer = ({
|
||||||
|
children,
|
||||||
|
width = '100%',
|
||||||
|
height = 360,
|
||||||
|
className,
|
||||||
|
style,
|
||||||
|
}) => {
|
||||||
|
const containerRef = useRef(null);
|
||||||
|
|
||||||
|
const [size, setSize] = useState({
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const element = containerRef.current;
|
||||||
|
|
||||||
|
if (!element) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateSize = () => {
|
||||||
|
const rect = element.getBoundingClientRect();
|
||||||
|
|
||||||
|
const nextWidth = Math.floor(rect.width);
|
||||||
|
const nextHeight = Math.floor(rect.height);
|
||||||
|
|
||||||
|
// 非法尺寸直接忽略
|
||||||
|
if (nextWidth <= 0 || nextHeight <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setSize((prev) => {
|
||||||
|
if (
|
||||||
|
prev.width === nextWidth &&
|
||||||
|
prev.height === nextHeight
|
||||||
|
) {
|
||||||
|
return prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
width: nextWidth,
|
||||||
|
height: nextHeight,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 首次主动测量
|
||||||
|
updateSize();
|
||||||
|
|
||||||
|
// 后续监听尺寸变化
|
||||||
|
const observer = new ResizeObserver(() => {
|
||||||
|
updateSize();
|
||||||
|
});
|
||||||
|
|
||||||
|
observer.observe(element);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
observer.disconnect();
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={containerRef}
|
||||||
|
className={className}
|
||||||
|
style={{
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
minWidth: 0,
|
||||||
|
minHeight: 0,
|
||||||
|
boxSizing: 'border-box',
|
||||||
|
overflow: 'hidden',
|
||||||
|
...style,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{size.width > 0 && size.height > 0
|
||||||
|
? React.cloneElement(children, {
|
||||||
|
width: size.width,
|
||||||
|
height: size.height,
|
||||||
|
})
|
||||||
|
: null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// =========================
|
||||||
|
// Universal Line Chart
|
||||||
|
// =========================
|
||||||
|
|
||||||
export const UniversalLineChart = ({
|
export const UniversalLineChart = ({
|
||||||
data,
|
data = [],
|
||||||
xKey,
|
xKey,
|
||||||
series,
|
series = [],
|
||||||
width = '100%',
|
width = '100%',
|
||||||
height = 360,
|
height = 360,
|
||||||
showGrid = true,
|
showGrid = true,
|
||||||
@@ -43,6 +147,7 @@ export const UniversalLineChart = ({
|
|||||||
smooth = true,
|
smooth = true,
|
||||||
dot = false,
|
dot = false,
|
||||||
className,
|
className,
|
||||||
|
style,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -50,48 +155,80 @@ export const UniversalLineChart = ({
|
|||||||
style={{
|
style={{
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
|
minWidth: 0,
|
||||||
|
minHeight: 0,
|
||||||
|
boxSizing: 'border-box',
|
||||||
background: backgroundColor,
|
background: backgroundColor,
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
padding: 12,
|
padding: 12,
|
||||||
|
overflow: 'hidden',
|
||||||
|
...style,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ResponsiveContainer width="100%" height="100%" minWidth={0} minHeight={0}>
|
<ChartResizeContainer
|
||||||
<LineChart data={data} minWidth={0} minHeight={0} width="100%" height="100%" >
|
width="100%"
|
||||||
|
height="100%"
|
||||||
|
>
|
||||||
|
<LineChart data={data}>
|
||||||
{showGrid && (
|
{showGrid && (
|
||||||
<CartesianGrid strokeDasharray="3 3" opacity={0.25} />
|
<CartesianGrid
|
||||||
|
strokeDasharray="3 3"
|
||||||
|
opacity={0.25}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<XAxis dataKey={xKey} />
|
<XAxis dataKey={xKey} />
|
||||||
|
|
||||||
<YAxis />
|
<YAxis />
|
||||||
|
|
||||||
{showTooltip && <Tooltip />}
|
{showTooltip && <Tooltip />}
|
||||||
|
|
||||||
{showLegend && <Legend />}
|
{showLegend && <Legend />}
|
||||||
|
|
||||||
{series.map((item, index) => (
|
{series.map((item, index) => (
|
||||||
<Line
|
<Line
|
||||||
key={item.key}
|
key={item.key}
|
||||||
type={smooth ? 'monotone' : 'linear'}
|
type={
|
||||||
|
smooth
|
||||||
|
? 'monotone'
|
||||||
|
: 'linear'
|
||||||
|
}
|
||||||
dataKey={item.key}
|
dataKey={item.key}
|
||||||
name={item.name}
|
name={item.name}
|
||||||
stroke={item.color || DEFAULT_COLORS[index % DEFAULT_COLORS.length]}
|
stroke={
|
||||||
strokeWidth={item.strokeWidth || 2}
|
item.color ||
|
||||||
strokeDasharray={item.dashed ? '6 6' : undefined}
|
DEFAULT_COLORS[
|
||||||
|
index %
|
||||||
|
DEFAULT_COLORS.length
|
||||||
|
]
|
||||||
|
}
|
||||||
|
strokeWidth={
|
||||||
|
item.strokeWidth || 2
|
||||||
|
}
|
||||||
|
strokeDasharray={
|
||||||
|
item.dashed
|
||||||
|
? '6 6'
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
dot={dot}
|
dot={dot}
|
||||||
activeDot={{ r: 6 }}
|
activeDot={{ r: 6 }}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</LineChart>
|
</LineChart>
|
||||||
</ResponsiveContainer>
|
</ChartResizeContainer>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// =========================
|
||||||
|
// Universal Bar Chart
|
||||||
|
// =========================
|
||||||
|
|
||||||
export const UniversalBarChart = ({
|
export const UniversalBarChart = ({
|
||||||
data,
|
data = [],
|
||||||
xKey,
|
xKey,
|
||||||
series,
|
series = [],
|
||||||
width = '100%',
|
width = '100%',
|
||||||
height = 360,
|
height = 360,
|
||||||
showGrid = true,
|
showGrid = true,
|
||||||
@@ -102,6 +239,7 @@ export const UniversalBarChart = ({
|
|||||||
radius = 6,
|
radius = 6,
|
||||||
stack = false,
|
stack = false,
|
||||||
className,
|
className,
|
||||||
|
style,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -109,21 +247,34 @@ export const UniversalBarChart = ({
|
|||||||
style={{
|
style={{
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
|
minWidth: 0,
|
||||||
|
minHeight: 0,
|
||||||
|
boxSizing: 'border-box',
|
||||||
background: backgroundColor,
|
background: backgroundColor,
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
padding: 12,
|
padding: 12,
|
||||||
|
overflow: 'hidden',
|
||||||
|
...style,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ResponsiveContainer width="100%" height="100%" minWidth={0} minHeight={0}>
|
<ChartResizeContainer
|
||||||
<BarChart data={data} minWidth={0} minHeight={0} width="100%" height="100%" >
|
width="100%"
|
||||||
|
height="100%"
|
||||||
|
>
|
||||||
|
<BarChart data={data}>
|
||||||
{showGrid && (
|
{showGrid && (
|
||||||
<CartesianGrid strokeDasharray="3 3" opacity={0.25} />
|
<CartesianGrid
|
||||||
|
strokeDasharray="3 3"
|
||||||
|
opacity={0.25}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<XAxis dataKey={xKey} />
|
<XAxis dataKey={xKey} />
|
||||||
|
|
||||||
<YAxis />
|
<YAxis />
|
||||||
|
|
||||||
{showTooltip && <Tooltip />}
|
{showTooltip && <Tooltip />}
|
||||||
|
|
||||||
{showLegend && <Legend />}
|
{showLegend && <Legend />}
|
||||||
|
|
||||||
{series.map((item, index) => (
|
{series.map((item, index) => (
|
||||||
@@ -131,22 +282,35 @@ export const UniversalBarChart = ({
|
|||||||
key={item.key}
|
key={item.key}
|
||||||
dataKey={item.key}
|
dataKey={item.key}
|
||||||
name={item.name}
|
name={item.name}
|
||||||
fill={item.color || DEFAULT_COLORS[index % DEFAULT_COLORS.length]}
|
fill={
|
||||||
|
item.color ||
|
||||||
|
DEFAULT_COLORS[
|
||||||
|
index %
|
||||||
|
DEFAULT_COLORS.length
|
||||||
|
]
|
||||||
|
}
|
||||||
barSize={barSize}
|
barSize={barSize}
|
||||||
radius={radius}
|
radius={radius}
|
||||||
stackId={stack ? 'stack' : undefined}
|
stackId={
|
||||||
|
stack
|
||||||
|
? 'stack'
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</BarChart>
|
</BarChart>
|
||||||
</ResponsiveContainer>
|
</ChartResizeContainer>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// =========================
|
||||||
|
// Universal Pie Chart
|
||||||
|
// =========================
|
||||||
|
|
||||||
export const UniversalPieChart = ({
|
export const UniversalPieChart = ({
|
||||||
data,
|
data = [],
|
||||||
width = '100%',
|
width = '100%',
|
||||||
height = 360,
|
height = 360,
|
||||||
showLegend = true,
|
showLegend = true,
|
||||||
@@ -157,7 +321,7 @@ export const UniversalPieChart = ({
|
|||||||
paddingAngle = 2,
|
paddingAngle = 2,
|
||||||
showLabel = true,
|
showLabel = true,
|
||||||
className,
|
className,
|
||||||
style
|
style,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -165,15 +329,23 @@ export const UniversalPieChart = ({
|
|||||||
style={{
|
style={{
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
|
minWidth: 0,
|
||||||
|
minHeight: 0,
|
||||||
|
boxSizing: 'border-box',
|
||||||
background: backgroundColor,
|
background: backgroundColor,
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
padding: 12,
|
padding: 12,
|
||||||
...style
|
overflow: 'hidden',
|
||||||
|
...style,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ResponsiveContainer width="100%" height="100%" minWidth={0} minHeight={0}>
|
<ChartResizeContainer
|
||||||
<PieChart minWidth={0} minHeight={0} width="100%" height="100%" >
|
width="100%"
|
||||||
|
height="100%"
|
||||||
|
>
|
||||||
|
<PieChart>
|
||||||
{showTooltip && <Tooltip />}
|
{showTooltip && <Tooltip />}
|
||||||
|
|
||||||
{showLegend && <Legend />}
|
{showLegend && <Legend />}
|
||||||
|
|
||||||
<Pie
|
<Pie
|
||||||
@@ -190,37 +362,86 @@ export const UniversalPieChart = ({
|
|||||||
{data.map((entry, index) => (
|
{data.map((entry, index) => (
|
||||||
<Cell
|
<Cell
|
||||||
key={`cell-${index}`}
|
key={`cell-${index}`}
|
||||||
fill={entry.color || DEFAULT_COLORS[index % DEFAULT_COLORS.length]}
|
fill={
|
||||||
|
entry.color ||
|
||||||
|
DEFAULT_COLORS[
|
||||||
|
index %
|
||||||
|
DEFAULT_COLORS.length
|
||||||
|
]
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</Pie>
|
</Pie>
|
||||||
</PieChart>
|
</PieChart>
|
||||||
</ResponsiveContainer>
|
</ChartResizeContainer>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// =========================
|
// =========================
|
||||||
// 使用示例
|
// 使用示例
|
||||||
// =========================
|
// =========================
|
||||||
|
|
||||||
export const DemoCharts = () => {
|
export const DemoCharts = () => {
|
||||||
const lineData = [
|
const lineData = [
|
||||||
{ month: '1月', uv: 400, pv: 240, amt: 240 },
|
{
|
||||||
{ month: '2月', uv: 300, pv: 456, amt: 300 },
|
month: '1月',
|
||||||
{ month: '3月', uv: 500, pv: 139, amt: 200 },
|
uv: 400,
|
||||||
{ month: '4月', uv: 278, pv: 390, amt: 278 },
|
pv: 240,
|
||||||
{ month: '5月', uv: 189, pv: 480, amt: 189 },
|
amt: 240,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
month: '2月',
|
||||||
|
uv: 300,
|
||||||
|
pv: 456,
|
||||||
|
amt: 300,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
month: '3月',
|
||||||
|
uv: 500,
|
||||||
|
pv: 139,
|
||||||
|
amt: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
month: '4月',
|
||||||
|
uv: 278,
|
||||||
|
pv: 390,
|
||||||
|
amt: 278,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
month: '5月',
|
||||||
|
uv: 189,
|
||||||
|
pv: 480,
|
||||||
|
amt: 189,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const pieData = [
|
const pieData = [
|
||||||
{ name: 'Vue', value: 40 },
|
{
|
||||||
{ name: 'React', value: 35 },
|
name: 'Vue',
|
||||||
{ name: 'Angular', value: 25 },
|
value: 40,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'React',
|
||||||
|
value: 35,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Angular',
|
||||||
|
value: 25,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="grid grid-cols-1 gap-6 p-6">
|
<div
|
||||||
|
className="
|
||||||
|
grid
|
||||||
|
grid-cols-1
|
||||||
|
gap-6
|
||||||
|
p-6
|
||||||
|
min-w-0
|
||||||
|
"
|
||||||
|
>
|
||||||
<UniversalLineChart
|
<UniversalLineChart
|
||||||
data={lineData}
|
data={lineData}
|
||||||
xKey="month"
|
xKey="month"
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ export default function AlarmCenterPage() {
|
|||||||
<div style={{ display: 'flex', paddingBottom: "4px" }}>
|
<div style={{ display: 'flex', paddingBottom: "4px" }}>
|
||||||
<div style={{ padding: '8px 12px 0' }}>
|
<div style={{ padding: '8px 12px 0' }}>
|
||||||
<Typography.Title level={4} style={{ margin: 0, fontSize: 'clamp(14px, 1.2vw, 18px)' }}>{t('router.alerts')}</Typography.Title>
|
<Typography.Title level={4} style={{ margin: 0, fontSize: 'clamp(14px, 1.2vw, 18px)' }}>{t('router.alerts')}</Typography.Title>
|
||||||
<Text type="secondary" style={{ fontSize: '12px' }}>{t('alerts.subtitle')}</Text>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Header style={{ padding: 0, borderBottom: '1px solid #f0f0f0', position: 'sticky', top: 0, zIndex: 10, height: 40, lineHeight: '40px' }}>
|
<Header style={{ padding: 0, borderBottom: '1px solid #f0f0f0', position: 'sticky', top: 0, zIndex: 10, height: 40, lineHeight: '40px' }}>
|
||||||
|
|||||||
Reference in New Issue
Block a user