Files
Nexus/web/app/preview-v3.html
T
Nexus Deploy a2ae74d582 release: BUG fixes batch 1-6, full bug scan docs, Ubuntu/Linux dev
- Backend: auth refresh reuse, schedule/retry/sync/agent/files fixes
- Frontend: push WS, files ETag browse, vite build assets
- Docs: audit/changelog, production deploy gate, bug scan registry B7-77
- Deploy: deploy-production.sh, prune assets, gate logs
2026-06-04 14:01:14 +08:00

370 lines
25 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="light">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>Nexus v7 — 全新设计预览</title>
<script src="/app/vendor/tailwindcss-browser.js"></script>
<style type="text/tailwindcss">
@theme {
--color-brand-50: #eff6ff; --color-brand-100: #dbeafe; --color-brand-200: #bfdbfe;
--color-brand-500: #3b82f6; --color-brand-600: #2563eb; --color-brand-700: #1d4ed8;
}
@custom-variant dark (&:where(.dark, .dark *));
</style>
<style>
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
:root {
--bg: #fafbfc; --bg-elevated: #ffffff; --bg-muted: #f3f4f6;
--border: #e5e7eb; --border-light: #f3f4f6;
--text: #111827; --text-secondary: #6b7280; --text-muted: #9ca3af;
--shadow-sm: 0 1px 2px rgba(0,0,0,.04); --shadow-md: 0 4px 12px rgba(0,0,0,.06);
}
:root.dark {
--bg: #09090b; --bg-elevated: #18181b; --bg-muted: #27272a;
--border: #27272a; --border-light: #1f1f23;
--text: #fafafa; --text-secondary: #a1a1aa; --text-muted: #71717a;
--shadow-sm: 0 1px 2px rgba(0,0,0,.2); --shadow-md: 0 4px 12px rgba(0,0,0,.4);
}
body {
background: var(--bg); color: var(--text);
font-family: -apple-system,BlinkMacSystemFont,'Segoe UI','PingFang SC','Microsoft YaHei',sans-serif;
-webkit-font-smoothing: antialiased;
}
input,select,textarea,button{font-family:inherit}
.scrollbar::-webkit-scrollbar{width:4px}
.scrollbar::-webkit-scrollbar-track{background:transparent}
.scrollbar::-webkit-scrollbar-thumb{background:var(--border);border-radius:2px}
/* ── Sidebar ── */
.nav-item {
display:flex;align-items:center;gap:10px;padding:8px 12px;border-radius:8px;
font-size:13px;font-weight:500;color:var(--text-secondary);
transition:all .15s;cursor:pointer;text-decoration:none;
}
.nav-item:hover{background:var(--bg-muted);color:var(--text)}
.nav-item.active{background:rgba(59,130,246,.08);color:#3b82f6}
/* ── Buttons ── */
.btn {
display:inline-flex;align-items:center;justify-content:center;gap:6px;
height:34px;padding:0 14px;border-radius:7px;font-size:13px;font-weight:500;
transition:all .15s;cursor:pointer;border:none;line-height:1;
}
.btn:focus-visible{outline:2px solid #3b82f6;outline-offset:2px}
.btn-primary{background:#3b82f6;color:#fff}.btn-primary:hover{background:#2563eb}
.btn-secondary{background:#fff;color:var(--text);border:1px solid var(--border);box-shadow:var(--shadow-sm)}.btn-secondary:hover{background:var(--bg-muted)}
.btn-ghost{background:transparent;color:var(--text-secondary)}.btn-ghost:hover{background:var(--bg-muted);color:var(--text)}
.btn-danger{background:#ef4444;color:#fff}.btn-danger:hover{background:#dc2626}
.btn-sm{height:28px;padding:0 10px;font-size:12px}
/* ── Inputs ── */
.input {
height:34px;padding:0 11px;border-radius:7px;font-size:13px;border:1px solid var(--border);
background:#fff;color:var(--text);outline:none;transition:all .15s;width:100%;
box-shadow:var(--shadow-sm);
}
.dark .input{background:var(--bg-muted)}
.input:focus{border-color:#3b82f6;box-shadow:0 0 0 3px rgba(59,130,246,.1)}
select.input{
-webkit-appearance:none;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%236b7280' stroke-width='2'%3E%3Cpath d='M2 4l4 4 4-4'/%3E%3C/svg%3E");
background-repeat:no-repeat;background-position:right 10px center;padding-right:32px;
}
/* ── Stat Cards ── */
.stat-card {
background:var(--bg-elevated);border:1px solid var(--border);border-radius:12px;
padding:18px 20px;box-shadow:var(--shadow-sm);transition:box-shadow .2s;
}
.stat-card:hover{box-shadow:var(--shadow-md)}
/* ── Table ── */
.table-wrap {
border:1px solid var(--border);border-radius:12px;overflow:hidden;
background:var(--bg-elevated);box-shadow:var(--shadow-sm);
}
.table-wrap table{width:100%;border-collapse:collapse;font-size:13px}
.table-wrap th {
padding:10px 16px;text-align:left;font-size:11px;font-weight:600;
text-transform:uppercase;letter-spacing:.05em;color:var(--text-secondary);
background:var(--bg-muted);border-bottom:1px solid var(--border);
}
.table-wrap td {
padding:11px 16px;border-bottom:1px solid var(--border-light);
color:var(--text);vertical-align:middle;
}
.table-wrap tr:last-child td{border-bottom:none}
.table-wrap tbody tr{transition:background .1s}
.table-wrap tbody tr:nth-child(even){background:rgba(59,130,246,.012)}
.dark .table-wrap tbody tr:nth-child(even){background:rgba(255,255,255,.008)}
.table-wrap tbody tr:hover{background:rgba(59,130,246,.04)}
/* ── Status Badges ── */
.badge-online{display:inline-flex;align-items:center;gap:5px;font-size:12px;font-weight:500;color:#059669}
.badge-online::before{content:'';width:7px;height:7px;border-radius:50%;background:#10b981}
.badge-offline{display:inline-flex;align-items:center;gap:5px;font-size:12px;font-weight:500;color:#dc2626}
.badge-offline::before{content:'';width:7px;height:7px;border-radius:50%;background:#ef4444}
.badge-none{display:inline-flex;align-items:center;gap:5px;font-size:12px;font-weight:500;color:var(--text-muted)}
.badge-none::before{content:'';width:7px;height:7px;border-radius:50%;background:var(--text-muted)}
.link-action{font-size:12px;color:var(--text-secondary);text-decoration:none;margin-right:10px;cursor:pointer;background:none;border:none;transition:.1s}
.link-action:hover{color:var(--text)}
.link-action.primary{color:#3b82f6}.link-action.primary:hover{color:#1d4ed8}
.link-action.success{color:#059669}.link-action.success:hover{color:#047857}
.link-action.danger{color:#ef4444}.link-action.danger:hover{color:#dc2626}
/* ── Pagination ── */
.pagination{display:flex;align-items:center;gap:4px;margin-top:14px}
.page-btn{width:30px;height:30px;display:flex;align-items:center;justify-content:center;border-radius:6px;border:1px solid var(--border);background:var(--bg-elevated);color:var(--text-secondary);font-size:12px;cursor:pointer;transition:all .15s}
.page-btn:hover{background:var(--bg-muted);color:var(--text)}
.page-btn.active{background:#3b82f6;color:#fff;border-color:#3b82f6;box-shadow:0 2px 8px rgba(59,130,246,.3)}
.page-btn:disabled{opacity:.3;cursor:default}
/* ── Tabs ── */
.tab-bar{display:flex;border-bottom:1px solid var(--border)}
.tab-btn{padding:9px 16px;font-size:13px;font-weight:500;color:var(--text-secondary);border-bottom:2px solid transparent;transition:all .15s;cursor:pointer;background:none;border-top:none;border-left:none;border-right:none}
.tab-btn:hover{color:var(--text)}
.tab-btn.active{color:#3b82f6;border-bottom-color:#3b82f6}
/* ── Modal ── */
.modal-overlay{position:fixed;inset:0;z-index:50;display:flex;align-items:center;justify-content:center;padding:16px;background:rgba(0,0,0,.4);backdrop-filter:blur(4px);opacity:0;visibility:hidden;transition:all .2s}
.modal-overlay.open{opacity:1;visibility:visible}
.modal{background:var(--bg-elevated);border:1px solid var(--border);border-radius:14px;padding:24px;width:100%;max-width:440px;max-height:90vh;overflow-y:auto;box-shadow:0 20px 60px rgba(0,0,0,.15)}
/* ── Toast ── */
.toast{position:fixed;top:16px;right:16px;z-index:9999;display:flex;flex-direction:column;gap:8px;max-width:380px;pointer-events:none}
.toast-item{pointer-events:auto;display:flex;align-items:flex-start;gap:10px;padding:12px 16px;border-radius:10px;font-size:13px;border:1px solid var(--border);background:var(--bg-elevated);color:var(--text);box-shadow:var(--shadow-md)}
.toast-success{border-left:3px solid #10b981}
.toast-error{border-left:3px solid #ef4444}
.toast-warning{border-left:3px solid #f59e0b}
/* ── Animations ── */
@keyframes slideIn{from{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:translateY(0)}}
.slide-in{animation:slideIn .25s ease}
</style>
</head>
<body class="flex min-h-screen">
<div class="toast" id="toastContainer"></div>
<!-- ════════ SIDEBAR ════════ -->
<aside class="w-56 shrink-0 flex flex-col border-r scrollbar" style="background:var(--bg-elevated);border-color:var(--border);overflow-y:auto">
<div class="px-4 py-4 border-b" style="border-color:var(--border)">
<div class="flex items-center gap-2.5">
<svg class="w-6 h-6" style="color:#3b82f6" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2"/></svg>
<span class="font-bold text-[15px]" style="color:var(--text)">Nexus</span>
</div>
</div>
<nav class="flex-1 px-2.5 py-3 space-y-0.5">
<a class="nav-item active"><svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="3" y="3" width="7" height="7" rx="1.5"/><rect x="14" y="3" width="7" height="7" rx="1.5"/><rect x="3" y="14" width="7" height="7" rx="1.5"/><rect x="14" y="14" width="7" height="7" rx="1.5"/></svg>仪表盘</a>
<a class="nav-item"><svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M5.25 14.25h13.5m-13.5 0a3 3 0 01-3-3m3 3a3 3 0 100 6h13.5a3 3 0 100-6m-16.5-3a3 3 0 013-3h13.5a3 3 0 013 3m-19.5 0a4.5 4.5 0 01.9-2.7L5.737 5.1a3.375 3.375 0 012.7-1.35h7.126c1.062 0 2.062.5 2.7 1.35l2.587 3.45a4.5 4.5 0 01.9 2.7"/></svg>服务器</a>
<a class="nav-item"><svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M2.25 12.75V12A2.25 2.25 0 014.5 9.75h15A2.25 2.25 0 0121.75 12v.75m-8.69-6.44l-2.12-2.12a1.5 1.5 0 00-1.061-.44H4.5A2.25 2.25 0 002.25 6v12a2.25 2.25 0 002.25 2.25h15A2.25 2.25 0 0021.75 18V9a2.25 2.25 0 00-2.25-2.25h-5.379a1.5 1.5 0 01-1.06-.44z"/></svg>文件管理</a>
<a class="nav-item"><svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5m-13.5-9L12 3m0 0l4.5 4.5M12 3v13.5"/></svg>推送</a>
<a class="nav-item"><svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M17.25 6.75L22.5 12l-5.25 5.25m-10.5 0L1.5 12l5.25-5.25m7.5-3l-4.5 16.5"/></svg>脚本库</a>
<div style="border-top:1px solid var(--border);margin:6px 0"></div>
<a class="nav-item"><svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M6.75 7.5l3 2.25-3 2.25m4.5 0h3m-9 8.25h13.5A2.25 2.25 0 0021 18V6a2.25 2.25 0 00-2.25-2.25H5.25A2.25 2.25 0 003 6v12a2.25 2.25 0 002.25 2.25z"/></svg>命令日志</a>
<a class="nav-item"><svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M14.857 17.082a23.848 23.848 0 005.454-1.31A8.967 8.967 0 0118 9.75V9A6 6 0 006 9v.75a8.967 8.967 0 01-2.312 6.022c1.733.64 3.56 1.085 5.455 1.31m5.714 0a24.255 24.255 0 01-5.714 0"/></svg>告警中心</a>
<a class="nav-item"><svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path stroke-linecap="round" stroke-linejoin="round" d="M9 12h3.75M9 15h3.75M9 18h3.75m3 .75H18a2.25 2.25 0 002.25-2.25V6.108c0-1.135-.845-2.098-1.976-2.192M5.801 8.248c-.065.21-.1.433-.1.664 0 .414.336.75.75.75h4.5a.75.75 0 00.75-.75A2.25 2.25 0 0010.25 7.5M8.25 8.25H4.875c-.621 0-1.125.504-1.125 1.125v11.25c0 .621.504 1.125 1.125 1.125h9.75c.621 0 1.125-.504 1.125-1.125V9.375c0-.621-.504-1.125-1.125-1.125H8.25zM6.75 12h.008v.008H6.75V12zm0 3h.008v.008H6.75V15zm0 3h.008v.008H6.75V18z"/></svg>审计日志</a>
<a class="nav-item"><svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="12" cy="12" r="3"/><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/></svg>设置</a>
</nav>
<div style="border-top:1px solid var(--border);padding:10px 14px">
<div class="flex items-center justify-between">
<div class="flex items-center gap-2">
<div class="w-6 h-6 rounded-full flex items-center justify-center text-[10px] font-bold" style="background:rgba(59,130,246,.1);color:#3b82f6">A</div>
<span class="text-xs" style="color:var(--text-secondary)">admin</span>
</div>
<button onclick="document.documentElement.classList.toggle('dark')" class="p-1 rounded-md text-sm transition hover:opacity-70" style="color:var(--text-muted)" title="切换主题">
<svg class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="12" cy="12" r="4"/><path d="M12 2v2m0 16v2M4.93 4.93l1.41 1.41m11.32 11.32l1.41 1.41M2 12h2m16 0h2M4.93 19.07l1.41-1.41m11.32-11.32l1.41-1.41"/></svg>
</button>
</div>
</div>
</aside>
<!-- ════════ MAIN ════════ -->
<div class="flex-1 flex flex-col min-w-0">
<header class="h-14 shrink-0 flex items-center justify-between px-6 border-b" style="background:var(--bg-elevated);border-color:var(--border)">
<h1 class="text-[15px] font-semibold" style="color:var(--text)">仪表盘</h1>
<button class="btn btn-secondary btn-sm">刷新</button>
</header>
<main class="flex-1 overflow-y-auto p-6 space-y-6 scrollbar">
<!-- Stats -->
<div class="grid grid-cols-2 lg:grid-cols-4 gap-4">
<div class="stat-card"><div class="text-[11px] font-semibold uppercase tracking-wide mb-2" style="color:var(--text-muted)">服务器总数</div><div class="text-[28px] font-bold tracking-tight" style="color:var(--text)">8</div></div>
<div class="stat-card"><div class="text-[11px] font-semibold uppercase tracking-wide mb-2" style="color:var(--text-muted)">在线</div><div class="text-[28px] font-bold tracking-tight" style="color:#059669">1</div></div>
<div class="stat-card"><div class="text-[11px] font-semibold uppercase tracking-wide mb-2" style="color:var(--text-muted)">离线</div><div class="text-[28px] font-bold tracking-tight" style="color:#dc2626">7</div></div>
<div class="stat-card"><div class="text-[11px] font-semibold uppercase tracking-wide mb-2" style="color:var(--text-muted)">告警中</div><div class="text-[28px] font-bold tracking-tight" style="color:#d97706">0</div></div>
</div>
<!-- Table -->
<div>
<div class="flex items-center justify-between mb-3">
<h2 class="text-[14px] font-semibold" style="color:var(--text)">服务器列表</h2>
<div class="flex items-center gap-2">
<select class="input" style="width:120px"><option>全部分类</option><option>商城</option><option>数据库</option></select>
<input class="input" style="width:200px" placeholder="搜索...">
<button class="btn btn-primary btn-sm">+ 添加</button>
</div>
</div>
<div class="table-wrap">
<table>
<thead><tr><th style="width:100px">状态</th><th>名称</th><th>地址</th><th style="width:100px">分类</th><th style="width:100px">Agent</th><th style="width:120px">心跳</th><th style="text-align:right;width:180px">操作</th></tr></thead>
<tbody>
<tr>
<td><span class="badge-online">在线</span></td>
<td><span style="font-weight:600">prod-web-01</span><span class="text-[10px] ml-2 text-red-500">82%</span></td>
<td style="color:var(--text-secondary)">192.168.1.10<span style="color:var(--text-muted)">:22</span></td>
<td><span style="display:inline-block;padding:1px 7px;border-radius:4px;font-size:11px;background:rgba(59,130,246,.08);color:#3b82f6">商城</span></td>
<td style="color:var(--text-secondary)">v1.2.0</td>
<td style="color:var(--text-muted);font-size:12px">3分钟前</td>
<td style="text-align:right">
<button class="link-action primary">终端</button>
<button class="link-action">文件</button>
<button class="link-action success">检查</button>
<button class="link-action">编辑</button>
<button class="link-action danger">删除</button>
</td>
</tr>
<tr>
<td><span class="badge-offline">离线</span></td>
<td><span style="font-weight:600">dev-db-01</span></td>
<td style="color:var(--text-secondary)">10.0.0.5<span style="color:var(--text-muted)">:22</span></td>
<td style="color:var(--text-muted)"></td>
<td style="color:var(--text-secondary)">v1.1.8</td>
<td style="color:var(--text-muted);font-size:12px">2小时前</td>
<td style="text-align:right">
<button class="link-action primary">终端</button>
<button class="link-action">文件</button>
<button class="link-action">编辑</button>
<button class="link-action danger">删除</button>
</td>
</tr>
<tr>
<td><span class="badge-none">未安装</span></td>
<td><span style="font-weight:600">new-server-03</span></td>
<td style="color:var(--text-secondary)">172.16.0.8<span style="color:var(--text-muted)">:22</span></td>
<td style="color:var(--text-muted)"></td>
<td style="color:var(--text-muted)"></td>
<td style="color:var(--text-muted);font-size:12px"></td>
<td style="text-align:right">
<button class="link-action primary">终端</button>
<button class="link-action">编辑</button>
<button class="link-action danger">删除</button>
</td>
</tr>
<tr>
<td><span class="badge-online">在线</span></td>
<td><span style="font-weight:600">cache-redis-01</span></td>
<td style="color:var(--text-secondary)">10.0.0.10<span style="color:var(--text-muted)">:6379</span></td>
<td><span style="display:inline-block;padding:1px 7px;border-radius:4px;font-size:11px;background:rgba(59,130,246,.08);color:#3b82f6">数据库</span></td>
<td style="color:var(--text-secondary)">v1.2.0</td>
<td style="color:var(--text-muted);font-size:12px">1分钟前</td>
<td style="text-align:right">
<button class="link-action primary">终端</button>
<button class="link-action">文件</button>
<button class="link-action success">检查</button>
<button class="link-action">编辑</button>
<button class="link-action danger">删除</button>
</td>
</tr>
<tr>
<td><span class="badge-offline">离线</span></td>
<td><span style="font-weight:600">old-web-02</span></td>
<td style="color:var(--text-secondary)">192.168.1.20<span style="color:var(--text-muted)">:22</span></td>
<td><span style="display:inline-block;padding:1px 7px;border-radius:4px;font-size:11px;background:rgba(59,130,246,.08);color:#3b82f6">商城</span></td>
<td style="color:var(--text-muted)"></td>
<td style="color:var(--text-muted);font-size:12px">3天前</td>
<td style="text-align:right">
<button class="link-action primary">终端</button>
<button class="link-action">编辑</button>
<button class="link-action danger">删除</button>
</td>
</tr>
</tbody>
</table>
</div>
<div class="pagination">
<span class="text-xs" style="color:var(--text-muted);margin-right:auto">第 1 / 2 页,共 8 台</span>
<button class="page-btn" disabled></button>
<button class="page-btn active">1</button>
<button class="page-btn">2</button>
<button class="page-btn"></button>
</div>
</div>
<!-- Form + Components -->
<div class="grid grid-cols-2 gap-4">
<div class="stat-card space-y-3">
<h3 class="text-sm font-semibold" style="color:var(--text)">添加服务器</h3>
<div class="grid grid-cols-2 gap-3">
<div><label class="block text-[11px] font-semibold mb-1.5" style="color:var(--text-secondary)">名称</label><input class="input" placeholder="web-server-01"></div>
<div><label class="block text-[11px] font-semibold mb-1.5" style="color:var(--text-secondary)">地址</label><input class="input" placeholder="192.168.1.10"></div>
</div>
<div class="grid grid-cols-3 gap-3">
<div><label class="block text-[11px] font-semibold mb-1.5" style="color:var(--text-secondary)">端口</label><input class="input" value="22"></div>
<div><label class="block text-[11px] font-semibold mb-1.5" style="color:var(--text-secondary)">用户</label><input class="input" value="root"></div>
<div><label class="block text-[11px] font-semibold mb-1.5" style="color:var(--text-secondary)">认证</label><select class="input"><option>密码</option><option>密钥</option></select></div>
</div>
<div class="flex gap-2 pt-1">
<button class="btn btn-primary btn-sm" onclick="showToast('success','服务器已添加')">保存</button>
<button class="btn btn-secondary btn-sm">取消</button>
</div>
</div>
<div class="stat-card">
<h3 class="text-sm font-semibold mb-4" style="color:var(--text)">组件展示</h3>
<div class="space-y-4">
<div class="flex flex-wrap gap-2">
<button class="btn btn-primary btn-sm">主要</button>
<button class="btn btn-secondary btn-sm">次要</button>
<button class="btn btn-ghost btn-sm">幽灵</button>
<button class="btn btn-danger btn-sm" onclick="showToast('error','操作失败')">危险</button>
</div>
<div style="height:1px;background:var(--border)"></div>
<div class="flex gap-3"><span class="badge-online">在线</span><span class="badge-offline">离线</span><span class="badge-none">未安装</span></div>
<div style="height:1px;background:var(--border)"></div>
<div class="tab-bar">
<button class="tab-btn active" onclick="this.parentElement.querySelectorAll('.tab-btn').forEach(b=>b.classList.remove('active'));this.classList.add('active')">系统信息</button>
<button class="tab-btn" onclick="this.parentElement.querySelectorAll('.tab-btn').forEach(b=>b.classList.remove('active'));this.classList.add('active')">同步日志</button>
<button class="tab-btn" onclick="this.parentElement.querySelectorAll('.tab-btn').forEach(b=>b.classList.remove('active'));this.classList.add('active')">SSH会话</button>
</div>
<div style="height:1px;background:var(--border)"></div>
<div class="flex gap-4">
<label class="flex items-center gap-2 cursor-pointer text-sm" style="color:var(--text)"><input type="checkbox" checked style="accent-color:#3b82f6"> 批量操作 A</label>
<label class="flex items-center gap-2 cursor-pointer text-sm" style="color:var(--text)"><input type="checkbox" style="accent-color:#3b82f6"> 批量操作 B</label>
</div>
<div style="height:1px;background:var(--border)"></div>
<button class="btn btn-secondary btn-sm" onclick="document.getElementById('demoModal').classList.add('open')">打开删除确认 Modal</button>
</div>
</div>
</div>
</main>
</div>
<!-- Modal -->
<div class="modal-overlay" id="demoModal" onclick="if(event.target===this)this.classList.remove('open')">
<div class="modal slide-in">
<div class="flex items-center justify-between mb-5">
<h3 class="text-[15px] font-semibold" style="color:var(--text)">确认删除</h3>
<button onclick="document.getElementById('demoModal').classList.remove('open')" style="background:none;border:none;font-size:18px;cursor:pointer;color:var(--text-muted)">&times;</button>
</div>
<p class="text-sm mb-6" style="color:var(--text-secondary)">确定要删除服务器 <strong style="color:var(--text)">prod-web-01</strong> 吗?此操作不可撤销。</p>
<div class="flex justify-end gap-2">
<button class="btn btn-secondary btn-sm" onclick="document.getElementById('demoModal').classList.remove('open')">取消</button>
<button class="btn btn-danger btn-sm" onclick="document.getElementById('demoModal').classList.remove('open');showToast('success','服务器已删除')">删除</button>
</div>
</div>
</div>
<script>
function showToast(type, msg) {
const icons = {success:'<svg style="width:16px;height:16px;color:#10b981" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M8 12l2 2 4-4"/></svg>',error:'<svg style="width:16px;height:16px;color:#ef4444" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M15 9l-6 6m0-6l6 6"/></svg>',info:'<svg style="width:16px;height:16px;color:#3b82f6" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4m0-4h.01"/></svg>',warning:'<svg style="width:16px;height:16px;color:#f59e0b" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 9v4m0 4h.01"/><path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/></svg>'}
const el = document.createElement('div');
el.className = 'toast-item toast-'+type+' slide-in';
el.innerHTML = (icons[type]||icons.info)+'<span style="flex:1">'+msg+'</span><button onclick="this.parentElement.remove()" style="background:none;border:none;cursor:pointer;font-size:18px;line-height:1;color:var(--text-muted)">×</button>';
document.getElementById('toastContainer').appendChild(el);
setTimeout(()=>{el.style.opacity='0';el.style.transition='opacity .2s';setTimeout(()=>el.remove(),200)}, 3000);
}
</script>
</body>
</html>