feat: match settings card width to ops map and add backup download

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-08-02 09:23:08 +08:00
parent 667cee33fa
commit 352faf404c
6 changed files with 105 additions and 11 deletions
+34 -4
View File
@@ -41,7 +41,9 @@
.hidden { display: none; }
pre { background: var(--panel); border: 1px solid #243041; border-radius: 10px; padding: 1rem; overflow: auto; font-size: 0.78rem; color: #b7c5d4; }
.center-page { display: flex; justify-content: center; align-items: flex-start; min-height: 50vh; padding: 1.5rem 0 3rem; }
.settings-card { width: 100%; max-width: 460px; }
.settings-card { width: 100%; }
.login-card { width: 100%; max-width: 420px; }
.settings-form { max-width: 520px; }
.settings-title { font-size: 1.15rem; font-weight: 650; margin: 0.25rem 0 0.85rem; }
.settings-hint { color: var(--muted); font-size: 0.85rem; line-height: 1.5; margin: 0 0 1rem; }
.settings-tabs { display: flex; border-bottom: 1px solid #243041; margin: -0.25rem -0.15rem 1.1rem; }
@@ -76,7 +78,7 @@
<main>
<section id="page-login" class="hidden">
<div class="center-page">
<div class="tile settings-card">
<div class="tile login-card">
<div class="settings-title">登录</div>
<label class="field"><span class="label">用户名</span>
<input id="loginUser" class="field-input" value="admin" autocomplete="username" />
@@ -141,7 +143,7 @@
<button type="button" id="tabAccount" class="active" data-tab="account">账户密码</button>
<button type="button" id="tabBackup" data-tab="backup">数据备份与恢复</button>
</div>
<div id="panelAccount">
<div id="panelAccount" class="settings-form">
<form id="settingsForm">
<div class="settings-title">账户密码</div>
<p id="settingsAuthHint" class="settings-hint"></p>
@@ -162,7 +164,7 @@
<button type="submit" class="btn-primary" id="settingsSaveBtn">保存账号</button>
</form>
</div>
<div id="panelBackup" class="hidden">
<div id="panelBackup" class="settings-form hidden">
<div class="settings-title">数据备份与恢复</div>
<p class="settings-hint">备份目录:<span id="backupDir">/root/market_intel_backups</span><br/>列表仅显示最近 5 条。</p>
<p id="backupDirErr" class="form-err hidden"></p>
@@ -187,6 +189,7 @@
<div class="btn-row">
<button type="button" class="btn-primary" id="autoSaveBtn">保存自动备份</button>
<button type="button" class="btn-primary" id="backupBtn">立即备份</button>
<button type="button" class="btn-primary" id="downloadBtn">下载备份</button>
<button type="button" class="btn-danger" id="restoreBtn">恢复选中备份</button>
</div>
</div>
@@ -340,6 +343,7 @@
document.getElementById("autoInterval").disabled = noAuth || !info.auto_enabled;
document.getElementById("autoSaveBtn").disabled = noAuth;
document.getElementById("backupBtn").disabled = noAuth || noWrite;
document.getElementById("downloadBtn").disabled = noAuth;
document.getElementById("restoreBtn").disabled = noAuth || noWrite;
}
document.getElementById("autoEnabled").addEventListener("change", () => {
@@ -451,6 +455,32 @@
}
});
document.getElementById("downloadBtn").addEventListener("click", async () => {
const name = document.getElementById("backupSelect").value;
document.getElementById("backupErr").textContent = "";
document.getElementById("backupOk").textContent = "";
if (!name) { document.getElementById("backupErr").textContent = "请选择要下载的备份"; return; }
try {
const r = await apiFetch("/api/settings/backup/download/" + encodeURIComponent(name));
if (!r.ok) {
const d = await r.json().catch(() => ({}));
throw new Error(d.detail || "download failed");
}
const blob = await r.blob();
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = name;
document.body.appendChild(a);
a.click();
a.remove();
URL.revokeObjectURL(url);
document.getElementById("backupOk").textContent = "已开始下载:" + name;
} catch (err) {
document.getElementById("backupErr").textContent = String(err.message || err);
}
});
document.getElementById("restoreBtn").addEventListener("click", async () => {
const pwd = document.getElementById("backupPass").value;
const name = document.getElementById("backupSelect").value;