Compare commits

..
5 Commits
Author SHA1 Message Date
cppla 1ce8d9c337 记住排序
CI / validate (push) Waiting to run
2026-07-23 10:16:00 +08:00
cppla 6f782e1940 fix #288 2026-07-22 18:56:54 +08:00
cppla 5295ac0c58 fix a light 2026-07-20 11:21:35 +08:00
cppla d923d67811 let load font weight inherit
CI / validate (push) Has been cancelled
2026-07-10 20:41:00 +08:00
cppla 0ce75874a3 fix inconsistent load font weight 2026-07-10 20:34:34 +08:00
3 changed files with 46 additions and 11 deletions
+5 -3
View File
@@ -1,7 +1,9 @@
:root{color-scheme:dark;--bg:#0d1117;--bg-alt:#171d27;--surface:#1d2530;--surface-soft:#151b24;--border:#334155;--text:#f1f5f9;--text-dim:#a7b4c8;--accent:#3b82f6;--accent-glow:#7db5ff;--danger:#f05252;--warn:#fbbf24;--ok:#10b981;--radius:10px;--radius-sm:4px;--shadow:0 8px 22px -10px rgba(0,0,0,.75);--font:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,'Noto Sans SC',sans-serif;--trans:.25s cubic-bezier(.4,0,.2,1);--logo-start:#3b82f6;--logo-end:#2563eb;--logo-accent-grad-start:#8bc1ff;--logo-accent-grad-end:#d4e8ff}
body.light{color-scheme:light;--bg:#f6f7f9;--bg-alt:#ffffff;--surface:#ffffff;--surface-soft:#f8fafc;--border:#e2e8f0;--text:#1e293b;--text-dim:#64748b;--accent:#2563eb;--accent-glow:#3b82f6;--shadow:0 4px 20px -4px rgba(0,0,0,.08);--logo-start:#2563eb;--logo-end:#1d4ed8;--logo-accent-grad-start:#1d4ed8;--logo-accent-grad-end:#60a5fa}
html.light,body.light{color-scheme:light;--bg:#f6f7f9;--bg-alt:#ffffff;--surface:#ffffff;--surface-soft:#f8fafc;--border:#e2e8f0;--text:#1e293b;--text-dim:#64748b;--accent:#2563eb;--accent-glow:#3b82f6;--shadow:0 4px 20px -4px rgba(0,0,0,.08);--logo-start:#2563eb;--logo-end:#1d4ed8;--logo-accent-grad-start:#1d4ed8;--logo-accent-grad-end:#60a5fa}
*{box-sizing:border-box}
html,body{height:100%;margin:0;padding:0;font-family:var(--font);background:var(--bg);color:var(--text);-webkit-font-smoothing:antialiased}
html,body{margin:0;padding:0;font-family:var(--font);background:var(--bg);color:var(--text);-webkit-font-smoothing:antialiased}
html{min-height:100%}
body{min-height:100vh}
html.light{background:var(--bg);color-scheme:light}
body,button{font-size:14px;line-height:1.35}
a{color:var(--accent);text-decoration:none}
@@ -103,7 +105,7 @@ body.light .virt-pill{background:color-mix(in srgb,var(--virt) 12%,#fff);color:c
.node-name{display:inline-block;max-width:160px;overflow:hidden;text-overflow:ellipsis;vertical-align:middle;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:13px;font-weight:750;letter-spacing:.15px;color:var(--text)}
.row-server[data-online="0"] .node-name{color:color-mix(in srgb,var(--text) 72%,var(--text-dim));font-weight:650}
.row-server:hover .node-name{color:var(--accent)}
.load-with-cores{display:inline-flex;align-items:flex-start;gap:.28rem;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-weight:700;line-height:1;white-space:nowrap}
.load-with-cores{display:inline-flex;align-items:flex-start;gap:.28rem;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;line-height:1;white-space:nowrap}
.load-value{display:inline-block;min-width:2.55rem}
.load-core-bubble{display:inline-flex;align-items:center;justify-content:center;height:14px;min-width:24px;padding:0 5px;margin-top:-6px;border-radius:999px;border:1px solid color-mix(in srgb,var(--accent) 34%,var(--border));background:color-mix(in srgb,var(--accent) 13%,var(--bg-alt));color:color-mix(in srgb,var(--accent) 74%,#fff);font-size:9px;font-weight:800;line-height:1;letter-spacing:0;box-shadow:0 1px 3px rgba(0,0,0,.22)}
body.light .load-core-bubble{background:color-mix(in srgb,var(--accent) 8%,#fff);color:color-mix(in srgb,var(--accent) 78%,#111827);box-shadow:0 1px 2px rgba(15,23,42,.08)}
+4 -3
View File
@@ -6,7 +6,7 @@
<meta name="description" content="云监控,ServerStatus中文版,ServerStatus,ServerStatus cppla" />
<title>云监控</title>
<link rel="icon" type="image/svg+xml" href="favicon.svg" />
<link rel="stylesheet" href="css/app.css?v=20260710-1" />
<link rel="stylesheet" href="css/app.css?v=20260720-1" />
</head>
<body>
<header class="topbar">
@@ -55,6 +55,7 @@
<label class="select-field">
<span>排序</span>
<select id="sortSelect">
<option value="config">配置顺序</option>
<option value="name">节点</option>
<option value="status">在线状态</option>
<option value="load">负载</option>
@@ -65,7 +66,7 @@
<option value="loss">丢包</option>
</select>
</label>
<button id="sortDirection" class="icon-text" title="切换排序方向"></button>
<button id="sortDirection" class="icon-text" title="切换排序方向"></button>
</section>
<section id="panel-servers" class="panel active" aria-labelledby="主机">
@@ -200,6 +201,6 @@
<a href="https://github.com/cppla/ServerStatus" target="_blank" rel="noopener">ServerStatus中文版</a>
</footer>
<script src="js/app.js?v=20260710-1" defer></script>
<script src="js/app.js?v=20260723-1" defer></script>
</body>
</html>
+37 -5
View File
@@ -1,3 +1,16 @@
const SORT_PREFERENCE_KEY = 'serverstatusSortPreference';
const SORT_FIELDS = new Set(['config','name','status','load','cpu','memory','hdd','traffic','loss']);
function loadSortPreference(){
const fallback = { sort: 'config', dir: 'asc' };
try{
const saved = JSON.parse(localStorage.getItem(SORT_PREFERENCE_KEY) || 'null');
if(saved && SORT_FIELDS.has(saved.sort) && (saved.dir === 'asc' || saved.dir === 'desc')) return saved;
}catch(_err){}
return fallback;
}
const initialSortPreference = loadSortPreference();
const S = {
updated: 0,
servers: [],
@@ -9,7 +22,7 @@ const S = {
layoutCompact: null,
osOptionsSignature: '',
suppressStatsReloadUntil: 0,
filters: { query: '', status: 'all', os: 'all', sort: 'name', dir: 'desc' },
filters: { query: '', status: 'all', os: 'all', ...initialSortPreference },
admin: {
token: localStorage.getItem('serverstatusAdminToken') || '',
enabled: false,
@@ -197,6 +210,7 @@ async function fetchData(){
const base = [server.name || '-', server.location || '-', server.type || '-'].join('|');
const key = `${base}#${index + 1}`;
server._key = key;
server._order = index;
if(!S.hist[key]) S.hist[key] = { cu: [], ct: [], cm: [] };
if(!S.loadHist[key]) S.loadHist[key] = { l1: [], l5: [], l15: [] };
pushHistory(S.hist[key].cu, server.time_10010);
@@ -239,6 +253,7 @@ function visibleServers(){
const dir = S.filters.dir === 'asc' ? 1 : -1;
const ma = metrics(a), mb = metrics(b);
const value = (server, m) => ({
config: num(server._order),
name: String(server.name || ''),
type: String(server.type || ''),
location: String(server.location || ''),
@@ -732,7 +747,17 @@ function bindTheme(){
if(S.openDetailKey) refreshDetail();
});
}
function syncSortControls(){
$('sortSelect').value = S.filters.sort;
$('sortDirection').textContent = S.filters.dir === 'desc' ? '降序' : '升序';
}
function saveSortPreference(){
try{
localStorage.setItem(SORT_PREFERENCE_KEY, JSON.stringify({ sort: S.filters.sort, dir: S.filters.dir }));
}catch(_err){}
}
function bindFilters(){
syncSortControls();
$('serverSearch').addEventListener('input', e => { S.filters.query = e.target.value; scheduleServersRender(); });
$('statusFilter').addEventListener('click', e => {
if(e.target.tagName !== 'BUTTON') return;
@@ -742,17 +767,24 @@ function bindFilters(){
});
$('osFilter').addEventListener('change', e => { S.filters.os = e.target.value; renderServersViewNow(); });
$('osFilter').addEventListener('blur', renderOsOptions);
$('sortSelect').addEventListener('change', e => { S.filters.sort = e.target.value; renderServersViewNow(); });
$('sortSelect').addEventListener('change', e => {
S.filters.sort = e.target.value;
if(S.filters.sort === 'config') S.filters.dir = 'asc';
syncSortControls();
saveSortPreference();
renderServersViewNow();
});
$('sortDirection').addEventListener('click', () => {
S.filters.dir = S.filters.dir === 'desc' ? 'asc' : 'desc';
$('sortDirection').textContent = S.filters.dir === 'desc' ? '降序' : '升序';
syncSortControls();
saveSortPreference();
renderServersViewNow();
});
document.querySelectorAll('#serversTable th[data-sort]').forEach(th => th.addEventListener('click', () => {
if(S.filters.sort === th.dataset.sort) S.filters.dir = S.filters.dir === 'desc' ? 'asc' : 'desc';
else S.filters.sort = th.dataset.sort;
$('sortSelect').value = S.filters.sort;
$('sortDirection').textContent = S.filters.dir === 'desc' ? '降序' : '升序';
syncSortControls();
saveSortPreference();
renderServersViewNow();
}));
}