monitor-ai/preview/dashboard.html

1261 lines
96 KiB
HTML
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.

<!DOCTYPE html>
<html lang="zh-CN" class="">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>monitor-ai — 告警监控中心 (预览)</title>
<style>
/* ==================== 设计令牌(来自 tlyq-design-system.css ==================== */
:root {
--bg: #f8fafc;
--bg-subtle: #f1f5f9;
--surface: #ffffff;
--surface-raised: #ffffff;
--fg: #0f172a;
--fg-subtle: #334155;
--muted: #475569;
--muted-subtle: #94a3b8;
--border: #e2e8f0;
--border-subtle: #f1f5f9;
--accent: #4f46e5;
--accent-hover: #4338ca;
--accent-active: #3730a3;
--accent-subtle: #eef2ff;
--accent-text: #ffffff;
--success: #059669;
--success-subtle: #ecfdf5;
--warning: #d97706;
--warning-subtle: #fffbeb;
--danger: #dc2626;
--danger-subtle: #fef2f2;
--info: #4f46e5;
--info-subtle: #eef2ff;
--font-display: -apple-system, BlinkMacSystemFont, 'SF Pro Display', system-ui, sans-serif;
--font-body: -apple-system, BlinkMacSystemFont, 'SF Pro Text', system-ui, sans-serif;
--font-mono: 'JetBrains Mono', ui-monospace, Menlo, monospace;
--text-xs: 0.75rem; --text-sm: 0.875rem; --text-base: 1rem; --text-lg: 1.125rem;
--text-xl: 1.25rem; --text-2xl: 1.5rem; --text-3xl: 1.875rem;
--space-1: 0.25rem; --space-2: 0.5rem; --space-3: 0.75rem; --space-4: 1rem;
--space-5: 1.25rem; --space-6: 1.5rem; --space-8: 2rem;
--radius-sm: 6px; --radius-md: 8px; --radius-lg: 12px; --radius-xl: 16px; --radius-full: 9999px;
--shadow-sm: 0 1px 3px rgba(0,0,0,.06), 0 1px 2px rgba(0,0,0,.04);
--shadow-md: 0 4px 6px -1px rgba(0,0,0,.08);
--shadow-lg: 0 10px 15px -3px rgba(0,0,0,.1);
--transition-fast: 150ms cubic-bezier(0.4,0,0.2,1);
--transition-normal: 200ms cubic-bezier(0.4,0,0.2,1);
--z-sticky: 200;
--z-overlay: 300;
--sidebar-width: 240px;
--header-height: 56px;
}
:root.dark {
--bg: #020617; --bg-subtle: #0f172a; --surface: #0f172a; --surface-raised: #1e293b;
--fg: #f8fafc; --fg-subtle: #cbd5e1; --muted: #94a3b8; --muted-subtle: #64748b;
--border: #1e293b; --border-subtle: #0f172a;
--accent: #6366f1; --accent-hover: #818cf8; --accent-active: #4f46e5; --accent-subtle: rgba(79,70,229,.15);
--accent-text: #ffffff;
--success: #34d399; --success-subtle: rgba(52,211,153,.1);
--warning: #fbbf24; --warning-subtle: rgba(251,191,36,.1);
--danger: #f87171; --danger-subtle: rgba(248,113,113,.1);
--info: #818cf8; --info-subtle: rgba(99,102,241,.1);
--shadow-sm: 0 1px 3px rgba(0,0,0,.3); --shadow-md: 0 4px 6px -1px rgba(0,0,0,.3);
}
/* ==================== 全局重置 ==================== */
*,*::before,*::after { box-sizing:border-box; margin:0; padding:0; }
html { font-size:16px; -webkit-font-smoothing:antialiased; }
body { font-family:var(--font-body); font-size:var(--text-sm); line-height:1.5; color:var(--fg); background:var(--bg); }
/* ==================== 布局 ==================== */
.app { display:flex; height:100vh; overflow:hidden; }
.sidebar {
width:var(--sidebar-width); height:100vh; background:var(--surface);
border-right:1px solid var(--border); display:flex; flex-direction:column;
flex-shrink:0; position:fixed; left:0; top:0; bottom:0; z-index:var(--z-sticky);
}
.sidebar-brand { display:flex; align-items:center; gap:var(--space-3); padding:0 var(--space-6); height:var(--header-height); border-bottom:1px solid var(--border); }
.sidebar-logo { width:32px; height:32px; border-radius:var(--radius-lg); background:var(--accent); color:var(--accent-text); display:flex; align-items:center; justify-content:center; font-weight:700; font-size:var(--text-sm); flex-shrink:0; }
.sidebar-title { font-family:var(--font-display); font-size:var(--text-base); font-weight:600; color:var(--fg); }
.sidebar-nav { flex:1; overflow-y:auto; padding:var(--space-4) var(--space-3); }
.sidebar-section { margin-bottom:var(--space-6); }
.sidebar-section-title { font-size:var(--text-xs); font-weight:600; color:var(--muted); text-transform:uppercase; letter-spacing:0.1em; padding:var(--space-2) var(--space-3); margin-bottom:var(--space-1); }
.sidebar-item { display:flex; align-items:center; gap:var(--space-3); padding:var(--space-2) var(--space-3); font-size:var(--text-sm); color:var(--fg-subtle); text-decoration:none; border-radius:var(--radius-md); transition:all var(--transition-fast); margin-bottom:var(--space-1); }
.sidebar-item:hover { background:var(--bg-subtle); color:var(--fg); }
.sidebar-item.active { background:var(--accent-subtle); color:var(--accent); font-weight:500; }
.sidebar-item-icon { width:20px; height:20px; display:flex; align-items:center; justify-content:center; flex-shrink:0; }
.sidebar-user { padding:var(--space-3); border-top:1px solid var(--border); }
.sidebar-user-inner { display:flex; align-items:center; gap:var(--space-3); padding:var(--space-2); border-radius:var(--radius-md); }
.sidebar-user-avatar { width:32px; height:32px; border-radius:var(--radius-full); background:var(--accent-subtle); color:var(--accent); display:flex; align-items:center; justify-content:center; font-weight:600; font-size:var(--text-xs); }
.sidebar-user-name { font-size:var(--text-sm); font-weight:500; color:var(--fg); }
.sidebar-user-role { font-size:var(--text-xs); color:var(--muted); }
.main { flex:1; margin-left:var(--sidebar-width); overflow-y:auto; }
.header { height:var(--header-height); background:var(--surface); border-bottom:1px solid var(--border); position:sticky; top:0; z-index:var(--z-sticky); backdrop-filter:blur(10px); }
.header-inner { display:flex; align-items:center; justify-content:space-between; height:100%; padding:0 var(--space-6); }
.header-left { display:flex; align-items:center; gap:var(--space-3); }
.header-breadcrumb { font-size:var(--text-sm); color:var(--muted); }
.header-breadcrumb span { color:var(--fg); font-weight:500; }
.header-right { display:flex; align-items:center; gap:var(--space-4); }
.content { padding:var(--space-6); max-width:1440px; }
/* ==================== 统计卡片 ==================== */
.stats-grid { display:grid; grid-template-columns:repeat(4,1fr); gap:var(--space-6); margin-bottom:var(--space-6); }
.stat-card { background:var(--surface); border:1px solid var(--border); border-radius:var(--radius-xl); padding:var(--space-6); transition:all var(--transition-fast); }
.stat-card:hover { border-color:var(--accent); box-shadow:var(--shadow-md); }
.stat-card-header { display:flex; align-items:center; justify-content:space-between; margin-bottom:var(--space-4); }
.stat-card-title { font-size:var(--text-sm); font-weight:500; color:var(--muted); }
.stat-card-icon { width:40px; height:40px; border-radius:var(--radius-lg); display:flex; align-items:center; justify-content:center; font-size:var(--text-lg); }
.stat-card-value { font-family:var(--font-display); font-size:var(--text-3xl); font-weight:600; color:var(--fg); margin-bottom:var(--space-2); }
.stat-card-sub { font-size:var(--text-xs); color:var(--muted); }
/* ==================== 服务状态列表 ==================== */
.service-list { display:flex; flex-direction:column; gap:var(--space-2); }
.service-card { background:var(--surface); border:1px solid var(--border); border-radius:var(--radius-lg); overflow:hidden; transition:all var(--transition-fast); }
.service-card:hover { border-color:var(--accent-subtle); }
.service-card-header { display:flex; align-items:center; justify-content:space-between; padding:var(--space-4) var(--space-6); cursor:pointer; user-select:none; }
.service-card-header:hover { background:var(--bg-subtle); }
.service-card-left { display:flex; align-items:center; gap:var(--space-4); }
.service-card-right { display:flex; align-items:center; gap:var(--space-4); font-size:var(--text-sm); color:var(--muted); }
.service-card-body { display:none; padding:0 var(--space-6) var(--space-4); border-top:1px solid var(--border-subtle); }
.service-card.open .service-card-body { display:block; }
.service-card-body-inner { padding-top:var(--space-4); display:flex; flex-direction:column; gap:var(--space-3); }
.check-detail { display:flex; align-items:center; gap:var(--space-3); padding:var(--space-2) var(--space-3); background:var(--bg-subtle); border-radius:var(--radius-md); font-size:var(--text-sm); }
/* ==================== 状态徽标 ==================== */
.status-badge { display:inline-flex; align-items:center; gap:var(--space-1); padding:2px var(--space-2); font-size:var(--text-xs); font-weight:500; border-radius:var(--radius-full); }
.status-normal { background:var(--success-subtle); color:var(--success); }
.status-abnormal { background:var(--danger-subtle); color:var(--danger); }
.status-unknown { background:var(--warning-subtle); color:var(--warning); }
.status-dot { width:8px; height:8px; border-radius:50%; flex-shrink:0; }
.status-dot-normal { background:var(--success); }
.status-dot-abnormal { background:var(--danger); animation:pulse 2s infinite; }
.status-dot-unknown { background:var(--warning); }
@keyframes pulse { 0%,100% { opacity:1; } 50% { opacity:0.4; } }
/* ==================== 按钮 ==================== */
.btn { display:inline-flex; align-items:center; justify-content:center; gap:var(--space-2); padding:var(--space-2) var(--space-4); font-family:var(--font-body); font-size:var(--text-sm); font-weight:500; border-radius:var(--radius-md); border:1px solid transparent; cursor:pointer; transition:all var(--transition-fast); white-space:nowrap; }
.btn-primary { background:var(--accent); color:var(--accent-text); border-color:var(--accent); }
.btn-primary:hover { background:var(--accent-hover); }
.btn-secondary { background:transparent; color:var(--fg); border-color:var(--border); }
.btn-secondary:hover { background:var(--bg-subtle); }
.btn-sm { padding:var(--space-1) var(--space-3); font-size:var(--text-xs); }
.btn-ghost { background:transparent; color:var(--fg-subtle); }
.btn-ghost:hover { background:var(--bg-subtle); color:var(--fg); }
/* ==================== 设置页 ==================== */
.section-title { font-family:var(--font-display); font-size:var(--text-2xl); font-weight:600; color:var(--fg); margin-bottom:var(--space-6); }
.channel-health { display:flex; gap:var(--space-6); margin-bottom:var(--space-6); }
.health-item { display:flex; align-items:center; gap:var(--space-2); font-size:var(--text-sm); }
.health-dot { width:8px; height:8px; border-radius:50%; }
.health-ok { background:var(--success); }
.health-fail { background:var(--danger); }
.card { background:var(--surface); border:1px solid var(--border); border-radius:var(--radius-xl); overflow:hidden; }
.card-header { padding:var(--space-4) var(--space-6); border-bottom:1px solid var(--border); display:flex; align-items:center; justify-content:space-between; }
.card-title { font-size:var(--text-base); font-weight:600; color:var(--fg); }
.card-body { padding:var(--space-6); }
.form-group { margin-bottom:var(--space-4); }
.form-label { display:block; font-size:var(--text-sm); font-weight:500; color:var(--fg); margin-bottom:var(--space-2); }
.form-input { width:100%; padding:var(--space-2) var(--space-3); font-size:var(--text-sm); background:var(--surface); color:var(--fg); border:1px solid var(--border); border-radius:var(--radius-md); transition:all var(--transition-fast); font-family:var(--font-mono); }
.form-input:focus { outline:none; border-color:var(--accent); box-shadow:0 0 0 3px var(--accent-subtle); }
.form-checkbox { display:flex; align-items:center; gap:var(--space-2); cursor:pointer; }
.form-checkbox input[type="checkbox"] { width:16px; height:16px; accent-color:var(--accent); }
.form-row { display:grid; grid-template-columns:1fr 1fr 1fr; gap:var(--space-4); }
.form-actions { display:flex; justify-content:flex-end; gap:var(--space-3); margin-top:var(--space-6); }
/* ==================== 主题切换 ==================== */
.theme-toggle { width:36px; height:36px; border-radius:var(--radius-md); border:1px solid var(--border); background:var(--surface); cursor:pointer; display:flex; align-items:center; justify-content:center; color:var(--fg-subtle); font-size:var(--text-lg); transition:all var(--transition-fast); }
.theme-toggle:hover { background:var(--bg-subtle); }
/* ==================== 标签 ==================== */
.tag { display:inline-flex; align-items:center; padding:1px var(--space-2); font-size:var(--text-xs); font-weight:500; border-radius:var(--radius-full); }
.tag-indigo { background:var(--accent-subtle); color:var(--accent); }
.tag-green { background:var(--success-subtle); color:var(--success); }
.tag-red { background:var(--danger-subtle); color:var(--danger); }
/* ==================== Toast ==================== */
.toast { position:fixed; top:var(--space-4); right:var(--space-4); min-width:300px; background:var(--surface); border:1px solid var(--border); border-radius:var(--radius-lg); box-shadow:var(--shadow-lg); z-index:700; padding:var(--space-4); transform:translateX(100%); opacity:0; transition:all var(--transition-normal); }
.toast.show { transform:translateX(0); opacity:1; }
.toast-title { font-size:var(--text-sm); font-weight:600; color:var(--fg); margin-bottom:var(--space-1); }
.toast-body { font-size:var(--text-sm); color:var(--fg-subtle); }
.badge { display:inline-flex; align-items:center; justify-content:center; min-width:20px; height:20px; padding:0 var(--space-2); font-size:var(--text-xs); font-weight:600; border-radius:var(--radius-full); }
.badge-success { background:var(--success); color:#fff; }
.badge-danger { background:var(--danger); color:#fff; }
/* ==================== 卡片式仪表盘 ==================== */
.card-grid { display:grid; grid-template-columns:repeat(3,1fr); gap:var(--space-4); margin-bottom:var(--space-6); }
@media (max-width:1200px) { .card-grid { grid-template-columns:repeat(2,1fr); } }
@media (max-width:768px) { .card-grid { grid-template-columns:1fr; } }
.svc-card { background:var(--surface); border:1px solid var(--border); border-radius:var(--radius-xl); padding:var(--space-5); transition:all var(--transition-fast); cursor:pointer; }
.svc-card:hover { box-shadow:var(--shadow-sm); }
.svc-card.abnormal { background:var(--danger-subtle); border-color:var(--danger); }
.svc-card.abnormal:hover { border-color:var(--danger); box-shadow:0 0 0 4px var(--danger-subtle); }
.svc-card-header { display:flex; align-items:center; justify-content:space-between; margin-bottom:var(--space-2); }
.svc-card-name { display:flex; align-items:center; gap:var(--space-2); font-weight:600; font-size:var(--text-sm); color:var(--fg); }
.svc-card-error { font-size:var(--text-xs); color:var(--danger); line-height:1.3; }
.svc-card-body-expand { display:none; margin-top:var(--space-2); padding-top:var(--space-2); border-top:1px solid var(--border); }
.svc-card.open .svc-card-body-expand { display:block; }
.svc-card-footer { display:flex; align-items:center; justify-content:space-between; margin-top:var(--space-2); }
.svc-card-countdown { font-size:var(--text-lg); font-weight:600; color:var(--danger); font-family:var(--font-mono); }
.svc-card-label { font-size:var(--text-xs); color:var(--muted); }
/* ==================== 方案 A统计条 ==================== */
.stat-bar { display:flex; align-items:center; gap:var(--space-8); padding:var(--space-4) var(--space-6); background:var(--surface); border:1px solid var(--border); border-radius:var(--radius-xl); margin-bottom:var(--space-6); }
.stat-bar-item { display:flex; align-items:center; gap:var(--space-3); }
.stat-bar-dot { width:10px; height:10px; border-radius:50%; flex-shrink:0; }
.stat-bar-dot-success { background:var(--success); }
.stat-bar-dot-danger { background:var(--danger); animation:pulse 2s infinite; }
.stat-bar-dot-warning { background:var(--warning); }
.stat-bar-value { font-family:var(--font-display); font-size:var(--text-2xl); font-weight:600; color:var(--fg); }
.stat-bar-label { font-size:var(--text-sm); color:var(--muted); }
.stat-bar-divider { width:1px; height:32px; background:var(--border); }
.stat-bar-right { margin-left:auto; font-size:var(--text-sm); color:var(--muted); }
/* ==================== Pro 仪表盘Dark-First / 专业监控风格) ==================== */
.page-pro { background:var(--bg); min-height:100%; }
.page-pro .content-area { padding:var(--space-8) var(--space-8); max-width:1440px; }
/* 页面标题区 */
.pro-header { display:flex; align-items:center; justify-content:space-between; margin-bottom:var(--space-8); }
.pro-title { font-family:var(--font-display); font-size:var(--text-3xl); font-weight:700; color:var(--fg); letter-spacing:var(--tracking-tight); }
.pro-title-sub { font-size:var(--text-sm); color:var(--muted); margin-top:var(--space-1); font-weight:400; }
.pro-actions { display:flex; align-items:center; gap:var(--space-3); }
/* 统计条 */
.pro-statbar { display:grid; grid-template-columns:1fr 1px 1fr 1px 1fr 1fr; gap:var(--space-6); align-items:center; padding:var(--space-6) var(--space-8); background:var(--surface); border:1px solid var(--border); border-radius:var(--radius-2xl); margin-bottom:var(--space-8); }
.pro-statbar-divider { width:100%; height:1px; background:var(--border); }
@media (min-width:768px) { .pro-statbar-divider { width:1px; height:48px; } }
.pro-stat-item { display:flex; flex-direction:column; gap:var(--space-1); }
.pro-stat-item-label { font-size:var(--text-xs); color:var(--muted); font-weight:500; text-transform:uppercase; letter-spacing:var(--tracking-widest); }
.pro-stat-item-value { font-family:var(--font-display); font-size:var(--text-2xl); font-weight:700; color:var(--fg); }
.pro-stat-item-sub { font-size:var(--text-xs); color:var(--muted-subtle); }
/* 服务网格 */
.pro-grid { display:grid; grid-template-columns:repeat(4,1fr); gap:var(--space-4); }
@media (max-width:1200px) { .pro-grid { grid-template-columns:repeat(3,1fr); } }
@media (max-width:900px) { .pro-grid { grid-template-columns:repeat(2,1fr); } }
@media (max-width:600px) { .pro-grid { grid-template-columns:1fr; } }
.pro-svc { position:relative; background:var(--surface); border:1px solid var(--border); border-radius:var(--radius-xl); padding:var(--space-5); transition:all var(--transition-normal); cursor:pointer; overflow:hidden; }
.pro-svc::before { content:''; position:absolute; top:0; left:0; right:0; height:3px; background:transparent; transition:background var(--transition-normal); border-radius:var(--radius-xl) var(--radius-xl) 0 0; }
.pro-svc:hover { border-color:var(--accent); box-shadow:var(--shadow-lg); transform:translateY(-2px); }
.pro-svc.abnormal { border-color:var(--danger); background:var(--danger-subtle); }
.pro-svc.abnormal::before { background:var(--danger); }
.pro-svc-top { display:flex; align-items:flex-start; justify-content:space-between; margin-bottom:var(--space-3); }
.pro-svc-name { font-weight:600; font-size:var(--text-sm); color:var(--fg); display:flex; align-items:center; gap:var(--space-2); }
.pro-svc-status-dot { width:8px; height:8px; border-radius:50%; flex-shrink:0; }
.pro-svc-status-dot.ok { background:var(--success); box-shadow:0 0 8px var(--success); }
.pro-svc-status-dot.err { background:var(--danger); box-shadow:0 0 12px var(--danger); animation:pro-pulse 2s infinite; }
@keyframes pro-pulse { 0%,100% { box-shadow:0 0 8px var(--danger); } 50% { box-shadow:0 0 20px var(--danger); } }
.pro-svc-body { display:flex; flex-direction:column; gap:var(--space-2); }
.pro-svc-status-text { font-family:var(--font-mono); font-size:var(--text-xl); font-weight:700; }
.pro-svc-status-text.ok { color:var(--success); }
.pro-svc-status-text.err { color:var(--danger); }
.pro-svc-desc { font-size:var(--text-xs); color:var(--muted); line-height:1.4; }
.pro-svc-footer { display:flex; align-items:center; justify-content:space-between; margin-top:var(--space-3); padding-top:var(--space-3); border-top:1px solid var(--border-subtle); }
.pro-svc-footer-label { font-size:var(--text-xs); color:var(--muted-subtle); }
.pro-svc-expand { display:none; margin-top:var(--space-3); padding-top:var(--space-3); border-top:1px solid var(--border); }
.pro-svc.open .pro-svc-expand { display:block; }
.pro-svc-expand-item { display:flex; align-items:center; gap:var(--space-2); padding:var(--space-1) 0; font-size:var(--text-xs); color:var(--fg-subtle); }
/* ==================== Design 仪表盘(精密仪器风格,复用设计系统变量) ==================== */
/* 仅覆盖仪表盘特有的风格,基础色/间距/圆角等均使用 :root 变量 */
.ds-root {
position:relative; min-height:calc(100vh - var(--header-height));
background:var(--bg); color:var(--fg);
font-family:var(--font-body);
padding:40px 48px;
overflow:hidden;
}
/* 暗色模式点阵网格(亮色模式不加,保持干净) */
:root.dark .ds-root::before {
content:''; position:absolute; inset:0; pointer-events:none; z-index:0;
background-image:radial-gradient(circle, var(--border) 1px, transparent 1px);
background-size:24px 24px; opacity:0.3;
}
.ds-inner { position:relative; z-index:1; max-width:1400px; margin:0 auto; }
/* 标题 */
.ds-header { margin-bottom:44px; }
.ds-header h1 { font-family:var(--font-display); font-size:var(--text-2xl); font-weight:700; color:var(--fg); }
.ds-header h1 span { color:var(--muted); font-weight:400; }
.ds-header p { font-size:var(--text-sm); color:var(--muted); margin-top:4px; }
/* 统计条 — 无缝分段,使用设计系统字体和间距 */
.ds-stats { display:flex; gap:1px; background:var(--border); border-radius:var(--radius-xl); overflow:hidden; }
.ds-stat { flex:1; background:var(--surface); padding:var(--space-4) var(--space-6); display:flex; flex-direction:column; gap:var(--space-1); }
.ds-stat-label { font-size:var(--text-xs); font-weight:500; color:var(--muted); text-transform:uppercase; letter-spacing:var(--tracking-widest); }
.ds-stat-value { font-family:var(--font-display); font-size:var(--text-2xl); font-weight:600; line-height:var(--leading-tight); }
.ds-stat-sub { font-size:var(--text-xs); color:var(--muted-subtle); }
/* 卡片网格 */
.ds-grid { display:grid; grid-template-columns:repeat(4,1fr); gap:var(--space-3); }
@media (max-width:1280px) { .ds-grid { grid-template-columns:repeat(3,1fr); } }
@media (max-width:900px) { .ds-grid { grid-template-columns:repeat(2,1fr); } }
@media (max-width:560px) { .ds-grid { grid-template-columns:1fr; } }
/* 卡片 */
.ds-card { position:relative; background:var(--surface); border:1px solid var(--border); border-radius:var(--radius-xl); padding:var(--space-5); transition:all var(--transition-normal); cursor:pointer; }
.ds-card:hover { border-color:var(--accent); box-shadow:var(--shadow-md); transform:translateY(-1px); }
.ds-card.open { border-color:var(--accent); }
/* 异常卡片 — 唯一使用独立颜色的地方(精密仪器风格的核心) */
.ds-card.abnormal { border-color:var(--danger); background:var(--danger-subtle); box-shadow:0 0 0 1px var(--danger), 0 0 24px rgba(220,38,38,.15); }
/* 状态指示器 — 页面唯一的装饰性动画 */
.ds-dot { width:10px; height:10px; border-radius:50%; position:relative; flex-shrink:0; }
.ds-dot::after { content:''; position:absolute; inset:-4px; border-radius:50%; z-index:-1; }
.ds-dot.ok { background:var(--success); }
.ds-dot.ok::after { background:var(--success-subtle); }
.ds-dot.err { background:var(--danger); animation:ds-breathe 3s ease-in-out infinite; }
.ds-dot.err::after { background:var(--danger-subtle); animation:ds-breathe-glow 3s ease-in-out infinite; }
@keyframes ds-breathe { 0%,100%{box-shadow:0 0 4px var(--danger)}50%{box-shadow:0 0 14px var(--danger)} }
@keyframes ds-breathe-glow { 0%,100%{transform:scale(1);opacity:.5}50%{transform:scale(1.8);opacity:1} }
.ds-card-top { display:flex; align-items:center; justify-content:space-between; margin-bottom:var(--space-4); }
.ds-card-name { display:flex; align-items:center; gap:var(--space-2); font-weight:500; font-size:var(--text-sm); color:var(--fg); }
/* 状态数值 — 等宽数字Precision 核心 */
.ds-card-value { font-family:var(--font-mono); font-size:var(--text-2xl); font-weight:600; margin-bottom:var(--space-2); }
.ds-card-value.ok { color:var(--success); }
.ds-card-value.err { color:var(--danger); }
.ds-card-meta { font-size:var(--text-xs); color:var(--muted); line-height:1.5; margin-bottom:var(--space-3); }
.ds-card-footer { display:flex; align-items:center; justify-content:space-between; padding-top:var(--space-3); border-top:1px solid var(--border-subtle); }
.ds-card-footer span { font-size:var(--text-xs); color:var(--muted-subtle); }
.ds-card-expand { display:none; margin-top:var(--space-3); padding-top:var(--space-3); border-top:1px solid var(--border-subtle); }
.ds-card.open .ds-card-expand { display:block; }
.ds-check { display:flex; align-items:center; gap:var(--space-2); padding:3px 0; font-size:var(--text-xs); color:var(--fg-subtle); }
.ds-check-dot { width:6px; height:6px; border-radius:50%; flex-shrink:0; }
.ds-check-dot.ok { background:var(--success); }
.ds-check-dot.err { background:var(--danger); }
/* 底部状态 */
.ds-footer { margin-top:36px; display:flex; align-items:center; justify-content:center; gap:32px; font-size:var(--text-xs); color:var(--muted); }
.ds-footer-sep { width:1px; height:12px; background:var(--border); }
/* 页面切换 */
.page { display:none; }
.page.active { display:block; }
@media (max-width:768px) {
.sidebar { display:none; }
.main { margin-left:0; }
.stats-grid { grid-template-columns:1fr 1fr; }
}
</style>
</head>
<body>
<div class="app">
<!-- ==================== 侧边栏 ==================== -->
<aside class="sidebar">
<div class="sidebar-brand">
<div class="sidebar-logo">M</div>
<span class="sidebar-title">monitor-ai</span>
</div>
<nav class="sidebar-nav">
<div class="sidebar-section">
<p class="sidebar-section-title">监控</p>
<a class="sidebar-item active" href="#" onclick="showPage('dashboard');return false">
<span class="sidebar-item-icon"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="9"/><rect x="14" y="3" width="7" height="5"/><rect x="14" y="12" width="7" height="9"/><rect x="3" y="16" width="7" height="5"/></svg></span> 仪表盘(行式)
</a>
<a class="sidebar-item" href="#" onclick="showPage('dashboard-cards');return false">
<span class="sidebar-item-icon"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/><rect x="7" y="7" width="3" height="3"/><rect x="14" y="7" width="3" height="3"/><rect x="7" y="14" width="3" height="3"/><rect x="14" y="14" width="3" height="3"/></svg></span> 仪表盘(卡片式)
</a>
<a class="sidebar-item" href="#" onclick="showPage('dashboard-plan-a');return false">
<span class="sidebar-item-icon"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/></svg></span> 仪表盘方案A
</a>
<a class="sidebar-item" href="#" onclick="showPage('dashboard-pro');return false">
<span class="sidebar-item-icon"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg></span> 仪表盘Pro
</a>
<a class="sidebar-item" href="#" onclick="showPage('dashboard-final');return false">
<span class="sidebar-item-icon"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/></svg></span> 仪表盘(最终)⭐
</a>
<a class="sidebar-item" href="#" onclick="showPage('dashboard-design');return false">
<span class="sidebar-item-icon"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="3"/></svg></span> 仪表盘Design🆕
</a>
<a class="sidebar-item" href="#" onclick="showPage('services');return false">
<span class="sidebar-item-icon"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><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"/><circle cx="6" cy="18" r="1"/></svg></span> 服务管理
</a>
</div>
<div class="sidebar-section">
<p class="sidebar-section-title">告警</p>
<a class="sidebar-item" href="#" onclick="showPage('settings');return false">
<span class="sidebar-item-icon"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.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-4 0v-.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-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 0-4h.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 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.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 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 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg></span> 告警设置
</a>
<a class="sidebar-item" href="#" onclick="showPage('alerts');return false">
<span class="sidebar-item-icon"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/></svg></span> 告警历史
</a>
<a class="sidebar-item" href="#" onclick="showPage('history');return false">
<span class="sidebar-item-icon"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg></span> 状态历史
</a>
</div>
<div class="sidebar-section">
<p class="sidebar-section-title">管理</p>
<a class="sidebar-item" href="#" onclick="showPage('users');return false">
<span class="sidebar-item-icon"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg></span> 用户管理
</a>
</div>
</nav>
<div class="sidebar-user">
<div class="sidebar-user-inner">
<div class="sidebar-user-avatar">A</div>
<div>
<div class="sidebar-user-name">admin</div>
<div class="sidebar-user-role">管理员</div>
</div>
</div>
</div>
</aside>
<!-- ==================== 主内容区 ==================== -->
<main class="main">
<header class="header">
<div class="header-inner">
<div class="header-left">
<span class="header-breadcrumb">monitor-ai / <span id="breadcrumb-page">仪表盘</span></span>
</div>
<div class="header-right">
<span style="font-size:var(--text-sm);color:var(--muted)">
Worker: <span style="color:var(--success);font-weight:500">运行中</span> · 自动刷新: 30s
</span>
<button class="theme-toggle" onclick="toggleTheme()" title="切换主题">🌓</button>
</div>
</div>
</header>
<div class="content">
<!-- ==================== 页面 1: 仪表盘 ==================== -->
<div id="page-dashboard" class="page active">
<!-- 统计卡片 -->
<div class="stats-grid">
<div class="stat-card">
<div class="stat-card-header">
<span class="stat-card-title">正常服务</span>
<div class="stat-card-icon" style="background:var(--success-subtle);color:var(--success)"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg></div>
</div>
<div class="stat-card-value">10</div>
<div class="stat-card-sub">共 11 个监控服务</div>
</div>
<div class="stat-card">
<div class="stat-card-header">
<span class="stat-card-title">异常服务</span>
<div class="stat-card-icon" style="background:var(--danger-subtle);color:var(--danger)"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg></div>
</div>
<div class="stat-card-value" style="color:var(--danger)">1</div>
<div class="stat-card-sub">Authelia SSO 异常</div>
</div>
<div class="stat-card">
<div class="stat-card-header">
<span class="stat-card-title">未知状态</span>
<div class="stat-card-icon" style="background:var(--warning-subtle);color:var(--warning)"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg></div>
</div>
<div class="stat-card-value">0</div>
<div class="stat-card-sub">所有服务已检查</div>
</div>
<div class="stat-card">
<div class="stat-card-header">
<span class="stat-card-title">最后检查</span>
<div class="stat-card-icon" style="background:var(--accent-subtle);color:var(--accent)"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg></div>
</div>
<div class="stat-card-value" style="font-size:var(--text-xl)">3 秒前</div>
<div class="stat-card-sub">2026-07-01 14:35:00</div>
</div>
</div>
<!-- 服务状态列表 -->
<div class="card">
<div class="card-header">
<span class="card-title">服务状态</span>
<button class="btn btn-sm btn-secondary" onclick="showToast('已手动刷新所有服务状态')">立即刷新</button>
</div>
<div class="card-body" style="padding:0;">
<div class="service-list" style="padding:0;">
<!-- 异常服务(展开) -->
<div class="service-card open">
<div class="service-card-header" onclick="this.parentElement.classList.toggle('open')">
<div class="service-card-left">
<span class="status-dot status-dot-abnormal"></span>
<span style="font-weight:600;color:var(--fg)">Authelia SSO</span>
<span class="tag tag-red">critical</span>
</div>
<div class="service-card-right">
<span style="color:var(--danger);font-weight:500;">异常 5m 23s</span>
<span style="color:var(--muted-subtle)"></span>
</div>
</div>
<div class="service-card-body">
<div class="service-card-body-inner">
<div class="check-detail">
<span class="status-dot status-dot-abnormal"></span>
<span style="flex:1">HTTP GET http://authelia:9091/api/health</span>
<span class="tag tag-red">连接超时 (10s)</span>
</div>
<div class="check-detail">
<span class="status-dot status-dot-normal"></span>
<span style="flex:1">Docker check: authelia</span>
<span class="tag tag-green">running</span>
</div>
</div>
</div>
</div>
<!-- 正常服务 -->
<div class="service-card">
<div class="service-card-header" onclick="this.parentElement.classList.toggle('open')">
<div class="service-card-left">
<span class="status-dot status-dot-normal"></span>
<span style="font-weight:500;color:var(--fg)">Redis</span>
<span class="tag tag-red">critical</span>
</div>
<div class="service-card-right">
<span style="color:var(--muted)">正常</span>
<span style="color:var(--muted-subtle)"></span>
</div>
</div>
<div class="service-card-body">
<div class="service-card-body-inner">
<div class="check-detail">
<span class="status-dot status-dot-normal"></span>
<span>Docker check: redis</span>
<span class="tag tag-green">running</span>
</div>
</div>
</div>
</div>
<div class="service-card">
<div class="service-card-header" onclick="this.parentElement.classList.toggle('open')">
<div class="service-card-left">
<span class="status-dot status-dot-normal"></span>
<span style="font-weight:500;color:var(--fg)">LLDAP</span>
<span class="tag tag-red">critical</span>
</div>
<div class="service-card-right"><span style="color:var(--muted)">正常</span><span style="color:var(--muted-subtle)"></span></div>
</div>
<div class="service-card-body">
<div class="service-card-body-inner">
<div class="check-detail"><span class="status-dot status-dot-normal"></span><span>HTTP GET http://ldap-ai:17170</span><span class="tag tag-green">200 OK</span></div>
<div class="check-detail"><span class="status-dot status-dot-normal"></span><span>Docker check: ldap-ai</span><span class="tag tag-green">running</span></div>
</div>
</div>
</div>
<div class="service-card"><div class="service-card-header" onclick="this.parentElement.classList.toggle('open')"><div class="service-card-left"><span class="status-dot status-dot-normal"></span><span style="font-weight:500;color:var(--fg)">OA 门户</span><span class="tag tag-green">warning</span></div><div class="service-card-right"><span style="color:var(--muted)">正常</span></div></div></div>
<div class="service-card"><div class="service-card-header" onclick="this.parentElement.classList.toggle('open')"><div class="service-card-left"><span class="status-dot status-dot-normal"></span><span style="font-weight:500;color:var(--fg)">资产管理</span><span class="tag tag-green">warning</span></div><div class="service-card-right"><span style="color:var(--muted)">正常</span></div></div></div>
<div class="service-card"><div class="service-card-header" onclick="this.parentElement.classList.toggle('open')"><div class="service-card-left"><span class="status-dot status-dot-normal"></span><span style="font-weight:500;color:var(--fg)">工单系统</span><span class="tag tag-green">warning</span></div><div class="service-card-right"><span style="color:var(--muted)">正常</span></div></div></div>
<div class="service-card"><div class="service-card-header" onclick="this.parentElement.classList.toggle('open')"><div class="service-card-left"><span class="status-dot status-dot-normal"></span><span style="font-weight:500;color:var(--fg)">Gitea 代码托管</span><span class="tag tag-green">warning</span></div><div class="service-card-right"><span style="color:var(--muted)">正常</span></div></div></div>
<div class="service-card"><div class="service-card-header" onclick="this.parentElement.classList.toggle('open')"><div class="service-card-left"><span class="status-dot status-dot-normal"></span><span style="font-weight:500;color:var(--fg)">Nginx 代理</span><span class="tag tag-red">critical</span></div><div class="service-card-right"><span style="color:var(--muted)">正常</span></div></div></div>
<div class="service-card"><div class="service-card-header" onclick="this.parentElement.classList.toggle('open')"><div class="service-card-left"><span class="status-dot status-dot-normal"></span><span style="font-weight:500;color:var(--fg)">官网 (www-ai)</span><span class="tag tag-green">warning</span></div><div class="service-card-right"><span style="color:var(--muted)">正常</span></div></div></div>
<div class="service-card"><div class="service-card-header" onclick="this.parentElement.classList.toggle('open')"><div class="service-card-left"><span class="status-dot status-dot-normal"></span><span style="font-weight:500;color:var(--fg)">云平台 (cloud-ai)</span><span class="tag tag-green">warning</span></div><div class="service-card-right"><span style="color:var(--muted)">正常</span></div></div></div>
<div class="service-card"><div class="service-card-header" onclick="this.parentElement.classList.toggle('open')"><div class="service-card-left"><span class="status-dot status-dot-normal"></span><span style="font-weight:500;color:var(--fg)">Token 工厂 (token-ai)</span><span class="tag tag-green">warning</span></div><div class="service-card-right"><span style="color:var(--muted)">正常</span></div></div></div>
</div>
</div>
</div>
</div>
<!-- ==================== 页面 1b: 卡片式仪表盘 ==================== -->
<div id="page-dashboard-cards" class="page">
<!-- 统计卡片(同上) -->
<div class="stats-grid">
<div class="stat-card">
<div class="stat-card-header">
<span class="stat-card-title">正常服务</span>
<div class="stat-card-icon" style="background:var(--success-subtle);color:var(--success)"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg></div>
</div>
<div class="stat-card-value">10</div>
<div class="stat-card-sub">共 11 个监控服务</div>
</div>
<div class="stat-card">
<div class="stat-card-header">
<span class="stat-card-title">异常服务</span>
<div class="stat-card-icon" style="background:var(--danger-subtle);color:var(--danger)"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg></div>
</div>
<div class="stat-card-value" style="color:var(--danger)">1</div>
<div class="stat-card-sub">Authelia SSO 异常</div>
</div>
<div class="stat-card">
<div class="stat-card-header">
<span class="stat-card-title">未知状态</span>
<div class="stat-card-icon" style="background:var(--warning-subtle);color:var(--warning)"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg></div>
</div>
<div class="stat-card-value">0</div>
<div class="stat-card-sub">所有服务已检查</div>
</div>
<div class="stat-card">
<div class="stat-card-header">
<span class="stat-card-title">最后检查</span>
<div class="stat-card-icon" style="background:var(--accent-subtle);color:var(--accent)"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg></div>
</div>
<div class="stat-card-value" style="font-size:var(--text-xl)">3 秒前</div>
<div class="stat-card-sub">2026-07-01 14:35:00</div>
</div>
</div>
<!-- 卡片式服务网格 -->
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:var(--space-4)">
<span style="font-size:var(--text-lg);font-weight:600;color:var(--fg)">服务状态</span>
<span style="font-size:var(--text-sm);color:var(--muted)">点击卡片展开检查详情</span>
</div>
<div class="card-grid">
<!-- 异常服务 -->
<div class="svc-card abnormal open" onclick="this.classList.toggle('open')">
<div class="svc-card-header">
<span class="svc-card-name"><span class="status-dot status-dot-abnormal"></span> Authelia SSO</span>
<span class="tag tag-red">critical</span>
</div>
<div class="svc-card-countdown">5m 23s</div>
<div class="svc-card-label">异常持续时长</div>
<div class="svc-card-error">HTTP GET http://authelia:9091/api/health → 连接超时 (10s)</div>
<div class="svc-card-body-expand">
<div class="check-detail"><span class="status-dot status-dot-abnormal"></span><span>HTTP 检查</span><span class="tag tag-red">连接超时</span></div>
<div class="check-detail"><span class="status-dot status-dot-normal"></span><span>Docker 检查</span><span class="tag tag-green">running</span></div>
</div>
</div>
<!-- 正常服务 × 10 -->
<div class="svc-card" onclick="this.classList.toggle('open')">
<div class="svc-card-header">
<span class="svc-card-name"><span class="status-dot status-dot-normal"></span> Redis</span>
<span class="tag tag-red">critical</span>
</div>
<div class="svc-card-countdown" style="color:var(--success);">正常</div>
<div class="svc-card-label">运行中</div>
<div class="svc-card-body-expand"><div class="check-detail"><span class="status-dot status-dot-normal"></span><span>Docker check</span><span class="tag tag-green">running</span></div></div>
</div>
<div class="svc-card" onclick="this.classList.toggle('open')">
<div class="svc-card-header">
<span class="svc-card-name"><span class="status-dot status-dot-normal"></span> LLDAP</span>
<span class="tag tag-red">critical</span>
</div>
<div class="svc-card-countdown" style="color:var(--success);">正常</div>
<div class="svc-card-label">HTTP + Docker 均正常</div>
<div class="svc-card-body-expand"><div class="check-detail"><span class="status-dot status-dot-normal"></span><span>HTTP GET http://ldap-ai:17170</span><span class="tag tag-green">200 OK</span></div><div class="check-detail"><span class="status-dot status-dot-normal"></span><span>Docker check</span><span class="tag tag-green">running</span></div></div>
</div>
<div class="svc-card" onclick="this.classList.toggle('open')"><div class="svc-card-header"><span class="svc-card-name"><span class="status-dot status-dot-normal"></span> OA 门户</span><span class="tag tag-green">warning</span></div><div class="svc-card-countdown" style="color:var(--success);">正常</div><div class="svc-card-label">运行中</div></div>
<div class="svc-card" onclick="this.classList.toggle('open')"><div class="svc-card-header"><span class="svc-card-name"><span class="status-dot status-dot-normal"></span> 资产管理</span><span class="tag tag-green">warning</span></div><div class="svc-card-countdown" style="color:var(--success);">正常</div><div class="svc-card-label">运行中</div></div>
<div class="svc-card" onclick="this.classList.toggle('open')"><div class="svc-card-header"><span class="svc-card-name"><span class="status-dot status-dot-normal"></span> 工单系统</span><span class="tag tag-green">warning</span></div><div class="svc-card-countdown" style="color:var(--success);">正常</div><div class="svc-card-label">运行中</div></div>
<div class="svc-card" onclick="this.classList.toggle('open')"><div class="svc-card-header"><span class="svc-card-name"><span class="status-dot status-dot-normal"></span> Gitea 代码托管</span><span class="tag tag-green">warning</span></div><div class="svc-card-countdown" style="color:var(--success);">正常</div><div class="svc-card-label">运行中</div></div>
<div class="svc-card" onclick="this.classList.toggle('open')"><div class="svc-card-header"><span class="svc-card-name"><span class="status-dot status-dot-normal"></span> Nginx 代理</span><span class="tag tag-red">critical</span></div><div class="svc-card-countdown" style="color:var(--success);">正常</div><div class="svc-card-label">运行中</div></div>
<div class="svc-card" onclick="this.classList.toggle('open')"><div class="svc-card-header"><span class="svc-card-name"><span class="status-dot status-dot-normal"></span> 官网 (www-ai)</span><span class="tag tag-green">warning</span></div><div class="svc-card-countdown" style="color:var(--success);">正常</div><div class="svc-card-label">运行中</div></div>
<div class="svc-card" onclick="this.classList.toggle('open')"><div class="svc-card-header"><span class="svc-card-name"><span class="status-dot status-dot-normal"></span> 云平台 (cloud-ai)</span><span class="tag tag-green">warning</span></div><div class="svc-card-countdown" style="color:var(--success);">正常</div><div class="svc-card-label">运行中</div></div>
<div class="svc-card" onclick="this.classList.toggle('open')"><div class="svc-card-header"><span class="svc-card-name"><span class="status-dot status-dot-normal"></span> Token 工厂 (token-ai)</span><span class="tag tag-green">warning</span></div><div class="svc-card-countdown" style="color:var(--success);">正常</div><div class="svc-card-label">运行中</div></div>
</div>
</div>
<!-- ==================== 页面 1f: 最终Design统计条 + 4大卡片 + 4列服务网格 ==================== -->
<div id="page-dashboard-final" class="page">
<!-- 标题行(最顶部) -->
<div class="ds-header" style="margin-bottom:32px;">
<h1>服务状态监控 <span>· 11 个服务</span></h1>
<p>实时健康检查 · 本月可用率 99.8% · 30 秒轮询</p>
</div>
<!-- 状态统计条 -->
<div class="ds-stats" style="margin-bottom:var(--space-5);">
<div class="ds-stat">
<span class="ds-stat-label">正常</span>
<span class="ds-stat-value" style="color:var(--success)">10</span>
<span class="ds-stat-sub">共 11 个监控服务</span>
</div>
<div class="ds-stat">
<span class="ds-stat-label">异常</span>
<span class="ds-stat-value" style="color:var(--danger)">1</span>
<span class="ds-stat-sub">Authelia SSO</span>
</div>
<div class="ds-stat">
<span class="ds-stat-label">未知</span>
<span class="ds-stat-value" style="color:var(--muted)">0</span>
</div>
<div class="ds-stat" style="text-align:right">
<span class="ds-stat-label">最后检查</span>
<span class="ds-stat-value" style="font-size:var(--text-base);font-weight:500">3 秒前</span>
<span class="ds-stat-sub">Worker 运行中 · 30s 轮询</span>
</div>
</div>
<!-- KPI 统计条 -->
<div class="ds-stats" style="margin-bottom:32px;">
<div class="ds-stat">
<span class="ds-stat-label">本月可用率</span>
<span class="ds-stat-value" style="color:var(--success)">99.8%</span>
<span class="ds-stat-sub">本月故障 3 次</span>
</div>
<div class="ds-stat">
<span class="ds-stat-label">平均恢复时间</span>
<span class="ds-stat-value" style="color:var(--fg)">2m 30s</span>
<span class="ds-stat-sub">较上月缩短 15s</span>
</div>
<div class="ds-stat">
<span class="ds-stat-label">本月告警</span>
<span class="ds-stat-value" style="color:var(--fg)">12</span>
<span class="ds-stat-sub">推送成功率 95.8%</span>
</div>
<div class="ds-stat">
<span class="ds-stat-label">活跃渠道</span>
<span class="ds-stat-value" style="color:var(--fg)">2</span>
<span class="ds-stat-sub">运维群 · 开发群</span>
</div>
</div>
<hr style="border:none;border-top:1px solid var(--border);margin:0 0 32px 0;">
<div class="card-grid" style="grid-template-columns:repeat(4,1fr);gap:var(--space-5);">
<div class="svc-card abnormal open" onclick="this.classList.toggle('open')"><div class="svc-card-header"><span class="svc-card-name"><span class="status-dot status-dot-abnormal"></span> Authelia SSO</span><span class="tag tag-red">critical</span></div><div class="svc-card-countdown">5m 23s</div><div class="svc-card-label">异常持续时长</div><div class="svc-card-error">HTTP 连接超时 (10s)</div><div class="svc-card-body-expand"><div class="check-detail"><span class="status-dot status-dot-abnormal"></span><span>HTTP 检查</span><span class="tag tag-red">超时</span></div><div class="check-detail"><span class="status-dot status-dot-normal"></span><span>Docker 检查</span><span class="tag tag-green">running</span></div></div></div>
<div class="svc-card"><div class="svc-card-header"><span class="svc-card-name"><span class="status-dot status-dot-normal"></span> Redis</span><span class="tag tag-red">critical</span></div><div class="svc-card-label">运行中</div></div>
<div class="svc-card"><div class="svc-card-header"><span class="svc-card-name"><span class="status-dot status-dot-normal"></span> LLDAP</span><span class="tag tag-red">critical</span></div><div class="svc-card-label">运行中</div></div>
<div class="svc-card"><div class="svc-card-header"><span class="svc-card-name"><span class="status-dot status-dot-normal"></span> OA 门户</span><span class="tag tag-green">warning</span></div><div class="svc-card-label">运行中</div></div>
<div class="svc-card"><div class="svc-card-header"><span class="svc-card-name"><span class="status-dot status-dot-normal"></span> 资产管理</span><span class="tag tag-green">warning</span></div><div class="svc-card-label">运行中</div></div>
<div class="svc-card"><div class="svc-card-header"><span class="svc-card-name"><span class="status-dot status-dot-normal"></span> 工单系统</span><span class="tag tag-green">warning</span></div><div class="svc-card-label">运行中</div></div>
<div class="svc-card"><div class="svc-card-header"><span class="svc-card-name"><span class="status-dot status-dot-normal"></span> Gitea</span><span class="tag tag-green">warning</span></div><div class="svc-card-label">运行中</div></div>
<div class="svc-card"><div class="svc-card-header"><span class="svc-card-name"><span class="status-dot status-dot-normal"></span> Nginx</span><span class="tag tag-red">critical</span></div><div class="svc-card-label">运行中</div></div>
<div class="svc-card"><div class="svc-card-header"><span class="svc-card-name"><span class="status-dot status-dot-normal"></span> www-ai</span><span class="tag tag-green">warning</span></div><div class="svc-card-label">运行中</div></div>
<div class="svc-card"><div class="svc-card-header"><span class="svc-card-name"><span class="status-dot status-dot-normal"></span> cloud-ai</span><span class="tag tag-green">warning</span></div><div class="svc-card-label">运行中</div></div>
<div class="svc-card"><div class="svc-card-header"><span class="svc-card-name"><span class="status-dot status-dot-normal"></span> token-ai</span><span class="tag tag-green">warning</span></div><div class="svc-card-label">运行中</div></div>
</div>
</div>
<!-- ==================== 页面 1c: 方案 A统计条 + 统一卡片网格) ==================== -->
<div id="page-dashboard-plan-a" class="page">
<!-- 统计条(无卡片边框,轻量文字) -->
<div class="stat-bar">
<div class="stat-bar-item">
<span class="stat-bar-dot stat-bar-dot-success"></span>
<div><div class="stat-bar-value">10</div><div class="stat-bar-label">正常</div></div>
</div>
<div class="stat-bar-divider"></div>
<div class="stat-bar-item">
<span class="stat-bar-dot stat-bar-dot-danger"></span>
<div><div class="stat-bar-value" style="color:var(--danger)">1</div><div class="stat-bar-label">异常</div></div>
</div>
<div class="stat-bar-divider"></div>
<div class="stat-bar-item">
<span class="stat-bar-dot stat-bar-dot-warning"></span>
<div><div class="stat-bar-value">0</div><div class="stat-bar-label">未知</div></div>
</div>
<div class="stat-bar-right">
<span style="color:var(--muted)">最后检查 3 秒前 · Worker 运行中 · 自动刷新 30s</span>
</div>
</div>
<!-- 统一卡片网格(所有卡片相同尺寸) -->
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:var(--space-4)">
<span style="font-size:var(--text-base);font-weight:600;color:var(--fg)">服务状态</span>
<span style="font-size:var(--text-sm);color:var(--muted)">11 个服务 · 点击卡片展开详情</span>
</div>
<div class="card-grid">
<div class="svc-card abnormal open" onclick="this.classList.toggle('open')">
<div class="svc-card-header">
<span class="svc-card-name"><span class="status-dot status-dot-abnormal"></span> Authelia SSO</span>
<span class="tag tag-red">critical</span>
</div>
<div class="svc-card-countdown">5m 23s</div>
<div class="svc-card-label">异常持续时长</div>
<div class="svc-card-error">HTTP GET authelia:9091/api/health → 连接超时 (10s)</div>
<div class="svc-card-body-expand">
<div class="check-detail"><span class="status-dot status-dot-abnormal"></span><span>HTTP 检查</span><span class="tag tag-red">连接超时</span></div>
<div class="check-detail"><span class="status-dot status-dot-normal"></span><span>Docker 检查</span><span class="tag tag-green">running</span></div>
</div>
<div class="svc-card-footer"><span class="svc-card-label">展开详情 ▴</span></div>
</div>
<div class="svc-card" onclick="this.classList.toggle('open')"><div class="svc-card-header"><span class="svc-card-name"><span class="status-dot status-dot-normal"></span> Redis</span><span class="tag tag-red">critical</span></div><div class="svc-card-countdown" style="color:var(--success);">正常</div><div class="svc-card-label">Docker: running</div></div>
<div class="svc-card" onclick="this.classList.toggle('open')"><div class="svc-card-header"><span class="svc-card-name"><span class="status-dot status-dot-normal"></span> LLDAP</span><span class="tag tag-red">critical</span></div><div class="svc-card-countdown" style="color:var(--success);">正常</div><div class="svc-card-label">HTTP + Docker 均正常</div></div>
<div class="svc-card" onclick="this.classList.toggle('open')"><div class="svc-card-header"><span class="svc-card-name"><span class="status-dot status-dot-normal"></span> OA 门户</span><span class="tag tag-green">warning</span></div><div class="svc-card-countdown" style="color:var(--success);">正常</div><div class="svc-card-label">HTTP: 200 OK</div></div>
<div class="svc-card" onclick="this.classList.toggle('open')"><div class="svc-card-header"><span class="svc-card-name"><span class="status-dot status-dot-normal"></span> 资产管理</span><span class="tag tag-green">warning</span></div><div class="svc-card-countdown" style="color:var(--success);">正常</div><div class="svc-card-label">HTTP: 200 OK</div></div>
<div class="svc-card" onclick="this.classList.toggle('open')"><div class="svc-card-header"><span class="svc-card-name"><span class="status-dot status-dot-normal"></span> 工单系统</span><span class="tag tag-green">warning</span></div><div class="svc-card-countdown" style="color:var(--success);">正常</div><div class="svc-card-label">HTTP: 200 OK</div></div>
<div class="svc-card" onclick="this.classList.toggle('open')"><div class="svc-card-header"><span class="svc-card-name"><span class="status-dot status-dot-normal"></span> Gitea 代码托管</span><span class="tag tag-green">warning</span></div><div class="svc-card-countdown" style="color:var(--success);">正常</div><div class="svc-card-label">HTTP + Docker 均正常</div></div>
<div class="svc-card" onclick="this.classList.toggle('open')"><div class="svc-card-header"><span class="svc-card-name"><span class="status-dot status-dot-normal"></span> Nginx 代理</span><span class="tag tag-red">critical</span></div><div class="svc-card-countdown" style="color:var(--success);">正常</div><div class="svc-card-label">Docker: running</div></div>
<div class="svc-card" onclick="this.classList.toggle('open')"><div class="svc-card-header"><span class="svc-card-name"><span class="status-dot status-dot-normal"></span> 官网 (www-ai)</span><span class="tag tag-green">warning</span></div><div class="svc-card-countdown" style="color:var(--success);">正常</div><div class="svc-card-label">HTTP: 200 OK</div></div>
<div class="svc-card" onclick="this.classList.toggle('open')"><div class="svc-card-header"><span class="svc-card-name"><span class="status-dot status-dot-normal"></span> 云平台 (cloud-ai)</span><span class="tag tag-green">warning</span></div><div class="svc-card-countdown" style="color:var(--success);">正常</div><div class="svc-card-label">HTTP: 200 OK</div></div>
<div class="svc-card" onclick="this.classList.toggle('open')"><div class="svc-card-header"><span class="svc-card-name"><span class="status-dot status-dot-normal"></span> Token 工厂 (token-ai)</span><span class="tag tag-green">warning</span></div><div class="svc-card-countdown" style="color:var(--success);">正常</div><div class="svc-card-label">HTTP: 200 OK</div></div>
</div>
</div>
<!-- ==================== 页面 1d: Pro 仪表盘Dark-First 专业监控风格) ==================== -->
<div id="page-dashboard-pro" class="page">
<div class="content-area">
<!-- 标题 -->
<div class="pro-header">
<div>
<h1 class="pro-title">服务状态监控</h1>
<p class="pro-title-sub">实时健康检查 · 11 个服务 · 30 秒轮询</p>
</div>
<div class="pro-actions">
<span style="font-size:var(--text-sm);color:var(--muted);display:flex;align-items:center;gap:var(--space-2)">
<span style="width:8px;height:8px;border-radius:50%;background:var(--success);display:inline-block;"></span> Worker 运行中
</span>
<button class="btn btn-sm btn-secondary" onclick="showToast('已手动刷新')">立即刷新</button>
</div>
</div>
<!-- 统计条 -->
<div class="pro-statbar">
<div class="pro-stat-item">
<span class="pro-stat-item-label">正常</span>
<span class="pro-stat-item-value" style="color:var(--success)">10</span>
<span class="pro-stat-item-sub">90.9% 可用率</span>
</div>
<div class="pro-statbar-divider"></div>
<div class="pro-stat-item">
<span class="pro-stat-item-label">异常</span>
<span class="pro-stat-item-value" style="color:var(--danger)">1</span>
<span class="pro-stat-item-sub">Authelia SSO</span>
</div>
<div class="pro-statbar-divider"></div>
<div class="pro-stat-item">
<span class="pro-stat-item-label">未知</span>
<span class="pro-stat-item-value" style="color:var(--muted)">0</span>
</div>
<div class="pro-stat-item" style="text-align:right">
<span class="pro-stat-item-label">最后检查</span>
<span class="pro-stat-item-value" style="font-size:var(--text-lg);font-weight:500">3 秒前</span>
<span class="pro-stat-item-sub">2026-07-01 14:35:00</span>
</div>
</div>
<!-- 服务卡片网格 -->
<div class="pro-grid">
<!-- 异常 -->
<div class="pro-svc abnormal open" onclick="this.classList.toggle('open')">
<div class="pro-svc-top">
<span class="pro-svc-name"><span class="pro-svc-status-dot err"></span> Authelia SSO</span>
<span class="tag tag-red">critical</span>
</div>
<div class="pro-svc-body">
<span class="pro-svc-status-text err">5m 23s</span>
<span class="pro-svc-desc">HTTP 检查失败:连接超时 (10s)<br>Docker 检查正常</span>
</div>
<div class="pro-svc-footer">
<span class="pro-svc-footer-label">展开详情 ▴</span>
</div>
<div class="pro-svc-expand">
<div class="pro-svc-expand-item"><span class="pro-svc-status-dot err"></span> HTTP GET authelia:9091/api/health — 连接超时</div>
<div class="pro-svc-expand-item"><span class="pro-svc-status-dot ok" style="box-shadow:none"></span> Docker check — running</div>
</div>
</div>
<!-- 正常 × 10 -->
<div class="pro-svc" onclick="this.classList.toggle('open')"><div class="pro-svc-top"><span class="pro-svc-name"><span class="pro-svc-status-dot ok"></span> Redis</span><span class="tag tag-red">critical</span></div><div class="pro-svc-body"><span class="pro-svc-status-text ok">正常</span><span class="pro-svc-desc">Docker · running</span></div><div class="pro-svc-footer"><span class="pro-svc-footer-label">详情 ▸</span></div><div class="pro-svc-expand"><div class="pro-svc-expand-item"><span class="pro-svc-status-dot ok" style="box-shadow:none"></span> Docker check — running</div></div></div>
<div class="pro-svc" onclick="this.classList.toggle('open')"><div class="pro-svc-top"><span class="pro-svc-name"><span class="pro-svc-status-dot ok"></span> LLDAP</span><span class="tag tag-red">critical</span></div><div class="pro-svc-body"><span class="pro-svc-status-text ok">正常</span><span class="pro-svc-desc">HTTP · 200 OK<br>Docker · running</span></div><div class="pro-svc-footer"><span class="pro-svc-footer-label">详情 ▸</span></div></div>
<div class="pro-svc" onclick="this.classList.toggle('open')"><div class="pro-svc-top"><span class="pro-svc-name"><span class="pro-svc-status-dot ok"></span> OA 门户</span><span class="tag tag-green">warning</span></div><div class="pro-svc-body"><span class="pro-svc-status-text ok">正常</span><span class="pro-svc-desc">HTTP · 200 OK</span></div></div>
<div class="pro-svc" onclick="this.classList.toggle('open')"><div class="pro-svc-top"><span class="pro-svc-name"><span class="pro-svc-status-dot ok"></span> 资产管理</span><span class="tag tag-green">warning</span></div><div class="pro-svc-body"><span class="pro-svc-status-text ok">正常</span><span class="pro-svc-desc">HTTP · 200 OK</span></div></div>
<div class="pro-svc" onclick="this.classList.toggle('open')"><div class="pro-svc-top"><span class="pro-svc-name"><span class="pro-svc-status-dot ok"></span> 工单系统</span><span class="tag tag-green">warning</span></div><div class="pro-svc-body"><span class="pro-svc-status-text ok">正常</span><span class="pro-svc-desc">HTTP · 200 OK</span></div></div>
<div class="pro-svc" onclick="this.classList.toggle('open')"><div class="pro-svc-top"><span class="pro-svc-name"><span class="pro-svc-status-dot ok"></span> Gitea</span><span class="tag tag-green">warning</span></div><div class="pro-svc-body"><span class="pro-svc-status-text ok">正常</span><span class="pro-svc-desc">HTTP · 200 OK<br>Docker · running</span></div></div>
<div class="pro-svc" onclick="this.classList.toggle('open')"><div class="pro-svc-top"><span class="pro-svc-name"><span class="pro-svc-status-dot ok"></span> Nginx</span><span class="tag tag-red">critical</span></div><div class="pro-svc-body"><span class="pro-svc-status-text ok">正常</span><span class="pro-svc-desc">Docker · running</span></div></div>
<div class="pro-svc" onclick="this.classList.toggle('open')"><div class="pro-svc-top"><span class="pro-svc-name"><span class="pro-svc-status-dot ok"></span> www-ai</span><span class="tag tag-green">warning</span></div><div class="pro-svc-body"><span class="pro-svc-status-text ok">正常</span><span class="pro-svc-desc">HTTP · 200 OK</span></div></div>
<div class="pro-svc" onclick="this.classList.toggle('open')"><div class="pro-svc-top"><span class="pro-svc-name"><span class="pro-svc-status-dot ok"></span> cloud-ai</span><span class="tag tag-green">warning</span></div><div class="pro-svc-body"><span class="pro-svc-status-text ok">正常</span><span class="pro-svc-desc">HTTP · 200 OK</span></div></div>
<div class="pro-svc" onclick="this.classList.toggle('open')"><div class="pro-svc-top"><span class="pro-svc-name"><span class="pro-svc-status-dot ok"></span> token-ai</span><span class="tag tag-green">warning</span></div><div class="pro-svc-body"><span class="pro-svc-status-text ok">正常</span><span class="pro-svc-desc">HTTP · 200 OK</span></div></div>
</div>
</div>
</div>
<!-- ==================== 页面 1e: Design 仪表盘(精密仪器风格) ==================== -->
<div id="page-dashboard-design" class="page" style="padding:0;">
<div class="ds-root">
<div class="ds-inner">
<!-- 标题 -->
<div class="ds-header">
<h1>Infrastructure Status <span>· 11 services</span></h1>
<p>Real-time health monitoring · 30s polling · 99.8% uptime this month</p>
</div>
<!-- 统计 -->
<div class="ds-stats">
<div class="ds-stat">
<span class="ds-stat-label">Healthy</span>
<span class="ds-stat-value" style="color:var(--ds-success)">10</span>
<span class="ds-stat-sub">90.9% available</span>
</div>
<div class="ds-stat">
<span class="ds-stat-label">Degraded</span>
<span class="ds-stat-value" style="color:var(--ds-danger)">1</span>
<span class="ds-stat-sub">Authelia SSO</span>
</div>
<div class="ds-stat">
<span class="ds-stat-label">Unknown</span>
<span class="ds-stat-value" style="color:var(--ds-muted)">0</span>
</div>
</div>
<!-- 卡片网格 -->
<div class="ds-grid">
<!-- 异常 -->
<div class="ds-card abnormal open" onclick="this.classList.toggle('open')">
<div class="ds-card-top">
<span class="ds-card-name"><span class="ds-dot err"></span> Authelia SSO</span>
<span class="tag tag-red" style="opacity:.9">critical</span>
</div>
<div class="ds-card-value err">5m 23s</div>
<div class="ds-card-meta">HTTP connection timeout after 10s.<br>Docker container running normally.</div>
<div class="ds-card-footer"><span>Details ▴</span></div>
<div class="ds-card-expand">
<div class="ds-check"><span class="ds-check-dot err"></span> http://authelia:9091/api/health — timeout</div>
<div class="ds-check"><span class="ds-check-dot ok"></span> docker ps authelia — running</div>
</div>
</div>
<!-- 正常 -->
<div class="ds-card" onclick="this.classList.toggle('open')"><div class="ds-card-top"><span class="ds-card-name"><span class="ds-dot ok"></span> Redis</span><span class="tag tag-red" style="opacity:.8">critical</span></div><div class="ds-card-value ok">Healthy</div><div class="ds-card-meta">Docker container running</div><div class="ds-card-footer"><span>Details ▸</span></div><div class="ds-card-expand"><div class="ds-check"><span class="ds-check-dot ok"></span> docker ps redis — Up 5 days</div></div></div>
<div class="ds-card" onclick="this.classList.toggle('open')"><div class="ds-card-top"><span class="ds-card-name"><span class="ds-dot ok"></span> LLDAP</span><span class="tag tag-red" style="opacity:.8">critical</span></div><div class="ds-card-value ok">Healthy</div><div class="ds-card-meta">HTTP 200 · Docker running</div></div>
<div class="ds-card"><div class="ds-card-top"><span class="ds-card-name"><span class="ds-dot ok"></span> OA</span><span class="tag tag-green" style="opacity:.8">warning</span></div><div class="ds-card-value ok">Healthy</div><div class="ds-card-meta">HTTP 200 OK</div></div>
<div class="ds-card"><div class="ds-card-top"><span class="ds-card-name"><span class="ds-dot ok"></span> Assets</span><span class="tag tag-green" style="opacity:.8">warning</span></div><div class="ds-card-value ok">Healthy</div><div class="ds-card-meta">HTTP 200 OK</div></div>
<div class="ds-card"><div class="ds-card-top"><span class="ds-card-name"><span class="ds-dot ok"></span> Issue</span><span class="tag tag-green" style="opacity:.8">warning</span></div><div class="ds-card-value ok">Healthy</div><div class="ds-card-meta">HTTP 200 OK</div></div>
<div class="ds-card"><div class="ds-card-top"><span class="ds-card-name"><span class="ds-dot ok"></span> Gitea</span><span class="tag tag-green" style="opacity:.8">warning</span></div><div class="ds-card-value ok">Healthy</div><div class="ds-card-meta">HTTP 200 · Docker running</div></div>
<div class="ds-card"><div class="ds-card-top"><span class="ds-card-name"><span class="ds-dot ok"></span> Nginx</span><span class="tag tag-red" style="opacity:.8">critical</span></div><div class="ds-card-value ok">Healthy</div><div class="ds-card-meta">Docker running</div></div>
<div class="ds-card"><div class="ds-card-top"><span class="ds-card-name"><span class="ds-dot ok"></span> www-ai</span><span class="tag tag-green" style="opacity:.8">warning</span></div><div class="ds-card-value ok">Healthy</div></div>
<div class="ds-card"><div class="ds-card-top"><span class="ds-card-name"><span class="ds-dot ok"></span> cloud-ai</span><span class="tag tag-green" style="opacity:.8">warning</span></div><div class="ds-card-value ok">Healthy</div></div>
<div class="ds-card"><div class="ds-card-top"><span class="ds-card-name"><span class="ds-dot ok"></span> token-ai</span><span class="tag tag-green" style="opacity:.8">warning</span></div><div class="ds-card-value ok">Healthy</div></div>
</div>
<div class="ds-footer">
<span>Last check 3s ago</span><span class="ds-footer-sep"></span>
<span>Worker running</span><span class="ds-footer-sep"></span>
<span>Auto-refresh 30s</span>
</div>
</div>
</div>
</div>
<!-- ==================== 页面 2: 告警设置 ==================== -->
<div id="page-settings" class="page">
<h2 class="section-title">告警渠道设置</h2>
<div class="channel-health">
<span style="font-size:var(--text-sm);color:var(--muted);">推送通道:</span>
<div class="health-item"><span class="health-dot health-ok"></span> 运维群 <span class="tag tag-green">正常</span></div>
<div class="health-item"><span class="health-dot health-ok"></span> 开发群 <span class="tag tag-green">正常</span></div>
<div class="health-item"><span class="health-dot health-fail"></span> 备用群 <span class="tag tag-red">连续 3 次失败</span></div>
</div>
<div class="card" style="margin-bottom:var(--space-6);">
<div class="card-header">
<span class="card-title">渠道 1: 运维群</span>
<div style="display:flex;gap:var(--space-2);">
<button class="btn btn-sm btn-primary" onclick="showToast('测试消息已发送到运维群')">测试推送</button>
<button class="btn btn-sm btn-secondary">删除</button>
</div>
</div>
<div class="card-body">
<div class="form-group">
<label class="form-label">Webhook URL</label>
<input class="form-input" value="https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=82bacfb9-****" readonly style="color:var(--muted)">
</div>
<div class="form-row">
<div class="form-group">
<label class="form-label">告警级别</label>
<label class="form-checkbox"><input type="checkbox" checked> critical</label>
<label class="form-checkbox"><input type="checkbox" checked> warning</label>
<label class="form-checkbox"><input type="checkbox"> info</label>
</div>
<div class="form-group">
<label class="form-label">冷却时间(分钟)</label>
<input class="form-input" value="5" style="width:80px">
</div>
<div class="form-group">
<label class="form-label">免打扰时段</label>
<label class="form-checkbox"><input type="checkbox" checked> 启用免打扰</label>
<div style="display:flex;gap:var(--space-2);align-items:center;margin-top:var(--space-2)">
<input class="form-input" value="23:00" style="width:80px">
<span style="color:var(--muted)"></span>
<input class="form-input" value="07:00" style="width:80px">
</div>
<label class="form-checkbox" style="margin-top:var(--space-2)"><input type="checkbox" checked> critical 仍然推送</label>
</div>
</div>
<div class="form-actions">
<button class="btn btn-primary" onclick="showToast('运维群设置已保存')">保存设置</button>
</div>
</div>
</div>
<div class="card" style="margin-bottom:var(--space-6);">
<div class="card-header">
<span class="card-title">渠道 2: 开发群</span>
<div style="display:flex;gap:var(--space-2);">
<button class="btn btn-sm btn-primary" onclick="showToast('测试消息已发送到开发群')">测试推送</button>
<button class="btn btn-sm btn-secondary">删除</button>
</div>
</div>
<div class="card-body">
<div class="form-group">
<label class="form-label">Webhook URL</label>
<input class="form-input" value="https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=********" readonly style="color:var(--muted)">
</div>
<div class="form-row">
<div class="form-group">
<label class="form-label">告警级别</label>
<label class="form-checkbox"><input type="checkbox" checked> critical</label>
<label class="form-checkbox"><input type="checkbox"> warning</label>
<label class="form-checkbox"><input type="checkbox"> info</label>
</div>
<div class="form-group">
<label class="form-label">冷却时间(分钟)</label>
<input class="form-input" value="10" style="width:80px">
</div>
<div class="form-group">
<label class="form-label">免打扰时段</label>
<label class="form-checkbox"><input type="checkbox"> 不启用免打扰</label>
</div>
</div>
<div class="form-actions">
<button class="btn btn-primary" onclick="showToast('开发群设置已保存')">保存设置</button>
</div>
</div>
</div>
<button class="btn btn-secondary" onclick="showToast('新渠道已添加')">+ 添加渠道</button>
</div>
<!-- ==================== 页面 3: 告警历史 ==================== -->
<div id="page-alerts" class="page">
<h2 class="section-title">告警历史</h2>
<div class="card">
<div class="card-body">
<div style="display:flex;gap:var(--space-4);margin-bottom:var(--space-6);">
<select class="form-input" style="width:auto;"><option>全部级别</option><option>critical</option><option>warning</option><option>info</option></select>
<select class="form-input" style="width:auto;"><option>全部渠道</option><option>运维群</option><option>开发群</option></select>
<select class="form-input" style="width:auto;"><option>最近 7 天</option><option>最近 30 天</option></select>
</div>
<table style="width:100%;border-collapse:collapse;font-size:var(--text-sm);">
<thead><tr style="background:var(--bg-subtle);border-bottom:1px solid var(--border);">
<th style="padding:var(--space-3) var(--space-4);text-align:left;font-weight:600;color:var(--fg-subtle)">时间</th>
<th style="padding:var(--space-3) var(--space-4);text-align:left;font-weight:600;color:var(--fg-subtle)">级别</th>
<th style="padding:var(--space-3) var(--space-4);text-align:left;font-weight:600;color:var(--fg-subtle)">渠道</th>
<th style="padding:var(--space-3) var(--space-4);text-align:left;font-weight:600;color:var(--fg-subtle)">服务</th>
<th style="padding:var(--space-3) var(--space-4);text-align:left;font-weight:600;color:var(--fg-subtle)">类型</th>
<th style="padding:var(--space-3) var(--space-4);text-align:left;font-weight:600;color:var(--fg-subtle)">结果</th>
</tr></thead>
<tbody>
<tr style="border-bottom:1px solid var(--border-subtle);"><td style="padding:var(--space-3) var(--space-4)">14:35:00</td><td style="padding:var(--space-3) var(--space-4)"><span class="badge badge-danger">critical</span></td><td style="padding:var(--space-3) var(--space-4)">运维群</td><td style="padding:var(--space-3) var(--space-4)">Authelia SSO</td><td style="padding:var(--space-3) var(--space-4)"><span class="tag tag-red">异常告警</span></td><td style="padding:var(--space-3) var(--space-4);color:var(--success)"></td></tr>
<tr style="border-bottom:1px solid var(--border-subtle);"><td style="padding:var(--space-3) var(--space-4)">13:20:15</td><td style="padding:var(--space-3) var(--space-4)"><span class="badge badge-danger">critical</span></td><td style="padding:var(--space-3) var(--space-4)">运维群</td><td style="padding:var(--space-3) var(--space-4)">Redis</td><td style="padding:var(--space-3) var(--space-4)"><span class="tag tag-green">恢复通知</span></td><td style="padding:var(--space-3) var(--space-4);color:var(--success)"></td></tr>
<tr style="border-bottom:1px solid var(--border-subtle);"><td style="padding:var(--space-3) var(--space-4)">13:17:30</td><td style="padding:var(--space-3) var(--space-4)"><span class="badge badge-danger">critical</span></td><td style="padding:var(--space-3) var(--space-4)">运维群</td><td style="padding:var(--space-3) var(--space-4)">Redis</td><td style="padding:var(--space-3) var(--space-4)"><span class="tag tag-red">异常告警</span></td><td style="padding:var(--space-3) var(--space-4);color:var(--success)"></td></tr>
<tr style="border-bottom:1px solid var(--border-subtle);"><td style="padding:var(--space-3) var(--space-4)">13:02:00</td><td style="padding:var(--space-3) var(--space-4)"><span class="badge badge-danger">critical</span></td><td style="padding:var(--space-3) var(--space-4)">开发群</td><td style="padding:var(--space-3) var(--space-4)">Redis</td><td style="padding:var(--space-3) var(--space-4)"><span class="tag tag-red">异常告警</span></td><td style="padding:var(--space-3) var(--space-4);color:var(--danger)"></td></tr>
<tr style="border-bottom:1px solid var(--border-subtle);"><td style="padding:var(--space-3) var(--space-4)">09:45:00</td><td style="padding:var(--space-3) var(--space-4)"><span class="badge badge-success">warning</span></td><td style="padding:var(--space-3) var(--space-4)">运维群</td><td style="padding:var(--space-3) var(--space-4)">Nginx 代理</td><td style="padding:var(--space-3) var(--space-4)"><span class="tag tag-indigo">抖动告警</span></td><td style="padding:var(--space-3) var(--space-4);color:var(--success)"></td></tr>
</tbody>
</table>
<div style="display:flex;justify-content:center;gap:var(--space-1);margin-top:var(--space-6);">
<button class="btn btn-sm btn-secondary" disabled>上一页</button>
<button class="btn btn-sm btn-primary">1</button>
<button class="btn btn-sm btn-secondary">2</button>
<button class="btn btn-sm btn-secondary">下一页</button>
</div>
</div>
</div>
</div>
<!-- ==================== 页面 4: 状态历史 ==================== -->
<div id="page-history" class="page">
<h2 class="section-title">状态变更历史</h2>
<div class="card">
<div class="card-body">
<div style="display:flex;gap:var(--space-4);margin-bottom:var(--space-6);">
<select class="form-input" style="width:auto;"><option>全部服务</option></select>
<select class="form-input" style="width:auto;"><option>最近 7 天</option></select>
</div>
<table style="width:100%;border-collapse:collapse;font-size:var(--text-sm);">
<thead><tr style="background:var(--bg-subtle);">
<th style="padding:var(--space-3) var(--space-4);text-align:left;font-weight:600;">服务</th>
<th style="padding:var(--space-3) var(--space-4);text-align:left;font-weight:600;">异常开始</th>
<th style="padding:var(--space-3) var(--space-4);text-align:left;font-weight:600;">恢复时间</th>
<th style="padding:var(--space-3) var(--space-4);text-align:left;font-weight:600;">持续</th>
<th style="padding:var(--space-3) var(--space-4);text-align:left;font-weight:600;">关联告警</th>
</tr></thead>
<tbody>
<tr style="border-bottom:1px solid var(--border-subtle);background:var(--danger-subtle);"><td style="padding:var(--space-3) var(--space-4);font-weight:500;">Authelia SSO</td><td style="padding:var(--space-3) var(--space-4)">14:30:00</td><td style="padding:var(--space-3) var(--space-4)"></td><td style="padding:var(--space-3) var(--space-4);color:var(--danger);">进行中</td><td style="padding:var(--space-3) var(--space-4)">1 条</td></tr>
<tr style="border-bottom:1px solid var(--border-subtle);"><td style="padding:var(--space-3) var(--space-4)">Redis</td><td style="padding:var(--space-3) var(--space-4)">13:17:30</td><td style="padding:var(--space-3) var(--space-4)">13:20:15</td><td style="padding:var(--space-3) var(--space-4)">2m45s</td><td style="padding:var(--space-3) var(--space-4)">2 条</td></tr>
<tr style="border-bottom:1px solid var(--border-subtle);"><td style="padding:var(--space-3) var(--space-4)">Nginx 代理</td><td style="padding:var(--space-3) var(--space-4)">09:40:00</td><td style="padding:var(--space-3) var(--space-4)">09:45:00</td><td style="padding:var(--space-3) var(--space-4)">5m00s</td><td style="padding:var(--space-3) var(--space-4)">1 条(抖动)</td></tr>
</tbody>
</table>
<div style="display:flex;gap:var(--space-6);margin-top:var(--space-6);">
<div class="stat-card" style="flex:1;">
<div class="stat-card-title">本月可用率</div>
<div class="stat-card-value" style="color:var(--success);margin:0;">99.8%</div>
</div>
<div class="stat-card" style="flex:1;">
<div class="stat-card-title">平均恢复时间</div>
<div class="stat-card-value" style="font-size:var(--text-xl);margin:0;">2 分 30 秒</div>
</div>
</div>
</div>
</div>
</div>
<!-- ==================== 页面 5: 服务管理 ==================== -->
<div id="page-services" class="page">
<h2 class="section-title">服务管理</h2>
<div class="card">
<div class="card-header">
<span class="card-title">监控服务清单</span>
<button class="btn btn-primary btn-sm" onclick="showToast('添加服务对话框(预览模式)')">+ 添加服务</button>
</div>
<div class="card-body" style="padding:0;">
<table style="width:100%;border-collapse:collapse;font-size:var(--text-sm);">
<thead><tr style="background:var(--bg-subtle);">
<th style="padding:var(--space-3) var(--space-4);text-align:left;">名称</th>
<th style="padding:var(--space-3) var(--space-4);text-align:left;">类别</th>
<th style="padding:var(--space-3) var(--space-4);text-align:left;">告警级别</th>
<th style="padding:var(--space-3) var(--space-4);text-align:left;">检查方式</th>
<th style="padding:var(--space-3) var(--space-4);text-align:left;">状态</th>
<th style="padding:var(--space-3) var(--space-4);text-align:left;">操作</th>
</tr></thead>
<tbody>
<tr style="border-bottom:1px solid var(--border-subtle);"><td style="padding:var(--space-3) var(--space-4);font-weight:500;">Authelia SSO</td><td style="padding:var(--space-3) var(--space-4)">endpoint</td><td style="padding:var(--space-3) var(--space-4)"><span class="tag tag-red">critical</span></td><td style="padding:var(--space-3) var(--space-4)">HTTP + Docker</td><td style="padding:var(--space-3) var(--space-4)"><span class="status-badge status-abnormal">异常</span></td><td style="padding:var(--space-3) var(--space-4)"><button class="btn btn-sm btn-ghost">编辑</button><button class="btn btn-sm btn-ghost">检查</button></td></tr>
<tr style="border-bottom:1px solid var(--border-subtle);"><td style="padding:var(--space-3) var(--space-4);font-weight:500;">Redis</td><td style="padding:var(--space-3) var(--space-4)">container</td><td style="padding:var(--space-3) var(--space-4)"><span class="tag tag-red">critical</span></td><td style="padding:var(--space-3) var(--space-4)">Docker</td><td style="padding:var(--space-3) var(--space-4)"><span class="status-badge status-normal">正常</span></td><td style="padding:var(--space-3) var(--space-4)"><button class="btn btn-sm btn-ghost">编辑</button></td></tr>
<tr style="border-bottom:1px solid var(--border-subtle);"><td style="padding:var(--space-3) var(--space-4);font-weight:500;">OA 门户</td><td style="padding:var(--space-3) var(--space-4)">endpoint</td><td style="padding:var(--space-3) var(--space-4)"><span class="tag tag-green">warning</span></td><td style="padding:var(--space-3) var(--space-4)">HTTP</td><td style="padding:var(--space-3) var(--space-4)"><span class="status-badge status-normal">正常</span></td><td style="padding:var(--space-3) var(--space-4)"><button class="btn btn-sm btn-ghost">编辑</button></td></tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- ==================== 页面 6: 用户管理 ==================== -->
<div id="page-users" class="page">
<h2 class="section-title">用户管理</h2>
<div class="card">
<div class="card-body" style="padding:0;">
<table style="width:100%;border-collapse:collapse;font-size:var(--text-sm);">
<thead><tr style="background:var(--bg-subtle);">
<th style="padding:var(--space-3) var(--space-4);text-align:left;">用户名</th>
<th style="padding:var(--space-3) var(--space-4);text-align:left;">显示名</th>
<th style="padding:var(--space-3) var(--space-4);text-align:left;">邮箱</th>
<th style="padding:var(--space-3) var(--space-4);text-align:left;">角色</th>
<th style="padding:var(--space-3) var(--space-4);text-align:left;">操作</th>
</tr></thead>
<tbody>
<tr style="border-bottom:1px solid var(--border-subtle);"><td style="padding:var(--space-3) var(--space-4);font-weight:500;">localadmin</td><td style="padding:var(--space-3) var(--space-4)">超级管理员</td><td style="padding:var(--space-3) var(--space-4)"></td><td style="padding:var(--space-3) var(--space-4)"><span class="tag tag-indigo">admin</span></td><td style="padding:var(--space-3) var(--space-4);color:var(--muted)">不可修改</td></tr>
<tr style="border-bottom:1px solid var(--border-subtle);"><td style="padding:var(--space-3) var(--space-4);">gaoxiaopei</td><td style="padding:var(--space-3) var(--space-4)">高晓沛</td><td style="padding:var(--space-3) var(--space-4)">gaoxiaopei@tlyq.ai</td><td style="padding:var(--space-3) var(--space-4)"><select class="form-input" style="width:auto;padding:2px var(--space-2);"><option>admin</option><option>viewer</option></select></td><td style="padding:var(--space-3) var(--space-4)"><button class="btn btn-sm btn-primary">保存</button></td></tr>
<tr style="border-bottom:1px solid var(--border-subtle);"><td style="padding:var(--space-3) var(--space-4);">zhangsan</td><td style="padding:var(--space-3) var(--space-4)">张三</td><td style="padding:var(--space-3) var(--space-4)">zhangsan@tlyq.ai</td><td style="padding:var(--space-3) var(--space-4)"><span class="tag tag-green">viewer</span></td><td style="padding:var(--space-3) var(--space-4)"><button class="btn btn-sm btn-primary">保存</button></td></tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</main>
</div>
<!-- Toast -->
<div id="toast" class="toast">
<div class="toast-title">提示</div>
<div class="toast-body" id="toast-body"></div>
</div>
<script>
// 页面切换
function showPage(name) {
document.querySelectorAll('.page').forEach(p => p.classList.remove('active'));
document.getElementById('page-' + name).classList.add('active');
document.querySelectorAll('.sidebar-item').forEach(i => i.classList.remove('active'));
event.target.closest('.sidebar-item').classList.add('active');
const names = {dashboard:'仪表盘(行式)','dashboard-cards':'仪表盘(卡片式)','dashboard-plan-a':'仪表盘方案A','dashboard-pro':'仪表盘Pro','dashboard-design':'仪表盘Design','dashboard-final':'仪表盘(最终)',services:'服务管理',settings:'告警设置',alerts:'告警历史',history:'状态历史',users:'用户管理'};
document.getElementById('breadcrumb-page').textContent = names[name] || name;
}
// 主题切换
function toggleTheme() {
document.documentElement.classList.toggle('dark');
}
// Toast 提示
function showToast(msg) {
const t = document.getElementById('toast');
document.getElementById('toast-body').textContent = msg;
t.classList.add('show');
setTimeout(() => t.classList.remove('show'), 2500);
}
// 键盘切换1-6 键切换页面
document.addEventListener('keydown', e => {
const pages = ['dashboard','services','settings','alerts','history','users'];
const n = parseInt(e.key);
if (n >= 1 && n <= 6) showPage(pages[n-1]);
});
</script>
</body>
</html>