274 lines
8.3 KiB
HTML
274 lines
8.3 KiB
HTML
<!doctype html>
|
|
<html lang="zh-CN">
|
|
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta http-equiv="Content-Security-Policy" content="default-src 'self' 'unsafe-inline' data:;">
|
|
<style>
|
|
:root {
|
|
color-scheme: dark light;
|
|
--bg: #0b1220;
|
|
--card: #111827;
|
|
--fg: #e6eaf2;
|
|
--muted: #9ca3af;
|
|
--accent1: #60a5fa;
|
|
--accent2: #34d399;
|
|
--error: #fca5a5;
|
|
--ok: #86efac;
|
|
}
|
|
|
|
html,
|
|
body {
|
|
height: 100%;
|
|
}
|
|
|
|
body {
|
|
margin: 0;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial;
|
|
background: radial-gradient(1200px 600px at 10% 0%, rgba(96, 165, 250, .2), transparent 70%),
|
|
radial-gradient(1200px 600px at 100% 120%, rgba(52, 211, 153, .15), transparent 70%),
|
|
var(--bg);
|
|
color: var(--fg);
|
|
}
|
|
|
|
.card {
|
|
width: min(540px, 92vw);
|
|
background: var(--card);
|
|
border-radius: 18px;
|
|
padding: 32px 26px;
|
|
box-shadow: 0 20px 48px rgba(0, 0, 0, .35);
|
|
text-align: center;
|
|
backdrop-filter: blur(6px);
|
|
}
|
|
|
|
.spinner {
|
|
width: 52px;
|
|
height: 52px;
|
|
margin: 0 auto 18px;
|
|
border: 4px solid rgba(255, 255, 255, .15);
|
|
border-top-color: var(--accent1);
|
|
border-radius: 50%;
|
|
animation: spin 1s linear infinite;
|
|
}
|
|
|
|
@keyframes spin {
|
|
to {
|
|
transform: rotate(360deg)
|
|
}
|
|
}
|
|
|
|
h2 {
|
|
margin: 6px 0 10px;
|
|
font-weight: 700;
|
|
font-size: 20px;
|
|
background: linear-gradient(90deg, var(--accent1), var(--accent2));
|
|
-webkit-background-clip: text;
|
|
-webkit-text-fill-color: transparent;
|
|
}
|
|
|
|
p {
|
|
margin: 0 0 10px;
|
|
opacity: .9;
|
|
}
|
|
|
|
.muted {
|
|
opacity: .75;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.tips {
|
|
margin-top: 16px;
|
|
text-align: left;
|
|
font-size: 13px;
|
|
line-height: 1.6;
|
|
opacity: .9;
|
|
background: #0f172a;
|
|
border-radius: 12px;
|
|
padding: 12px 14px;
|
|
border: 1px solid rgba(255, 255, 255, .08);
|
|
}
|
|
|
|
code {
|
|
background: #0b1220;
|
|
padding: 1px 6px;
|
|
border-radius: 6px;
|
|
font-size: 12px;
|
|
}
|
|
|
|
ul {
|
|
padding-left: 18px;
|
|
margin: 6px 0 0;
|
|
}
|
|
|
|
/* 更新视图样式 */
|
|
.progress {
|
|
margin: 18px auto 10px;
|
|
width: 100%;
|
|
max-width: 440px;
|
|
height: 12px;
|
|
background: #0b1220;
|
|
border-radius: 999px;
|
|
overflow: hidden;
|
|
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .06);
|
|
}
|
|
|
|
.progress__bar {
|
|
height: 100%;
|
|
width: 0%;
|
|
background: linear-gradient(90deg, var(--accent1), var(--accent2));
|
|
transition: width .15s ease;
|
|
}
|
|
|
|
.row {
|
|
opacity: .9;
|
|
font-size: 14px;
|
|
margin-top: 6px;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
padding: 0 6px;
|
|
}
|
|
|
|
.actions {
|
|
margin-top: 14px;
|
|
}
|
|
|
|
.btn {
|
|
appearance: none;
|
|
border: 0;
|
|
padding: 9px 16px;
|
|
border-radius: 10px;
|
|
background: var(--accent1);
|
|
color: #fff;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.btn[disabled] {
|
|
opacity: .6;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
.warn {
|
|
color: var(--error);
|
|
font-size: 13px;
|
|
margin-top: 6px;
|
|
}
|
|
|
|
.ok {
|
|
color: var(--ok);
|
|
font-size: 13px;
|
|
margin-top: 6px;
|
|
}
|
|
|
|
[hidden] {
|
|
display: none !important;
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body>
|
|
<div class="card">
|
|
<!-- 等待视图 -->
|
|
<div id="wait-view">
|
|
<div class="spinner"></div>
|
|
<h2>正在检查更新…</h2>
|
|
<p class="muted">系统会自动检测可用的新版本</p>
|
|
<div class="tips">
|
|
<strong>如果长时间停留在此页面,可尝试:</strong>
|
|
<ul>
|
|
<li>检查网络是否可用</li>
|
|
<li>确认杀软/防火墙没有拦截本应用</li>
|
|
<li>重启应用后再试</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 更新视图 -->
|
|
<div id="update-view" hidden>
|
|
<h2 id="upd-title">检测到新版本,正在下载…</h2>
|
|
<div class="progress" aria-label="下载进度">
|
|
<div id="upd-bar" class="progress__bar"></div>
|
|
</div>
|
|
<div class="row">
|
|
<div id="upd-percent">0.00%</div>
|
|
<div id="upd-meta"><small>—</small></div>
|
|
</div>
|
|
<div id="upd-msg" class="ok" hidden>下载完成,准备安装</div>
|
|
<div id="upd-err" class="warn" hidden></div>
|
|
<div class="actions">
|
|
<button id="btn-install" class="btn" hidden>立即重启安装</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
(function bindUpdaterUI() {
|
|
const bridge = window.appUpdater;
|
|
if (!bridge) return;
|
|
|
|
const $ = (id) => document.getElementById(id);
|
|
const waitView = $('wait-view');
|
|
const updView = $('update-view');
|
|
const updBar = $('upd-bar');
|
|
const updPercent = $('upd-percent');
|
|
const updMeta = $('upd-meta');
|
|
const updTitle = $('upd-title');
|
|
const updMsg = $('upd-msg');
|
|
const updErr = $('upd-err');
|
|
const btnInstall = $('btn-install');
|
|
|
|
const fmtMB = b => (b / 1024 / 1024).toFixed(1) + ' MB';
|
|
const fmtSpeed = bps => (bps / 1024).toFixed(0) + ' KB/s';
|
|
|
|
const showUpdateView = (titleText) => {
|
|
if (titleText) updTitle.textContent = titleText;
|
|
waitView.hidden = true;
|
|
updView.hidden = false;
|
|
};
|
|
|
|
bridge.onAvailable((info) => {
|
|
const ver = info?.version || '';
|
|
showUpdateView(ver ? `检测到新版本 v${ver},正在下载…` : '检测到新版本,正在下载…');
|
|
});
|
|
|
|
bridge.onProgress((p) => {
|
|
if (updView.hidden) showUpdateView('正在下载更新…');
|
|
const percent = Math.max(0, Math.min(100, p?.percent || 0));
|
|
updBar.style.width = percent.toFixed(2) + '%';
|
|
updPercent.textContent = percent.toFixed(2) + '%';
|
|
const transferred = typeof p?.transferred === 'number' ? fmtMB(p.transferred) : '-';
|
|
const total = typeof p?.total === 'number' ? fmtMB(p.total) : '-';
|
|
const speed = typeof p?.bytesPerSecond === 'number' ? fmtSpeed(p.bytesPerSecond) : '-';
|
|
updMeta.innerHTML = `<small>${transferred} / ${total} · ${speed}</small>`;
|
|
updMsg.hidden = true;
|
|
updErr.hidden = true;
|
|
});
|
|
|
|
bridge.onDownloaded(() => {
|
|
updTitle.textContent = '更新下载完成';
|
|
updBar.style.width = '100%';
|
|
updPercent.textContent = '100%';
|
|
updMsg.hidden = false;
|
|
btnInstall.hidden = false;
|
|
});
|
|
|
|
bridge.onError((err) => {
|
|
updTitle.textContent = '更新出错';
|
|
updErr.hidden = false;
|
|
updErr.textContent = (err && (err.message || err)) || '发生未知错误';
|
|
btnInstall.hidden = true;
|
|
});
|
|
|
|
btnInstall.addEventListener('click', () => {
|
|
btnInstall.disabled = true;
|
|
try { bridge.quitAndInstallNow?.(); } catch (e) { }
|
|
});
|
|
})();
|
|
</script>
|
|
</body>
|
|
|
|
</html> |