feat: Enhance GraphView and ServerManager components with tunnel type styles and hover effects for improved visualization and user interaction
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 5m45s
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 5m45s
This commit is contained in:
+54
-23
@@ -4,6 +4,7 @@ function GraphView({ servers, connections }) {
|
||||
const [nodePositions, setNodePositions] = useState({});
|
||||
const [draggedNode, setDraggedNode] = useState(null);
|
||||
const [dragOffset, setDragOffset] = useState({ x: 0, y: 0 });
|
||||
const [hoveredLinkId, setHoveredLinkId] = useState(null);
|
||||
const svgRef = useRef(null);
|
||||
|
||||
if (servers.length === 0) {
|
||||
@@ -98,6 +99,17 @@ function GraphView({ servers, connections }) {
|
||||
return colors[country] || '#6b7280';
|
||||
};
|
||||
|
||||
// Цвета/стили для разных типов туннелей
|
||||
const getTunnelStyle = (tunnelType) => {
|
||||
const map = {
|
||||
GRE: { color: '#206bc4', dash: '0', width: 3 },
|
||||
IPSec: { color: '#f59f00', dash: '6,4', width: 3 },
|
||||
WireGuard: { color: '#2fb344', dash: '0', width: 4 },
|
||||
OpenVPN: { color: '#be4bdb', dash: '3,3', width: 3 },
|
||||
};
|
||||
return map[tunnelType] || { color: '#667382', dash: '5,5', width: 2 };
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ width: '100%', height: '500px', border: '1px solid #e5e7eb', borderRadius: '8px', overflow: 'hidden', background: '#f8fafc' }}>
|
||||
<svg
|
||||
@@ -110,6 +122,32 @@ function GraphView({ servers, connections }) {
|
||||
onMouseLeave={handleMouseUp}
|
||||
style={{ cursor: draggedNode ? 'grabbing' : 'default' }}
|
||||
>
|
||||
<defs>
|
||||
{/* Определение стрелок под цвет канала */}
|
||||
{Array.from(new Set(connections.map(c => c.tunnelType))).map((t) => {
|
||||
const style = getTunnelStyle(t);
|
||||
return (
|
||||
<marker key={`arrow-${t}`}
|
||||
id={`arrow-${t}`}
|
||||
markerWidth="12"
|
||||
markerHeight="8"
|
||||
refX="10"
|
||||
refY="4"
|
||||
orient="auto"
|
||||
>
|
||||
<polygon points="0 0, 12 4, 0 8" fill={style.color} />
|
||||
</marker>
|
||||
);
|
||||
})}
|
||||
{/* Свечение для выделения */}
|
||||
<filter id="glow" x="-50%" y="-50%" width="200%" height="200%">
|
||||
<feGaussianBlur stdDeviation="2" result="coloredBlur"/>
|
||||
<feMerge>
|
||||
<feMergeNode in="coloredBlur"/>
|
||||
<feMergeNode in="SourceGraphic"/>
|
||||
</feMerge>
|
||||
</filter>
|
||||
</defs>
|
||||
{/* Связи */}
|
||||
{connections.map((connection, index) => {
|
||||
const fromServer = servers.find(s => s.ip === connection.from);
|
||||
@@ -119,19 +157,27 @@ function GraphView({ servers, connections }) {
|
||||
|
||||
const fromPos = getNodePosition(connection.from);
|
||||
const toPos = getNodePosition(connection.to);
|
||||
const style = getTunnelStyle(connection.tunnelType);
|
||||
const id = `${connection.from}-${connection.to}-${index}`;
|
||||
const isHovered = hoveredLinkId === id;
|
||||
|
||||
return (
|
||||
<g key={`link-${index}`}>
|
||||
<g key={`link-${index}`}
|
||||
onMouseEnter={() => setHoveredLinkId(id)}
|
||||
onMouseLeave={() => setHoveredLinkId(null)}
|
||||
>
|
||||
{/* Линия связи */}
|
||||
<line
|
||||
x1={fromPos.x}
|
||||
y1={fromPos.y}
|
||||
x2={toPos.x}
|
||||
y2={toPos.y}
|
||||
stroke="#94a3b8"
|
||||
strokeWidth="3"
|
||||
strokeDasharray="5,5"
|
||||
markerEnd="url(#arrowhead)"
|
||||
stroke={style.color}
|
||||
strokeWidth={isHovered ? style.width + 1 : style.width}
|
||||
strokeDasharray={style.dash}
|
||||
markerEnd={`url(#arrow-${connection.tunnelType})`}
|
||||
filter={isHovered ? 'url(#glow)' : undefined}
|
||||
opacity={isHovered ? 1 : 0.9}
|
||||
/>
|
||||
|
||||
{/* Подпись связи */}
|
||||
@@ -141,8 +187,8 @@ function GraphView({ servers, connections }) {
|
||||
width="80"
|
||||
height="30"
|
||||
rx="15"
|
||||
fill="white"
|
||||
stroke="#e5e7eb"
|
||||
fill="#ffffff"
|
||||
stroke={style.color}
|
||||
strokeWidth="1"
|
||||
/>
|
||||
<text
|
||||
@@ -282,22 +328,7 @@ function GraphView({ servers, connections }) {
|
||||
);
|
||||
})}
|
||||
|
||||
{/* Определение стрелки */}
|
||||
<defs>
|
||||
<marker
|
||||
id="arrowhead"
|
||||
markerWidth="12"
|
||||
markerHeight="8"
|
||||
refX="10"
|
||||
refY="4"
|
||||
orient="auto"
|
||||
>
|
||||
<polygon
|
||||
points="0 0, 12 4, 0 8"
|
||||
fill="#94a3b8"
|
||||
/>
|
||||
</marker>
|
||||
</defs>
|
||||
{/* Доп. определения добавлены выше */}
|
||||
</svg>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -700,11 +700,20 @@ function ServerManager() {
|
||||
/>
|
||||
|
||||
<div className="card w-100 mb-4">
|
||||
<div className="card-header">
|
||||
<div className="card-header d-flex justify-content-between align-items-center">
|
||||
<h3 className="card-title mb-0">Граф связей между серверами</h3>
|
||||
<div className="text-muted small">Поддержка: GRE, IPSec, WireGuard, OpenVPN</div>
|
||||
</div>
|
||||
<div className="card-body">
|
||||
<GraphView servers={servers} connections={connections} />
|
||||
<div className="mt-3">
|
||||
<div className="d-flex align-items-center gap-3 flex-wrap">
|
||||
<div className="d-flex align-items-center gap-2"><span style={{display:'inline-block',width:24,height:4,background:'#206bc4'}}></span><span className="text-muted small">GRE</span></div>
|
||||
<div className="d-flex align-items-center gap-2"><span style={{display:'inline-block',width:24,height:4,background:'#f59f00',borderBottom:'2px dashed #f59f00'}}></span><span className="text-muted small">IPSec</span></div>
|
||||
<div className="d-flex align-items-center gap-2"><span style={{display:'inline-block',width:24,height:4,background:'#2fb344'}}></span><span className="text-muted small">WireGuard</span></div>
|
||||
<div className="d-flex align-items-center gap-2"><span style={{display:'inline-block',width:24,height:4,background:'#be4bdb',borderBottom:'2px dotted #be4bdb'}}></span><span className="text-muted small">OpenVPN</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="card w-100 mb-4">
|
||||
|
||||
Reference in New Issue
Block a user