添加流量地图
This commit is contained in:
@@ -586,11 +586,17 @@ input:checked+.slider:before {
|
||||
/* ---- Charts Section ---- */
|
||||
.charts-section {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-columns: 3fr 2fr;
|
||||
gap: 16px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
.charts-section {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.chart-card {
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--border-color);
|
||||
@@ -748,6 +754,24 @@ input:checked+.slider:before {
|
||||
color: var(--accent-cyan);
|
||||
}
|
||||
|
||||
/* ---- Globe Card ---- */
|
||||
.globe-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.globe-body {
|
||||
flex: 1;
|
||||
min-height: 280px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
.globe-body:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
/* ---- Gauges ---- */
|
||||
.gauges-container {
|
||||
display: flex;
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&family=JetBrains+Mono:wght@400;500;600&display=swap"
|
||||
rel="stylesheet">
|
||||
<link rel="stylesheet" href="/css/style.css">
|
||||
<script src="//unpkg.com/globe.gl"></script>
|
||||
<script>
|
||||
// Prevent theme flicker
|
||||
(function () {
|
||||
@@ -234,6 +235,20 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Global Traffic 3D Globe -->
|
||||
<div class="chart-card globe-card" id="globeCard">
|
||||
<div class="chart-card-header">
|
||||
<h2 class="chart-title">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" class="chart-title-icon">
|
||||
<circle cx="12" cy="12" r="10" />
|
||||
<path d="M2 12h20M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z" />
|
||||
</svg>
|
||||
全球节点分布
|
||||
</h2>
|
||||
</div>
|
||||
<div class="globe-body" id="globeContainer"></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Server List -->
|
||||
|
||||
@@ -77,7 +77,8 @@
|
||||
newPasswordInput: document.getElementById('newPassword'),
|
||||
confirmNewPasswordInput: document.getElementById('confirmNewPassword'),
|
||||
btnChangePassword: document.getElementById('btnChangePassword'),
|
||||
changePasswordMessage: document.getElementById('changePasswordMessage')
|
||||
changePasswordMessage: document.getElementById('changePasswordMessage'),
|
||||
globeContainer: document.getElementById('globeContainer')
|
||||
};
|
||||
|
||||
// ---- State ----
|
||||
@@ -89,6 +90,7 @@
|
||||
let currentSourceFilter = 'all';
|
||||
let currentPage = 1;
|
||||
let pageSize = 20;
|
||||
let myGlobe = null;
|
||||
|
||||
// ---- Initialize ----
|
||||
function init() {
|
||||
@@ -101,6 +103,9 @@
|
||||
// Network chart
|
||||
networkChart = new AreaChart(dom.networkCanvas);
|
||||
|
||||
// Initial globe
|
||||
initGlobe();
|
||||
|
||||
// Event listeners
|
||||
dom.btnSettings.addEventListener('click', openSettings);
|
||||
dom.modalClose.addEventListener('click', closeSettings);
|
||||
@@ -353,6 +358,78 @@
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Global Globe ----
|
||||
function initGlobe() {
|
||||
if (!dom.globeContainer) return;
|
||||
|
||||
myGlobe = Globe()
|
||||
(dom.globeContainer)
|
||||
.globeImageUrl('//unpkg.com/three-globe/example/img/earth-dark.jpg')
|
||||
.bumpImageUrl('//unpkg.com/three-globe/example/img/earth-topology.png')
|
||||
.backgroundColor('rgba(0,0,0,0)')
|
||||
.showAtmosphere(true)
|
||||
.atmosphereColor('#6366f1')
|
||||
.atmosphereDaylightAlpha(0.1)
|
||||
.pointsData([])
|
||||
.pointColor(() => '#06b6d4')
|
||||
.pointAltitude(0.05)
|
||||
.pointRadius(0.8)
|
||||
.pointsMerge(true)
|
||||
.pointLabel(d => `
|
||||
<div style="background: rgba(10, 14, 26, 0.9); padding: 8px 12px; border: 1px solid var(--accent-indigo); border-radius: 8px; backdrop-filter: blur(8px);">
|
||||
<div style="font-weight: 700; color: #fff; margin-bottom: 4px;">${escapeHtml(d.job)}</div>
|
||||
<div style="font-size: 0.75rem; color: var(--text-secondary);">${escapeHtml(d.city || '')}, ${escapeHtml(d.countryName || d.country || '')}</div>
|
||||
<div style="font-size: 0.75rem; margin-top: 4px;">
|
||||
<span style="color: var(--accent-indigo);">BW:</span> ↓${formatBandwidth(d.netRx)} ↑${formatBandwidth(d.netTx)}
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
|
||||
// Resizing
|
||||
const resizeObserver = new ResizeObserver(() => {
|
||||
if (myGlobe) {
|
||||
const width = dom.globeContainer.clientWidth;
|
||||
const height = dom.globeContainer.clientHeight;
|
||||
myGlobe.width(width).height(height);
|
||||
}
|
||||
});
|
||||
resizeObserver.observe(dom.globeContainer);
|
||||
|
||||
// Initial view
|
||||
myGlobe.controls().autoRotate = true;
|
||||
myGlobe.controls().autoRotateSpeed = 0.5;
|
||||
myGlobe.controls().enableZoom = false; // Disable zoom to maintain dashboard feel
|
||||
}
|
||||
|
||||
function updateGlobe(servers) {
|
||||
if (!myGlobe) return;
|
||||
|
||||
// Filter servers with lat/lng
|
||||
const geoData = servers
|
||||
.filter(s => s.lat && s.lng)
|
||||
.map(s => ({
|
||||
lat: s.lat,
|
||||
lng: s.lng,
|
||||
job: s.job,
|
||||
city: s.city,
|
||||
country: s.country,
|
||||
countryName: s.countryName,
|
||||
netRx: s.netRx,
|
||||
netTx: s.netTx,
|
||||
size: Math.max(0.2, Math.min(1.5, (s.netRx + s.netTx) / 1024 / 1024 / 5)) // Scale by bandwidth (MB/s)
|
||||
}));
|
||||
|
||||
myGlobe.pointsData(geoData);
|
||||
|
||||
// Also add arcs for "traffic flow" if we have multiple servers
|
||||
// For now, let's just show rings or pulses for active traffic
|
||||
myGlobe.ringsData(geoData.filter(d => (d.netRx + d.netTx) > 1024 * 1024)); // Pulse for servers > 1MB/s
|
||||
myGlobe.ringColor(() => '#6366f1');
|
||||
myGlobe.ringMaxRadius(3);
|
||||
myGlobe.ringPropagationSpeed(1);
|
||||
myGlobe.ringRepeatPeriod(1000);
|
||||
}
|
||||
|
||||
// ---- Update Dashboard ----
|
||||
function updateDashboard(data) {
|
||||
// Server count
|
||||
@@ -384,6 +461,9 @@
|
||||
|
||||
// Update server table
|
||||
renderFilteredServers();
|
||||
|
||||
// Update globe
|
||||
updateGlobe(data.servers || []);
|
||||
|
||||
// Flash animation
|
||||
if (previousMetrics) {
|
||||
|
||||
Reference in New Issue
Block a user