fix: complete bilingual admin UI and mobile sign in

This commit is contained in:
2026-09-15 07:14:53 +08:00
parent 6921211dcd
commit ad6d817f19
8 changed files with 15 additions and 15 deletions
+3 -3
View File
@@ -19,7 +19,7 @@
<div class="admin-sidebar-foot"><a href="/">← Open navigation</a><br><a href="/admin/logout">Sign out</a></div>
</aside>
<main class="admin-content"><div class="admin-main">
<header class="admin-topbar"><div><h1>Overview</h1><div class="admin-sub">Service directory and system status</div></div><div class="top-actions"><button class="btn" onclick="toggleTheme()">Theme</button><a class="btn" href="/">Open site</a></div></header>
<header class="admin-topbar"><div><h1>Overview</h1><div class="admin-sub">Service directory and system status</div></div><div class="top-actions"><button class="icon-button" onclick="toggleTheme()" title="Toggle theme" aria-label="Toggle theme">◐</button><a class="btn" href="/">Open site</a></div></header>
<section class="section"><div class="section-title">System status</div>
<div class="metric-strip"><div class="metric"><div class="metric-value">{{ .service_count }}</div><div class="metric-label">Services</div></div><div class="metric"><div class="metric-value">{{ .category_count }}</div><div class="metric-label">Categories</div></div><div class="metric"><div class="metric-value green">{{ .online_count }}</div><div class="metric-label">Online</div></div><div class="metric"><div class="metric-value red">{{ .offline_count }}</div><div class="metric-label">Offline</div></div></div></section>
<section class="section"><div class="section-title">Quick actions</div><div class="panel action-list">
@@ -50,9 +50,9 @@ async function regenMCPToken(){if(!confirm(t('Regenerate token? The old token wi
async function loadCloudBackups(){const el=backupList;try{const r=await fetch('/admin/api/backup/list'),d=await r.json();if(!d.files?.length){el.textContent='No cloud backups';return}el.innerHTML=d.files.map(f=>`<div style="padding:8px 0;border-bottom:1px solid var(--line);display:flex;justify-content:space-between;gap:10px"><span>${f.name}<small style="display:block;color:var(--faint)">${formatTime(f.mod_time)} · ${formatSize(f.size)}</small></span><span><button class="btn" onclick="restoreBackup('${encodeURIComponent(f.name)}')">Restore</button> <button class="btn btn-danger" onclick="deleteBackup('${encodeURIComponent(f.name)}')">Delete</button></span></div>`).join('')}catch(e){el.textContent='Unable to load backups'}}
function formatSize(b){if(!b)return'';if(b<1024)return b+' B';if(b<1048576)return(b/1024).toFixed(1)+' KB';return(b/1048576).toFixed(1)+' MB'}
function formatTime(s){if(!s)return'';const d=new Date(s);return isNaN(d)?s:d.toLocaleString('zh-CN')}
async function saveSettings(){const data={language:cfg_language.value,site_title:cfg_site_title.value,webdav_url:cfg_url.value,webdav_user:cfg_user.value,webdav_password:cfg_pass.value,auto_backup:cfg_auto_backup.checked?'true':'false'};const r=await fetch('/admin/api/settings',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(data)}),d=await r.json();alert(d.message||t('Saved'));if(d.success){location.reload()}}
async function saveSettings(){const data={language:cfg_language.value,site_title:cfg_site_title.value,webdav_url:cfg_url.value,webdav_user:cfg_user.value,webdav_password:cfg_pass.value,auto_backup:cfg_auto_backup.checked?'true':'false'};const r=await fetch('/admin/api/settings',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(data)}),d=await r.json();alert(d.success?t('Saved'):(d.message||t('Failed')));if(d.success){location.reload()}}
async function runBackup(){if(!confirm(t('Run cloud backup now?')))return;const r=await fetch('/admin/api/backup/webdav',{method:'POST'}),d=await r.json();alert(d.message||t('Backup complete'))}
async function restoreBackup(name){name=decodeURIComponent(name);if(!confirm(t('Restore ')+name+t('? Current database will be backed up first.')))return;const r=await fetch('/admin/api/backup/restore?name='+encodeURIComponent(name),{method:'POST'}),d=await r.json();alert(d.message||t('Done'));if(d.success)location.reload()}
async function deleteBackup(name){name=decodeURIComponent(name);if(!confirm(t('Delete ')+name+t('?')))return;const r=await fetch('/admin/api/backup/delete?name='+encodeURIComponent(name),{method:'DELETE'}),d=await r.json();if(d.success)loadCloudBackups();else alert(d.message)}
document.addEventListener('keydown',e=>{if(e.key==='Escape')closeSettings()});
</script><script src="/static/js/i18n.js"></script></body></html>
</script><script src="/static/js/i18n.js?v=2"></script></body></html>