452 lines
21 KiB
HTML
452 lines
21 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="zh-CN">
|
|
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<meta name="description" content="多源Prometheus服务器监控展示大屏 - 实时CPU、内存、磁盘、网络统计">
|
|
<title>数据可视化展示大屏</title>
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link
|
|
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>
|
|
// Prevent theme flicker
|
|
(function () {
|
|
const savedTheme = localStorage.getItem('theme');
|
|
const settings = window.SITE_SETTINGS || {};
|
|
const defaultTheme = settings.default_theme || 'dark';
|
|
let theme = savedTheme || defaultTheme;
|
|
|
|
if (theme === 'auto') {
|
|
theme = window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
|
|
}
|
|
|
|
if (theme === 'light') {
|
|
document.documentElement.classList.add('light-theme');
|
|
}
|
|
|
|
// Also apply title if available to prevent flicker
|
|
if (settings.page_name) {
|
|
document.title = settings.page_name;
|
|
}
|
|
})();
|
|
</script>
|
|
</head>
|
|
|
|
<body>
|
|
<!-- Animated Background -->
|
|
<div class="bg-grid"></div>
|
|
<div class="bg-glow bg-glow-1"></div>
|
|
<div class="bg-glow bg-glow-2"></div>
|
|
<div class="bg-glow bg-glow-3"></div>
|
|
|
|
<!-- App Container -->
|
|
<div id="app">
|
|
<!-- Header -->
|
|
<header class="header" id="header">
|
|
<div class="header-left">
|
|
<div class="logo">
|
|
<div id="logoIconContainer">
|
|
<svg class="logo-icon" id="logoSvg" viewBox="0 0 32 32" fill="none">
|
|
<rect x="2" y="2" width="28" height="28" rx="8" stroke="url(#logoGrad)" stroke-width="2.5" />
|
|
<path d="M8 22 L12 14 L16 18 L20 10 L24 16" stroke="url(#logoGrad)" stroke-width="2"
|
|
stroke-linecap="round" stroke-linejoin="round" fill="none" />
|
|
<circle cx="12" cy="14" r="2" fill="url(#logoGrad)" />
|
|
<circle cx="20" cy="10" r="2" fill="url(#logoGrad)" />
|
|
<defs>
|
|
<linearGradient id="logoGrad" x1="0" y1="0" x2="32" y2="32">
|
|
<stop offset="0%" stop-color="#6366f1" />
|
|
<stop offset="100%" stop-color="#06b6d4" />
|
|
</linearGradient>
|
|
</defs>
|
|
</svg>
|
|
</div>
|
|
<h1 class="logo-text" id="logoText">数据可视化展示大屏</h1>
|
|
</div>
|
|
</div>
|
|
<div class="header-right">
|
|
|
|
<div class="theme-switch-wrapper">
|
|
<label class="theme-switch" for="themeToggle">
|
|
<input type="checkbox" id="themeToggle" />
|
|
<div class="slider round">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
|
|
stroke-linejoin="round" class="theme-icon sun-icon">
|
|
<circle cx="12" cy="12" r="5"></circle>
|
|
<line x1="12" y1="1" x2="12" y2="3"></line>
|
|
<line x1="12" y1="21" x2="12" y2="23"></line>
|
|
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
|
|
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
|
|
<line x1="1" y1="12" x2="3" y2="12"></line>
|
|
<line x1="21" y1="12" x2="23" y2="12"></line>
|
|
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
|
|
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
|
|
</svg>
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
|
|
stroke-linejoin="round" class="theme-icon moon-icon" style="display: none;">
|
|
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
|
|
</svg>
|
|
</div>
|
|
</label>
|
|
</div>
|
|
<div id="userSection">
|
|
<button class="btn btn-login" id="btnLogin">登录</button>
|
|
</div>
|
|
<button class="btn-settings" id="btnSettings" title="配置管理">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
|
|
stroke-linejoin="round">
|
|
<circle cx="12" cy="12" r="3"></circle>
|
|
<path
|
|
d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z">
|
|
</path>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
</header>
|
|
|
|
<!-- Main Dashboard -->
|
|
<main class="dashboard" id="dashboard">
|
|
<!-- Top Stat Cards -->
|
|
<section class="stat-cards">
|
|
<div class="stat-card stat-card-servers" id="cardServers">
|
|
<div class="stat-card-icon">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
|
<rect x="2" y="2" width="20" height="8" rx="2" />
|
|
<rect x="2" y="14" width="20" height="8" rx="2" />
|
|
<circle cx="6" cy="6" r="1" fill="currentColor" />
|
|
<circle cx="6" cy="18" r="1" fill="currentColor" />
|
|
</svg>
|
|
</div>
|
|
<div class="stat-card-content">
|
|
<span class="stat-card-label" id="totalServersLabel">服务器总数</span>
|
|
<span class="stat-card-value" id="totalServers">0</span>
|
|
</div>
|
|
</div>
|
|
<div class="stat-card stat-card-cpu" id="cardCpu">
|
|
<div class="stat-card-icon">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
|
<rect x="4" y="4" width="16" height="16" rx="2" />
|
|
<rect x="9" y="9" width="6" height="6" />
|
|
<line x1="9" y1="2" x2="9" y2="4" />
|
|
<line x1="15" y1="2" x2="15" y2="4" />
|
|
<line x1="9" y1="20" x2="9" y2="22" />
|
|
<line x1="15" y1="20" x2="15" y2="22" />
|
|
<line x1="2" y1="9" x2="4" y2="9" />
|
|
<line x1="2" y1="15" x2="4" y2="15" />
|
|
<line x1="20" y1="9" x2="22" y2="9" />
|
|
<line x1="20" y1="15" x2="22" y2="15" />
|
|
</svg>
|
|
</div>
|
|
<div class="stat-card-content">
|
|
<span class="stat-card-label">CPU 使用率</span>
|
|
<span class="stat-card-value" id="cpuPercent">0%</span>
|
|
<span class="stat-card-sub" id="cpuDetail">0 / 0 核心</span>
|
|
</div>
|
|
</div>
|
|
<div class="stat-card stat-card-mem" id="cardMem">
|
|
<div class="stat-card-icon">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
|
<rect x="3" y="3" width="18" height="18" rx="2" />
|
|
<path d="M7 7h4v4H7zM13 7h4v4h-4zM7 13h4v4H7zM13 13h4v4h-4z" />
|
|
</svg>
|
|
</div>
|
|
<div class="stat-card-content">
|
|
<span class="stat-card-label">内存使用率</span>
|
|
<span class="stat-card-value" id="memPercent">0%</span>
|
|
<span class="stat-card-sub" id="memDetail">0 / 0 GB</span>
|
|
</div>
|
|
</div>
|
|
<div class="stat-card stat-card-disk" id="cardDisk">
|
|
<div class="stat-card-icon">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
|
<ellipse cx="12" cy="5" rx="9" ry="3" />
|
|
<path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3" />
|
|
<path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5" />
|
|
</svg>
|
|
</div>
|
|
<div class="stat-card-content">
|
|
<span class="stat-card-label">磁盘使用率</span>
|
|
<span class="stat-card-value" id="diskPercent">0%</span>
|
|
<span class="stat-card-sub" id="diskDetail">0 / 0 GB</span>
|
|
</div>
|
|
</div>
|
|
<div class="stat-card stat-card-bandwidth" id="cardBandwidth">
|
|
<div class="stat-card-icon">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
|
<path d="M22 12h-4l-3 9L9 3l-3 9H2" />
|
|
</svg>
|
|
</div>
|
|
<div class="stat-card-content">
|
|
<span class="stat-card-label">实时总带宽</span>
|
|
<span class="stat-card-value" id="totalBandwidth">0 B/s</span>
|
|
<span class="stat-card-sub" id="bandwidthDetail">↓ 0 ↑ 0</span>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Center Charts -->
|
|
<section class="charts-section">
|
|
<!-- Network Traffic 24h Chart -->
|
|
<div class="chart-card chart-card-wide" id="networkChart">
|
|
<div class="chart-card-header">
|
|
<div class="chart-header-left">
|
|
<h2 class="chart-title">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" class="chart-title-icon">
|
|
<polyline points="22 12 18 12 15 21 9 3 6 12 2 12" />
|
|
</svg>
|
|
网络流量趋势 (24h)
|
|
</h2>
|
|
</div>
|
|
<div class="chart-legend">
|
|
<span class="legend-item"><span class="legend-dot legend-rx"></span>接收 (RX)</span>
|
|
<span class="legend-item"><span class="legend-dot legend-tx"></span>发送 (TX)</span>
|
|
<span class="legend-item disabled" id="legendP95" style="cursor: pointer;" title="点击切换 P95 线显示/隐藏">
|
|
<span class="legend-dot legend-p95"></span>95计费 (P95)
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div class="chart-body">
|
|
<canvas id="networkCanvas"></canvas>
|
|
</div>
|
|
<div class="chart-footer">
|
|
<div class="traffic-stat">
|
|
<span class="traffic-label">24h 接收总量</span>
|
|
<span class="traffic-value" id="traffic24hRx">0 B</span>
|
|
</div>
|
|
<div class="traffic-stat">
|
|
<span class="traffic-label">24h 发送总量</span>
|
|
<span class="traffic-value" id="traffic24hTx">0 B</span>
|
|
</div>
|
|
<div class="traffic-stat traffic-stat-p95">
|
|
<span class="traffic-label">95计费带宽</span>
|
|
<span class="traffic-value" id="trafficP95">0 B/s</span>
|
|
</div>
|
|
<div class="traffic-stat traffic-stat-total">
|
|
<span class="traffic-label">24h 总流量</span>
|
|
<span class="traffic-value" id="traffic24hTotal">0 B</span>
|
|
</div>
|
|
<div class="traffic-stat traffic-stat-time">
|
|
<span class="traffic-label">当前时间</span>
|
|
<span class="traffic-value" id="footerTime">00:00:00</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Server List -->
|
|
<section class="server-list-section" id="serverListSection">
|
|
<div class="chart-card">
|
|
<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">
|
|
<rect x="2" y="2" width="20" height="8" rx="2" />
|
|
<rect x="2" y="14" width="20" height="8" rx="2" />
|
|
<circle cx="6" cy="6" r="1" fill="currentColor" />
|
|
<circle cx="6" cy="18" r="1" fill="currentColor" />
|
|
</svg>
|
|
服务器详情
|
|
</h2>
|
|
<div class="chart-header-right">
|
|
<select id="sourceFilter" class="source-select">
|
|
<option value="all">所有数据源</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="server-table-wrap">
|
|
<table class="server-table" id="serverTable">
|
|
<thead>
|
|
<tr>
|
|
<th>状态</th>
|
|
<th>Job / 实例</th>
|
|
<th>数据源</th>
|
|
<th>CPU</th>
|
|
<th>内存</th>
|
|
<th>磁盘</th>
|
|
<th>网络 ↓</th>
|
|
<th>网络 ↑</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="serverTableBody">
|
|
<tr class="empty-row">
|
|
<td colspan="8">暂无数据 - 请先配置 Prometheus 数据源</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<div class="pagination-footer">
|
|
<div class="page-size-selector">
|
|
<span>每页显示</span>
|
|
<select id="pageSizeSelect" class="source-select">
|
|
<option value="10">10</option>
|
|
<option value="20" selected>20</option>
|
|
<option value="50">50</option>
|
|
<option value="100">100</option>
|
|
</select>
|
|
<span>条</span>
|
|
</div>
|
|
<div class="pagination-controls" id="paginationControls">
|
|
<!-- Pagination buttons will be injected here -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</main>
|
|
|
|
<!-- Settings Modal -->
|
|
<div class="modal-overlay" id="settingsModal">
|
|
<div class="modal">
|
|
<div class="modal-header">
|
|
<div class="modal-tabs">
|
|
<button class="modal-tab active" data-tab="prom">数据源管理</button>
|
|
<button class="modal-tab" data-tab="site">大屏设置</button>
|
|
</div>
|
|
<button class="modal-close" id="modalClose">×</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<!-- Prometheus Sources Tab -->
|
|
<div class="tab-content active" id="tab-prom">
|
|
<!-- Add Source Form -->
|
|
<div class="add-source-form" id="addSourceForm">
|
|
<h3>添加数据源</h3>
|
|
<div class="form-row">
|
|
<div class="form-group">
|
|
<label for="sourceName">名称</label>
|
|
<input type="text" id="sourceName" placeholder="例:生产环境" autocomplete="off">
|
|
</div>
|
|
<div class="form-group form-group-wide">
|
|
<label for="sourceUrl">Prometheus URL</label>
|
|
<input type="url" id="sourceUrl" placeholder="http://prometheus.example.com:9090" autocomplete="off">
|
|
</div>
|
|
</div>
|
|
<div class="form-row">
|
|
<div class="form-group form-group-wide">
|
|
<label for="sourceDesc">描述 (可选)</label>
|
|
<input type="text" id="sourceDesc" placeholder="数据源描述" autocomplete="off">
|
|
</div>
|
|
<div class="form-actions">
|
|
<button class="btn btn-test" id="btnTest">测试连接</button>
|
|
<button class="btn btn-add" id="btnAdd">添加</button>
|
|
</div>
|
|
</div>
|
|
<div class="form-message" id="formMessage"></div>
|
|
</div>
|
|
|
|
<!-- Source List -->
|
|
<div class="source-list" id="sourceList">
|
|
<h3>已配置数据源</h3>
|
|
<div class="source-items" id="sourceItems">
|
|
<div class="source-empty">暂无数据源</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Site Settings Tab -->
|
|
<div class="tab-content" id="tab-site">
|
|
<div class="site-settings-form">
|
|
<h3>自定义大屏展示</h3>
|
|
<div class="form-group">
|
|
<label for="pageNameInput">页面名称 (浏览器标签页标题)</label>
|
|
<input type="text" id="pageNameInput" placeholder="例:运维监控大屏">
|
|
</div>
|
|
<div class="form-group" style="margin-top: 15px;">
|
|
<label for="siteTitleInput">标题 (大屏左上角显示名称)</label>
|
|
<input type="text" id="siteTitleInput" placeholder="例:数据可视化展示大屏">
|
|
</div>
|
|
<div class="form-group" style="margin-top: 15px;">
|
|
<label for="logoUrlInput">Logo URL (图片链接,为空则显示默认图标)</label>
|
|
<input type="url" id="logoUrlInput" placeholder="https://example.com/logo.png">
|
|
</div>
|
|
<div class="form-group" style="margin-top: 15px;">
|
|
<label for="defaultThemeInput">默认主题</label>
|
|
<select id="defaultThemeInput"
|
|
style="padding: 10px 14px; background: var(--bg-input); border: 1px solid var(--border-color); border-radius: var(--radius-sm); color: var(--text-primary);">
|
|
<option value="dark">默认夜间模式</option>
|
|
<option value="light">默认白天模式</option>
|
|
<option value="auto">跟随浏览器/系统</option>
|
|
</select>
|
|
</div>
|
|
<div class="form-actions" style="margin-top: 25px; display: flex; justify-content: flex-end;">
|
|
<button class="btn btn-add" id="btnSaveSiteSettings">保存设置</button>
|
|
</div>
|
|
<div class="form-message" id="siteSettingsMessage"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Server Detail Modal -->
|
|
<div class="modal-overlay" id="serverDetailModal">
|
|
<div class="modal" style="max-width: 800px; width: 95%;">
|
|
<div class="modal-header">
|
|
<div style="display: flex; flex-direction: column;">
|
|
<h2 id="serverDetailTitle" style="margin-bottom: 0;">服务器详情</h2>
|
|
</div>
|
|
<button class="modal-close" id="serverDetailClose">×</button>
|
|
</div>
|
|
<div class="modal-body" id="serverDetailBody" style="padding: 0;">
|
|
<div id="detailLoading" style="text-align: center; padding: 40px; display: none;">
|
|
<div class="dot dot-pulse"
|
|
style="display: inline-block; width: 12px; height: 12px; background: var(--accent-indigo);"></div>
|
|
<span style="margin-left: 10px; color: var(--text-secondary);">正在从数据源读取详情...</span>
|
|
</div>
|
|
<div class="detail-container" id="detailContainer">
|
|
<!-- Metric Items are injected here -->
|
|
<div class="detail-metrics-list" id="detailMetricsList"></div>
|
|
|
|
<div class="detail-info-grid" id="detailInfoGrid">
|
|
<div class="info-item">
|
|
<span class="info-label">CPU 核心总数</span>
|
|
<span class="info-value" id="detailCpuCores">0 核心</span>
|
|
</div>
|
|
<div class="info-item">
|
|
<span class="info-label">物理内存总量</span>
|
|
<span class="info-value" id="detailMemTotal">0 GB</span>
|
|
</div>
|
|
<div class="info-item">
|
|
<span class="info-label">运行时间 (Uptime)</span>
|
|
<span class="info-value" id="detailUptime">0天 0小时</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Login Modal -->
|
|
<div class="modal-overlay" id="loginModalOverlay">
|
|
<div class="modal" style="max-width: 400px;">
|
|
<div class="modal-header">
|
|
<h2>用户登录</h2>
|
|
<button class="modal-close" id="closeLoginModal">×</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<form id="loginForm">
|
|
<div class="form-group">
|
|
<label for="username">用户名</label>
|
|
<input type="text" id="username" placeholder="请输入用户名" required>
|
|
</div>
|
|
<div class="form-group" style="margin-top: 16px;">
|
|
<label for="password">密码</label>
|
|
<input type="password" id="password" placeholder="请输入密码" required>
|
|
</div>
|
|
<div id="loginError" style="color: var(--accent-rose); font-size: 0.8rem; margin-top: 10px; display: none;">
|
|
</div>
|
|
<button type="submit" class="btn btn-primary"
|
|
style="width: 100%; margin-top: 24px; background: var(--gradient-primary); color: white; border: none; padding: 12px; border-radius: 8px; cursor: pointer;">登
|
|
录</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="/js/utils.js"></script>
|
|
<script src="/js/chart.js"></script>
|
|
<script src="/js/app.js"></script>
|
|
</body>
|
|
|
|
</html> |