Files
aler_dashboard_git/dashbaord.en-zh.php
T
2026-07-09 16:31:30 +08:00

1095 lines
38 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
session_start();
// 禁止页面缓存,解决标题刷新不生效
header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");
header("Pragma: no-cache");
header("Expires: Thu, 01 Jan 1970 00:00:00 GMT");
// 会话超时:30分钟无操作自动登出,与admin后台统一
$expire = 1800;
if (empty($_SESSION['user_id']) || (time() - $_SESSION['login_time'] > $expire)) {
session_destroy();
header("Location: login.php");
exit;
}
// 刷新登录计时
$_SESSION['login_time'] = time();
$userName = htmlspecialchars($_SESSION['username']);
$userRole = $_SESSION['role'];
$roleText = $userRole === 'admin' ? '管理员' : '只读用户';
// 读取全局系统配置(admin后台保存后同步生效)
$dbHost = "10.150.117.190";
$dbUser = "root";
$dbPwd = "hp93000";
$dbName = "alert_mail_stat";
$connConfig = mysqli_connect($dbHost, $dbUser, $dbPwd, $dbName);
mysqli_set_charset($connConfig, "utf8mb4");
$configSql = "SELECT k,v FROM sys_config";
$configRes = mysqli_query($connConfig, $configSql);
$sysConfig = [];
while ($row = mysqli_fetch_assoc($configRes)) {
$sysConfig[$row['k']] = $row['v'];
}
mysqli_close($connConfig);
// 赋值全局配置
$pageTitle = htmlspecialchars($sysConfig['page_title'] ?? "告警监控系统");
$themeColor = htmlspecialchars($sysConfig['theme_color'] ?? "#409eff");
$pageSize = $sysConfig['page_size'] ?? "20";
$footerText = htmlspecialchars($sysConfig['footer_text'] ?? "");
$sidebarRawLinks = $sysConfig['sidebar_links'] ?? "";
// 读取语言配置 zh=中文 en=英文
$currentLang = $sysConfig['lang'] ?? "zh";
// 全局多语言词典
$langMap = [
'zh' => [
'html_lang' => 'zh-CN',
'page_subtitle' => '爱德万测试 · 运维告警监控平台',
'monitor_panel' => '监控面板',
'alert_overview' => '告警监控总览',
'disk_capacity' => '服务器磁盘容量',
'quick_link' => '快捷外链',
'system_manage' => '系统管理',
'system_config' => '后台配置管理',
'current_user' => '当前用户:',
'sys_time' => '系统时间:',
'refresh_all' => '刷新全部数据',
'export_csv' => '导出告警CSV',
'logout' => '退出登录',
'lang_text' => '语言:',
'today_fire' => '今日触发告警',
'unresolved_now' => '当前未恢复告警',
'resolved_today' => '今日恢复告警',
'instance_total' => '故障实例总数',
'recovery_rate' => '告警恢复率',
'today_fire_sub' => 'Total Firing Today',
'unresolved_sub' => 'Unresolved Now',
'resolved_sub' => 'Resolved Today',
'instance_sub' => 'Affected Host Instance',
'rate_sub' => 'Daily Recovery Rate',
'top10_alert' => '今日告警频次 TOP 10',
'active_alert' => '当前未恢复活跃告警',
'alert_count' => '条未恢复故障',
'all_alert_log' => '今日全量告警明细列表',
'total_alert' => '条告警记录',
'tag' => '告警状态',
'alert_name' => '告警名称',
'instance_addr' => '实例地址',
'severity' => '告警级别',
'occur_time' => '故障发生时间',
'empty_normal' => '当前无未恢复告警,系统运行正常',
'empty_no_data' => '暂无今日告警数据',
'disk_stat' => '用户目录磁盘占用统计',
'filter_ip' => '筛选实例IP',
'export_disk' => '导出当前表格CSV',
'all_instance' => '全部实例',
'disk_loading' => '磁盘数据加载中...',
'no_disk_data' => '暂无磁盘指标数据',
'no_ip_data' => '该实例下无目录数据',
'csv_tip' => '正在生成CSV报表,请稍候...',
'alert_axis' => '告警次数',
'data_fail' => '数据加载失败,请重新登录',
'server_fail' => '接口数据加载失败,请检查后端服务',
'disk_api_fail' => '磁盘接口异常:',
'disk_request_fail' => '请求df.php接口失败,请检查服务',
'no_export_data' => '无数据可导出'
],
'en' => [
'html_lang' => 'en',
'page_subtitle' => 'Advantest · O&M Alert Monitor Platform',
'monitor_panel' => 'Monitor Panel',
'alert_overview' => 'Alert Overview',
'disk_capacity' => 'Server Disk Capacity',
'quick_link' => 'Quick Links',
'system_manage' => 'System Manage',
'system_config' => 'System Config',
'current_user' => 'User: ',
'sys_time' => 'System Time: ',
'refresh_all' => 'Refresh All Data',
'export_csv' => 'Export Alert CSV',
'logout' => 'Logout',
'lang_text' => 'Lang: ',
'today_fire' => 'Today Triggered Alerts',
'unresolved_now' => 'Unresolved Alerts',
'resolved_today' => 'Resolved Today',
'instance_total' => 'Affected Instances',
'recovery_rate' => 'Daily Recovery Rate',
'today_fire_sub' => 'Total Firing Today',
'unresolved_sub' => 'Unresolved Now',
'resolved_sub' => 'Resolved Today',
'instance_sub' => 'Affected Host Instance',
'rate_sub' => 'Daily Recovery Rate',
'top10_alert' => 'Top 10 Alert Frequency Today',
'active_alert' => 'Active Unresolved Alerts',
'alert_count' => ' unresolved alerts',
'all_alert_log' => 'Today Full Alert List',
'total_alert' => ' alert records',
'tag' => 'Status',
'alert_name' => 'Alert Name',
'instance_addr' => 'Instance IP',
'severity' => 'Severity',
'occur_time' => 'Occur Time',
'empty_normal' => 'No active alerts, system normal',
'empty_no_data' => 'No alert data today',
'disk_stat' => 'User Directory Disk Usage',
'filter_ip' => 'Filter IP: ',
'export_disk' => 'Export Table CSV',
'all_instance' => 'All Instances',
'disk_loading' => 'Loading disk data...',
'no_disk_data' => 'No disk data available',
'no_ip_data' => 'No directory data for this instance',
'csv_tip' => 'Generating CSV file, please wait...',
'alert_axis' => 'Alert Count',
'data_fail' => 'Data load failed, please relogin',
'server_fail' => 'API request failed, check backend service',
'disk_api_fail' => 'Disk API Error: ',
'disk_request_fail' => 'Request df.php failed, check service',
'no_export_data' => 'No data to export'
]
];
$t = $langMap[$currentLang];
// 解析侧边外链列表
$sidebarLinkList = [];
if (!empty($sidebarRawLinks)) {
$lines = explode("\n", $sidebarRawLinks);
foreach ($lines as $line) {
$line = trim($line);
if (empty($line)) continue;
$item = explode("|", $line, 2);
if (count($item) === 2) {
$sidebarLinkList[] = [
"name" => trim($item[0]),
"url" => trim($item[1])
];
}
}
}
?>
<!DOCTYPE html>
<html lang="<?php echo $t['html_lang']; ?>">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo $pageTitle; ?> - Advantest <?php echo $t['page_subtitle']; ?></title>
<link rel="stylesheet" href="/static/css/font-awesome.min.css">
<script src="/static/js/echarts.min.js"></script>
<script src="/static/js/jquery.min.js"></script>
<style>
*{box-sizing:border-box;margin:0;padding:0}
body{
background:#f9fafc;
font-family:"Inter","PingFang SC","Microsoft YaHei";
color:#1d2939;
line-height:1.6;
display:flex;
flex-direction:column;
height:100vh;
overflow:hidden;
}
/* 侧边导航菜单 */
.wrap-main{
display:flex;
flex:1;
overflow:hidden;
}
.sidebar{
width:240px;
background:#152c5b;
color:#e2e8f0;
flex-shrink:0;
overflow-y:auto;
}
.sidebar-brand{
padding:26px 24px;
border-bottom:1px solid rgba(255,255,255,0.1);
}
.sidebar-brand-en{
font-size:20px;
font-weight:700;
letter-spacing:1px;
color:#fff;
}
.sidebar-brand-cn{
font-size:13px;
color:#a5b4fc;
margin-top:4px;
}
.sidebar-menu{
padding:16px 0;
}
.menu-title{
padding:10px 24px;
font-size:12px;
color:#94a3b8;
letter-spacing:1px;
}
.menu-item{
display:flex;
align-items:center;
gap:12px;
padding:14px 24px;
cursor:pointer;
font-size:14px;
transition:0.2s;
color:#cbd5e1;
}
.menu-item i{
font-size:16px;
width:20px;
text-align:center;
}
.menu-item.active{
background:rgba(255,255,255,0.12);
color:#fff;
border-left:4px solid #3b82f6;
}
.menu-item:hover:not(.active){
background:rgba(255,255,255,0.06);
color:#fff;
}
/* 主内容区域 */
.main-content{
flex:1;
overflow-y:auto;
display:flex;
flex-direction:column;
}
/* 顶部导航栏 高端纯白渐变 */
.header{
background:#ffffff;
border-bottom:1px solid #eef2fb;
color:#152c5b;
padding:20px 40px;
display:flex;
justify-content:space-between;
align-items:center;
box-shadow:0 2px 12px rgba(21,44,91,0.05);
flex-shrink:0;
}
.header-left{
display:flex;
align-items:center;
gap:14px;
}
.header-brand-en{
font-size:24px;
font-weight:700;
letter-spacing:2px;
color:#152c5b;
}
.header-brand-cn{
font-size:15px;
color:#64748b;
}
.header-right{
display:flex;
align-items:center;
gap:18px;
font-size:14px;
flex-wrap:wrap;
}
.user-info,.time-info,.lang-box{
color:#475569;
display:flex;
align-items:center;
gap:6px;
}
.lang-box select{
padding:4px 8px;
border:1px solid #cbd5e1;
border-radius:6px;
}
.btn-base{
border:none;
padding:9px 20px;
border-radius:8px;
cursor:pointer;
font-size:14px;
display:flex;
align-items:center;
gap:6px;
transition:all 0.2s ease;
}
.btn-outline{
background:#ffffff;
color:#152c5b;
border:1px solid #cbd5e1;
}
.btn-outline:hover{
background:#f7f8fc;
border-color:#152c5b;
}
.btn-primary{
background:#152c5b;
color:#fff;
}
.btn-primary:hover{
background:#0f1e42;
}
.btn-success{
background:#10b981;
color:#fff;
}
.btn-success:hover{
background:#059669;
}
/* 日期选择框样式 */
#csvDatePicker{
padding:8px 10px;
border:1px solid #cbd5e1;
border-radius:8px;
font-size:14px;
}
/* 加载遮罩 */
.mask{
position:fixed;
left:0;top:0;
width:100%;height:100%;
background:rgba(255,255,255,0.85);
display:none;
z-index:999;
align-items:center;
justify-content:center;
}
.mask-box{
background:#fff;
padding:30px 40px;
border-radius:14px;
font-size:15px;
display:flex;
gap:14px;
align-items:center;
box-shadow:0 8px 30px rgba(21,44,91,0.12);
border:1px solid #eef2fb;
}
.loading{
display:inline-block;
width:20px;height:20px;
border:2px solid #e2e8f0;
border-top-color:#152c5b;
border-radius:50%;
animation:spin 0.8s linear infinite;
}
@keyframes spin{to{transform:rotate(360deg)}}
/* 容器内边距 */
.container{
padding:36px 40px;
max-width:100%;
flex:1;
}
/* 页面面板控制(切换显示隐藏) */
.page-panel{
display:none;
}
.page-panel.active{
display:block;
}
/* 统计卡片区域 */
.stat-cards{
display:grid;
grid-template-columns:repeat(5,1fr);
gap:28px;
margin-bottom:36px;
}
.stat-card{
background:#fff;
border-radius:16px;
padding:32px;
box-shadow:0 3px 18px rgba(21,44,91,0.06);
border:1px solid #eef2fb;
transition:transform 0.26s,box-shadow 0.26s;
}
.stat-card:hover{
transform:translateY(-6px);
box-shadow:0 10px 32px rgba(21,44,91,0.09);
}
.stat-header{
display:flex;
justify-content:space-between;
align-items:flex-start;
margin-bottom:20px;
}
.stat-title{
font-size:14px;
color:#64748b;
}
.stat-icon{
width:46px;height:46px;
border-radius:12px;
display:flex;
align-items:center;
justify-content:center;
font-size:20px;
}
.stat-card.fire .stat-icon{background:#fee2e2;color:#dc2626}
.stat-card.active .stat-icon{background:#fef3c7;color:#d97706}
.stat-card.resolve .stat-icon{background:#dcfce7;color:#16a34a}
.stat-card.instance .stat-icon{background:#dbeafe;color:#2563eb}
.stat-card.rate .stat-icon{background:#ffedd5;color:#ea580c}
.stat-value{
font-size:44px;
font-weight:700;
color:#152c5b;
letter-spacing:1px;
line-height:1.1;
}
.stat-card.active .stat-value{color:#d97706}
.stat-sub{
font-size:12px;
color:#94a3b8;
margin-top:8px;
}
/* 图表、表格通用卡片 */
.chart-section,.active-alert-section,.table-section,.disk-section{
margin-bottom:36px;
}
.base-card{
background:#fff;
border-radius:16px;
padding:32px;
box-shadow:0 3px 18px rgba(21,44,91,0.06);
border:1px solid #eef2fb;
}
.title-wrap{
display:flex;
justify-content:space-between;
align-items:center;
margin-bottom:26px;
padding-bottom:16px;
border-bottom:1px solid #eef2fb;
}
.title-left{
display:flex;
align-items:center;
gap:12px;
font-size:19px;
font-weight:600;
color:#152c5b;
}
.title-left i{color:#2563eb;font-size:21px}
.table-count{font-size:14px;color:#64748b}
.filter-box{
display:flex;
align-items:center;
gap:10px;
}
.filter-box label{
font-size:14px;
color:#475569;
}
#ipSelect{
padding:6px 10px;
border:1px solid #cbd5e0;
border-radius:6px;
font-size:14px;
min-width:200px;
}
#diskExportBtn{
padding:6px 14px;
background:#2b6cb0;
color:#fff;
border:none;
border-radius:6px;
cursor:pointer;
font-size:14px;
}
#diskExportBtn:hover{
background:#2c5282;
}
#chart1{height:420px;width:100%}
.active-alert-section .base-card{border:1px solid #fee2e2}
/* 表格通用样式 */
.table-wrap{overflow-x:auto}
table{width:100%;border-collapse:collapse;font-size:14px}
th{
background:#f9fafc;
color:#475569;
font-weight:600;
padding:16px 18px;
text-align:left;
border-bottom:2px solid #e2e8f0;
white-space:nowrap;
}
th i{margin-right:6px;font-size:13px;color:#94a3b8}
td{
padding:18px;
border-bottom:1px solid #f1f5f9;
color:#334155;
}
tbody tr{transition:background 0.2s}
tbody tr:hover{background:#f7f8fc}
tbody tr:nth-child(even){background:#fbfcfe}
/* 状态标签 */
.status-tag{
display:inline-flex;
align-items:center;
gap:5px;
padding:6px 14px;
border-radius:24px;
font-size:13px;
font-weight:500;
}
.status-tag.fire{background:#fee2e2;color:#dc2626}
.status-tag.resolve{background:#dcfce7;color:#16a34a}
.severity-tag{
display:inline-block;
padding:5px 12px;
border-radius:8px;
font-size:13px;
font-weight:500;
}
.severity-critical{background:#fee2e2;color:#dc2626}
.severity-warning{background:#ffedd5;color:#ea580c}
.severity-info{background:#dbeafe;color:#2563eb}
.num-big {
color: #c53030;
font-weight: 500;
}
/* 空数据提示 */
.empty-state{
text-align:center;
padding:80px 20px;
color:#94a3b8;
font-size:15px;
}
.empty-icon{
font-size:56px;
margin-bottom:18px;
opacity:0.35;
}
/* 响应式适配 */
@media (max-width:1440px){.stat-cards{grid-template-columns:repeat(3,1fr)}}
@media (max-width:992px){
.wrap-main{flex-direction:column;}
.sidebar{width:100%;height:auto}
}
@media (max-width:768px){
.header{padding:16px 24px;flex-direction:column;gap:16px;align-items:flex-start}
.header-right{flex-wrap:wrap}
.container{padding:24px}
.stat-cards{grid-template-columns:1fr;gap:20px}
.stat-card{padding:24px}
.base-card{padding:24px}
.filter-box{flex-wrap:wrap;}
}
</style>
</head>
<body>
<div class="mask" id="exportMask">
<div class="mask-box">
<span class="loading"></span>
<span><?php echo $t['csv_tip']; ?></span>
</div>
</div>
<div class="wrap-main">
<!-- 左侧侧边栏菜单 -->
<div class="sidebar">
<div class="sidebar-brand">
<div class="sidebar-brand-en">Advantest</div>
<div class="sidebar-brand-cn"><?php echo $t['page_subtitle']; ?></div>
</div>
<div class="sidebar-menu">
<div class="menu-title"><?php echo $t['monitor_panel']; ?></div>
<div class="menu-item active" data-page="alertPage">
<i class="fa fa-line-chart"></i>
<span><?php echo $t['alert_overview']; ?></span>
</div>
<div class="menu-item" data-page="diskPage">
<i class="fa fa-hdd-o"></i>
<span><?php echo $t['disk_capacity']; ?></span>
</div>
<!-- 后台配置快捷外链自动渲染 -->
<?php if (!empty($sidebarLinkList)): ?>
<div class="menu-title"><?php echo $t['quick_link']; ?></div>
<?php foreach ($sidebarLinkList as $link): ?>
<div class="menu-item" onclick="window.open('<?php echo htmlspecialchars($link['url']); ?>')">
<i class="fa fa-external-link"></i>
<span><?php echo htmlspecialchars($link['name']); ?></span>
</div>
<?php endforeach; ?>
<?php endif; ?>
<div class="menu-title"><?php echo $t['system_manage']; ?></div>
<div class="menu-item" onclick="location.href='admin.php'">
<i class="fa fa-cog"></i>
<span><?php echo $t['system_config']; ?></span>
</div>
</div>
</div>
<!-- 右侧主内容区 -->
<div class="main-content">
<div class="header">
<div class="header-left">
<div>
<div class="header-brand-en">Advantest</div>
<div class="header-brand-cn"><?php echo $t['page_subtitle']; ?></div>
</div>
</div>
<div class="header-right">
<span class="user-info"><i class="fa fa-user-circle"></i><?php echo $t['current_user']; ?><?php echo $userName; ?><?php echo $roleText; ?></span>
<span class="time-info"><i class="fa fa-clock-o"></i><?php echo $t['sys_time']; ?><span id="updateTime">--</span></span>
<!-- 语言切换下拉框 -->
<div class="lang-box">
<span><?php echo $t['lang_text']; ?></span>
<select id="langSwitch">
<option value="zh" <?php echo $currentLang==='zh'?'selected':''; ?>>中文</option>
<option value="en" <?php echo $currentLang==='en'?'selected':''; ?>>EN</option>
</select>
</div>
<button class="btn-base btn-outline" onclick="loadAllData()"><i class="fa fa-refresh"></i><?php echo $t['refresh_all']; ?></button>
<div style="display:flex;align-items:center;gap:8px;">
<input type="date" id="csvDatePicker">
<button class="btn-base btn-success" onclick="exportCsv()"><i class="fa fa-download"></i><?php echo $t['export_csv']; ?></button>
</div>
<button class="btn-base btn-primary" id="logoutBtn"><i class="fa fa-sign-out"></i><?php echo $t['logout']; ?></button>
</div>
</div>
<div class="container">
<!-- 告警监控面板 -->
<div class="page-panel active" id="alertPage">
<div class="stat-cards">
<div class="stat-card fire">
<div class="stat-header">
<div class="stat-title"><?php echo $t['today_fire']; ?></div>
<div class="stat-icon"><i class="fa fa-exclamation-triangle"></i></div>
</div>
<div class="stat-value" id="fire">0</div>
<div class="stat-sub"><?php echo $t['today_fire_sub']; ?></div>
</div>
<div class="stat-card active">
<div class="stat-header">
<div class="stat-title"><?php echo $t['unresolved_now']; ?></div>
<div class="stat-icon"><i class="fa fa-bolt"></i></div>
</div>
<div class="stat-value" id="activeAlert">0</div>
<div class="stat-sub"><?php echo $t['unresolved_sub']; ?></div>
</div>
<div class="stat-card resolve">
<div class="stat-header">
<div class="stat-title"><?php echo $t['resolved_today']; ?></div>
<div class="stat-icon"><i class="fa fa-check-circle"></i></div>
</div>
<div class="stat-value" id="resolve">0</div>
<div class="stat-sub"><?php echo $t['resolved_sub']; ?></div>
</div>
<div class="stat-card instance">
<div class="stat-header">
<div class="stat-title"><?php echo $t['instance_total']; ?></div>
<div class="stat-icon"><i class="fa fa-server"></i></div>
</div>
<div class="stat-value" id="instance">0</div>
<div class="stat-sub"><?php echo $t['instance_sub']; ?></div>
</div>
<div class="stat-card rate">
<div class="stat-header">
<div class="stat-title"><?php echo $t['recovery_rate']; ?></div>
<div class="stat-icon"><i class="fa fa-line-chart"></i></div>
</div>
<div class="stat-value" id="rate">0%</div>
<div class="stat-sub"><?php echo $t['rate_sub']; ?></div>
</div>
</div>
<!-- TOP10图表 -->
<div class="chart-section">
<div class="base-card">
<div class="title-wrap">
<div class="title-left"><i class="fa fa-bar-chart"></i><?php echo $t['top10_alert']; ?></div>
</div>
<div id="chart1"></div>
</div>
</div>
<!-- 未恢复活跃告警 -->
<div class="active-alert-section">
<div class="base-card">
<div class="title-wrap">
<div class="title-left"><i class="fa fa-exclamation-circle"></i><?php echo $t['active_alert']; ?></div>
<div class="table-count"><?php echo $currentLang==='zh'?'共 ':' '; ?><span id="activeCount">0</span><?php echo $t['alert_count']; ?></div>
</div>
<div class="table-wrap">
<table>
<thead>
<tr>
<th style="width:100px"><i class="fa fa-tag"></i><?php echo $t['tag']; ?></th>
<th><i class="fa fa-bullhorn"></i><?php echo $t['alert_name']; ?></th>
<th><i class="fa fa-desktop"></i><?php echo $t['instance_addr']; ?></th>
<th style="width:120px"><i class="fa fa-signal"></i><?php echo $t['severity']; ?></th>
<th style="width:200px"><i class="fa fa-calendar"></i><?php echo $t['occur_time']; ?></th>
</tr>
</thead>
<tbody id="active-table-body">
<tr>
<td colspan="5">
<div class="empty-state">
<div class="empty-icon"><i class="fa fa-check"></i></div>
<div><?php echo $t['empty_normal']; ?></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- 今日全量明细 -->
<div class="table-section">
<div class="base-card">
<div class="title-wrap">
<div class="title-left"><i class="fa fa-list-alt"></i><?php echo $t['all_alert_log']; ?></div>
<div class="table-count"><?php echo $currentLang==='zh'?'共 ':' '; ?><span id="totalCount">0</span><?php echo $t['total_alert']; ?></div>
</div>
<div class="table-wrap">
<table>
<thead>
<tr>
<th style="width:100px"><i class="fa fa-tag"></i><?php echo $t['tag']; ?></th>
<th><i class="fa fa-bullhorn"></i><?php echo $t['alert_name']; ?></th>
<th><i class="fa fa-desktop"></i><?php echo $t['instance_addr']; ?></th>
<th style="width:120px"><i class="fa fa-signal"></i><?php echo $t['severity']; ?></th>
<th style="width:200px"><i class="fa fa-calendar"></i><?php echo $t['occur_time']; ?></th>
</tr>
</thead>
<tbody id="table-body">
<tr>
<td colspan="5">
<div class="empty-state">
<div class="empty-icon"><i class="fa fa-folder-open-o"></i></div>
<div><?php echo $t['empty_no_data']; ?></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<!-- 磁盘容量监控面板 -->
<div class="page-panel" id="diskPage">
<div class="disk-section">
<div class="base-card">
<div class="title-wrap">
<div class="title-left"><i class="fa fa-hdd-o"></i><?php echo $t['disk_stat']; ?></div>
<div class="filter-box">
<label><?php echo $t['filter_ip']; ?></label>
<select id="ipSelect">
<option value="all"><?php echo $t['all_instance']; ?></option>
</select>
<button id="diskExportBtn"><?php echo $t['export_disk']; ?></button>
</div>
</div>
<div class="table-wrap">
<table>
<thead>
<tr>
<th>IP</th>
<th>Username</th>
<th>Path</th>
<th>Size(GB)</th>
</tr>
</thead>
<tbody id="diskTableBody">
<tr>
<td colspan="4" class="empty-state">
<div class="empty-icon"><i class="fa fa-spinner fa-spin"></i></div>
<div><?php echo $t['disk_loading']; ?></div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
const api = "./api/index.php";
let chartInstance = null;
let diskSourceData = [];
let currentDiskIp = "all";
// 当前语言
const currentLang = "<?php echo $currentLang; ?>";
// 语言切换接口
$("#langSwitch").change(function(){
const newLang = $(this).val();
fetch(api,{
method:"POST",
headers:{"Content-Type":"application/x-www-form-urlencoded"},
body:"act=save_lang&lang="+newLang
}).then(()=>{
// 带随机参数清除缓存刷新页面
location.href = "dashboard.php?_t="+new Date().getTime();
})
})
// 侧边菜单切换
$(".menu-item").click(function(){
const pageId = $(this).data("page");
if (!pageId) return;
$(".menu-item").removeClass("active");
$(this).addClass("active");
$(".page-panel").removeClass("active");
$("#"+pageId).addClass("active");
if(pageId === "diskPage") loadDiskData();
})
// 格式化时间
function formatTime(dateStr){
if(!dateStr || dateStr === '0001-01-01 00:00:00' || dateStr === null) return '-';
return dateStr;
}
// 告警级别标签
function getSeverityTag(severity){
const s = (severity || '').toLowerCase();
if(s==='critical') return `<span class="severity-tag severity-critical">${currentLang==='zh'?'严重':'Critical'}</span>`;
if(s==='warning') return `<span class="severity-tag severity-warning">${currentLang==='zh'?'警告':'Warning'}</span>`;
if(s==='info') return `<span class="severity-tag severity-info">${currentLang==='zh'?'信息':'Info'}</span>`;
return `<span class="severity-tag severity-info">${severity||'-'}</span>`;
}
// 加载未恢复告警
function loadActiveAlert(){
$.getJSON(api+"?act=active_firing",function(list){
$("#activeAlert").text(list.length);
$("#activeCount").text(list.length);
if(list.length===0){
$("#active-table-body").html(`<tr><td colspan="5"><div class="empty-state"><div class="empty-icon"><i class="fa fa-check"></i></div><div><?php echo $t['empty_normal']; ?></div></div></td></tr>`);
return;
}
let html="";
list.forEach(row=>{
const t=`<span class="status-tag fire"><i class="fa fa-bolt"></i>${currentLang==='zh'?'触发':'Firing'}</span>`;
html+=`<tr>
<td>${t}</td>
<td style="font-weight:500;color:#152c5b">${row.alert_name||'-'}</td>
<td style="font-family:Consolas,monospace;font-size:13px">${row.instance||'-'}</td>
<td>${getSeverityTag(row.severity)}</td>
<td style="color:#475569">${formatTime(row.receive_time)}</td>
</tr>`;
});
$("#active-table-body").html(html);
}).fail(()=>{
$("#activeAlert").text("-");
$("#active-table-body").html(`<tr><td colspan="5"><div class="empty-state" style="color:#dc2626"><div class="empty-icon"><i class="fa fa-exclamation-circle"></i></div><div><?php echo $t['data_fail']; ?></div></div></td></tr>`);
});
}
// 统计卡片数据
function loadStats(){
$.getJSON(api+"?act=day_total",function(res){
const fire=parseInt(res.fire_count)||0;
const resolve=parseInt(res.resolve_count)||0;
const ins=parseInt(res.instance_num)||0;
const rate=fire>0?Math.round((resolve/fire)*100):0;
$("#fire").text(fire);
$("#resolve").text(resolve);
$("#instance").text(ins);
$("#rate").text(rate+"%");
}).fail(()=>{$("#fire,#resolve,#instance,#rate").text("-");});
}
// 图表加载
function loadChart(){
$.getJSON(api+"?act=top_alert",function(list){
if(!chartInstance){
chartInstance=echarts.init(document.getElementById("chart1"));
window.addEventListener("resize",()=>chartInstance.resize());
}
const names=list.map(i=>i.alert_name);
const cnt=list.map(i=>i.cnt);
chartInstance.setOption({
tooltip:{trigger:"axis",axisPointer:{type:"shadow"}},
grid:{left:"3%",right:"4%",bottom:"18%",top:"8%",containLabel:true},
xAxis:{
type:"category",
data:names,
axisLabel:{rotate:30,fontSize:12,color:"#64748b"},
axisLine:{lineStyle:{color:"#e2e8f0"}},
axisTick:{show:false}
},
yAxis:{
type:"value",
name:"<?php echo $t['alert_axis']; ?>",
nameTextStyle:{color:"#64748b",fontSize:13},
axisLabel:{color:"#64748b",fontSize:12},
splitLine:{lineStyle:{color:"#f1f5f9",type:"dashed"}},
axisLine:{show:false}
},
series:[{
type:"bar",
data:cnt,
barWidth:"48%",
itemStyle:{
color:new echarts.graphic.LinearGradient(0,0,0,1,[
{offset:0,color:"#3b82f6"},
{offset:1,color:"#1d4ed8"}
]),
borderRadius:[6,6,0,0]
},
label:{show:true,position:"top",fontSize:12,color:"#334155"}
}]
});
});
}
// 告警明细表格
function loadTable(){
$.getJSON(api+"?act=log_list",function(list){
$("#totalCount").text(list.length);
if(list.length===0){
$("#table-body").html(`<tr><td colspan="5"><div class="empty-state"><div class="empty-icon"><i class="fa fa-folder-open-o"></i></div><div><?php echo $t['empty_no_data']; ?></div></div></td></tr>`);
return;
}
let html="";
list.forEach(row=>{
const typeText = row.alert_type == 1
? `<span class="status-tag fire"><i class="fa fa-bolt"></i>${currentLang==='zh'?'触发':'Firing'}</span>`
: `<span class="status-tag resolve"><i class="fa fa-check"></i>${currentLang==='zh'?'恢复':'Resolved'}</span>`;
html += `<tr>
<td>${typeText}</td>
<td style="font-weight:500;color:#152c5b">${row.alert_name || '-'}</td>
<td style="font-family:Consolas,monospace;font-size:13px">${row.instance || '-'}</td>
<td>${getSeverityTag(row.severity)}</td>
<td style="color:#475569">${formatTime(row.receive_time)}</td>
</tr>`;
});
$("#table-body").html(html);
}).fail(()=>{
$("#table-body").html(`<tr><td colspan="5"><div class="empty-state" style="color:#dc2626"><div class="empty-icon"><i class="fa fa-exclamation-circle"></i></div><div><?php echo $t['server_fail']; ?></div></div></td></tr>`);
});
}
// 更新顶部时间
function updateTime() {
const now = new Date();
const Y = now.getFullYear();
const M = String(now.getMonth() + 1).padStart(2, '0');
const D = String(now.getDate()).padStart(2, '0');
const h = String(now.getHours()).padStart(2, '0');
const m = String(now.getMinutes()).padStart(2, '0');
const s = String(now.getSeconds()).padStart(2, '0');
$("#updateTime").text(`${Y}-${M}-${D} ${h}:${m}:${s}`);
}
// 刷新全部数据
function loadAllData() {
loadStats();
loadChart();
loadTable();
loadActiveAlert();
updateTime();
if($("#diskPage").hasClass("active")) loadDiskData();
}
// 导出CSV
function exportCsv(){
const mask = $("#exportMask");
mask.css("display","flex");
const selectDate = $("#csvDatePicker").val().trim();
let exportUrl = api + "?act=export_csv";
if(selectDate) exportUrl += "&date=" + encodeURIComponent(selectDate);
window.open(exportUrl, "_blank");
setTimeout(()=>mask.hide(),5000);
}
// 磁盘数据加载
function loadDiskData(){
const tbody = $("#diskTableBody");
tbody.html(`<tr><td colspan="4" class="empty-state"><div class="empty-icon"><i class="fa fa-spinner fa-spin"></i></div><div><?php echo $t['disk_loading']; ?></div></td></tr>`);
$.getJSON("./api/df.php").done(ret=>{
if(ret.code !== 0){
tbody.html(`<tr><td colspan="4" class="empty-state" style="color:#dc2626"><?php echo $t['disk_api_fail']; ?>${ret.msg}</td></tr>`);
return;
}
diskSourceData = ret.list;
if(diskSourceData.length === 0){
tbody.html(`<tr><td colspan="4" class="empty-state"><?php echo $t['no_disk_data']; ?></td></tr>`);
return;
}
const ipSel = $("#ipSelect");
ipSel.find("option:not([value='all'])").remove();
const ipSet = new Set();
diskSourceData.forEach(item=>ipSet.add(item.instance_ip));
ipSet.forEach(ip=>ipSel.append(`<option value="${ip}">${ip}</option>`));
currentDiskIp = "all";
renderDiskTable("all");
}).fail(()=>{
tbody.html(`<tr><td colspan="4" class="empty-state" style="color:#dc2626"><?php echo $t['disk_request_fail']; ?></td></tr>`);
})
}
// 渲染磁盘表格
function renderDiskTable(filterIp){
currentDiskIp = filterIp;
const tbody = $("#diskTableBody");
tbody.html("");
let showList = filterIp === "all" ? diskSourceData : diskSourceData.filter(i=>i.instance_ip === filterIp);
if(showList.length === 0){
tbody.html(`<tr><td colspan="4" class="empty-state"><?php echo $t['no_ip_data']; ?></td></tr>`);
return;
}
showList.forEach(row=>{
tbody.append(`
<tr>
<td>${row.instance_ip}</td>
<td>${row.username}</td>
<td>${row.directory}</td>
<td class="num-big">${row.size_gb}GB</td>
</tr>
`)
})
}
// 导出磁盘CSV
$("#diskExportBtn").click(function(){
let showList = currentDiskIp === "all" ? diskSourceData : diskSourceData.filter(i=>i.instance_ip === currentDiskIp);
if(showList.length === 0){
alert("<?php echo $t['no_export_data']; ?>");
return;
}
let csv = "\uFEFFIP,Username,Path,Size(GB)\n";
showList.forEach(item=>{
const line = [
`"${item.instance_ip}"`,
`"${item.username}"`,
`"${item.directory}"`,
`"${item.size_gb}GB"`
];
csv += line.join(",") + "\n";
})
const blob = new Blob([csv], {type:"text/csv;charset=utf-8"});
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "disk_"+new Date().getTime()+".csv";
a.click();
URL.revokeObjectURL(url);
})
// IP筛选切换
$("#ipSelect").change(function(){
renderDiskTable($(this).val());
})
// 页面初始化
$(function() {
loadAllData();
setInterval(loadAllData, 60000);
setInterval(updateTime, 1000);
$("#logoutBtn").click(function(){
location.href="logout.php";
})
});
</script>
</body>
</html>