启动器:游戏更新拆分为当前/历史双子页,新增关于我们页

- 游戏更新页改为「当前更新 / 历史更新」两个标签页,历史页显示条数
- 更新日志合并仓库 Release 与本地 更新日志.txt,按日期去重、仓库优先;断网时仅用本地记录
- 主导航新增「关于我们」,内容与游戏内控制台关于页一致(四行文案 + 赞助/公众号二维码)
- 新增 /qr/<name> 白名单路由读取启动器目录 qr\ 下图片,防止目录穿越
- 二维码支持点击放大(灯箱),图片缺失时显示占位而不是破图
- 打包 spec 补入 qr 三张图,静态响应统一 Cache-Control: no-store
This commit is contained in:
hanfuit committed 2026-11-05 16:05:00 +08:00
1 parent 1dd3775510
commit 6a4b7e58d3
8 files changed
+311 -36

No files matched your search

+82 -30
View File
@@ -1,13 +1,13 @@
/* 90轮回启动器 · 单窗口页面与工具整合 */
(function () {
const $ = (id) => document.getElementById(id);
const pages = ['start','changelog','gm','loadout','gamesettings'];
const pages = ['start','changelog','gm','loadout','gamesettings','about'];
let shellGmInit = false;
let loConfig = null, loPage = 0, loScheme = -1;
let loCooldownUntil = 0, loCooldownCharacter = '', loCooldownTimer = null;
let hotkeyCapture = false;
const crumb = {start:'开始 / 本地服务端',changelog:'游戏更新',gm:'GM工具',loadout:'工具 / 一键换装',gamesettings:'工具 / 游戏设置'};
const crumb = {start:'开始 / 本地服务端',changelog:'游戏更新',gm:'GM工具',loadout:'工具 / 一键换装',gamesettings:'工具 / 游戏设置',about:'关于我们'};
function shellGo(page) {
if (!pages.includes(page)) page = 'start';
@@ -258,43 +258,60 @@
}
}
// 更新日志页面:拉取所有 Release 的 changelog
async function loadChangelog() {
const box = $('changelogBody');
if (!box) return;
box.innerHTML = '<div class="empty-box">正在获取更新日志…</div>';
// 更新日志:当前更新 = 最新一条;历史更新 = 其余。优先 Gitea,断网退回本地 更新日志.txt
function updDate(rel) {
return rel.published_at ? rel.published_at.replace('T', ' ').slice(0, 16) : '';
}
function updCard(rel, featured) {
const badge = rel.prerelease ? '<span class="gm-tag gm-tag-gray">预发布</span>' : '';
const body = (rel.body || '').replace(/\r?\n/g, '<br>');
const assets = (rel.assets || []).map(a =>
`<a class="changelog-asset" href="${escapeHtml(a.download_url)}" target="_blank">⬇ ${escapeHtml(a.name)} (${fmt(a.size)} B)</a>`
).join('');
const when = updDate(rel);
return `<div class="changelog-item${featured ? ' featured' : ''}">
<div class="changelog-head">
<span class="changelog-tag">${escapeHtml(rel.tag || rel.name || '')}</span>
<span class="changelog-name">${escapeHtml(rel.name || '')}</span>
${badge}
<span class="changelog-time">${escapeHtml(when)}</span>
</div>
<div class="changelog-body">${body || '<span class="muted">(这次没有填写更新说明)</span>'}</div>
${assets ? '<div class="changelog-assets">' + assets + '</div>' : ''}
${featured ? `<a class="changelog-link" href="${escapeHtml(rel.html_url)}" target="_blank">在浏览器中查看 ↗</a>` : ''}
</div>`;
}
async function loadChangelog(force) {
const latestBox = $('updLatest'), historyBox = $('updHistory');
if (!latestBox || !historyBox) return;
latestBox.innerHTML = '<div class="empty-box">正在获取更新日志…</div>';
historyBox.innerHTML = '';
try {
const r = await api('/api/update/changelog');
const r = await api('/api/update/changelog' + (force ? '?force=1' : ''));
if (!r.ok) {
box.innerHTML = '<div class="empty-box">获取失败:' + escapeHtml(r.error || '未知错误') + '</div>';
latestBox.innerHTML = '<div class="empty-box">获取失败:' + escapeHtml(r.error || '未知错误') + '</div>';
if ($('updHistoryCount')) $('updHistoryCount').textContent = '';
return;
}
const rels = r.releases || [];
if (!rels.length) {
box.innerHTML = '<div class="empty-box">暂无发布记录。<br>在 Gitea 仓库「发布 Release」并填写更新说明后,这里会自动显示。</div>';
latestBox.innerHTML = '<div class="empty-box">暂无更新记录。<br>在仓库发布 Release,或把「更新日志.txt」放在启动器目录,这里就会显示。</div>';
if ($('updHistoryCount')) $('updHistoryCount').textContent = '';
return;
}
box.innerHTML = rels.map(rel => {
const badge = rel.prerelease ? '<span class="gm-tag gm-tag-gray">预发布</span>' : '';
const body = (rel.body || '').replace(/\r?\n/g, '<br>');
const assets = (rel.assets || []).map(a =>
`<a class="changelog-asset" href="${escapeHtml(a.download_url)}" target="_blank">⬇ ${escapeHtml(a.name)} (${fmt(a.size)} B)</a>`
).join('');
const when = rel.published_at ? rel.published_at.replace('T', ' ').slice(0, 16) : '';
return `<div class="changelog-item">
<div class="changelog-head">
<span class="changelog-tag">${escapeHtml(rel.tag || rel.name || '')}</span>
<span class="changelog-name">${escapeHtml(rel.name || '')}</span>
${badge}
<span class="changelog-time">${escapeHtml(when)}</span>
</div>
<div class="changelog-body">${body}</div>
${assets ? '<div class="changelog-assets">' + assets + '</div>' : ''}
<a class="changelog-link" href="${escapeHtml(rel.html_url)}" target="_blank">在浏览器中查看 ↗</a>
</div>`;
}).join('');
const source = r.source === 'local'
? '<div class="upd-source">未连接更新仓库,以下内容读取自本地「更新日志.txt」。</div>'
: '';
latestBox.innerHTML = source + updCard(rels[0], true);
const rest = rels.slice(1);
historyBox.innerHTML = rest.length
? rest.map(rel => updCard(rel, false)).join('')
: '<div class="empty-box">还没有更早的更新记录。</div>';
if ($('updHistoryCount')) $('updHistoryCount').textContent = rest.length ? '(' + rest.length + ')' : '';
} catch (e) {
box.innerHTML = '<div class="empty-box">获取失败:' + escapeHtml(e && e.message ? e.message : e) + '</div>';
latestBox.innerHTML = '<div class="empty-box">获取失败:' + escapeHtml(e && e.message ? e.message : e) + '</div>';
}
}
@@ -745,6 +762,41 @@
});
});
// ---------------- 游戏更新:当前 / 历史 两个子页 ----------------
(function bindUpdTabs() {
const tabs = document.querySelectorAll('#updTabs .st-tab');
tabs.forEach(btn => {
btn.addEventListener('click', () => {
const which = btn.dataset.upd;
tabs.forEach(b => b.classList.toggle('active', b === btn));
$('updLatestPane').style.display = which === 'latest' ? '' : 'none';
$('updHistoryPane').style.display = which === 'history' ? '' : 'none';
});
});
const refresh = $('updRefresh');
if (refresh) refresh.addEventListener('click', () => loadChangelog(true));
})();
// ---------------- 关于我们:二维码放大 ----------------
(function bindQrLightbox() {
const box = $('qrLightbox'), img = $('qrLightboxImg'), tip = $('qrLightboxTip');
if (!box || !img) return;
document.querySelectorAll('#page-about .about-qr').forEach(fig => {
fig.addEventListener('click', () => {
const src = fig.querySelector('img');
if (!src) return;
img.src = src.getAttribute('src');
img.alt = src.getAttribute('alt') || '';
tip.textContent = (fig.dataset.qr || '') + ' · 点击任意处关闭';
box.classList.remove('hidden');
});
});
box.addEventListener('click', () => box.classList.add('hidden'));
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') box.classList.add('hidden');
});
})();
// 兼容旧 GM 页面里的返回
window.shellGo = shellGo;
loadStart();