// 宇森官网 · 公开页数据打通(图鉴/规则/论坛/公告 从 Odoo 取数)
// 依赖 assets/game-api.js
// ---------- 规则 ----------
async function loadRules(){
const el = document.querySelector('#rules .rules-scroll');
if (!el) return;
try {
const d = await API.rules();
const groups = (d && d.groups) || [];
if (!groups.length){ el.innerHTML = '
暂无规则数据
'; return; }
el.innerHTML = groups.map(g => {
const rows = (g.items || []).map(it => {
const lvl = (it.level != null) ? ('Lv.' + it.level) : (g.key === 'world' ? '阶段' : '');
return `${lvl}${it.name}${it.desc || ''}
`;
}).join('');
return ``;
}).join('');
} catch (e) { /* 保留静态兜底内容 */ }
}
// ---------- 论坛 ----------
async function loadForum(){
const threadsEl = document.getElementById('threads');
const catsEl = document.querySelector('#forum .f-cats');
if (!threadsEl) return;
try {
const list = await API.threads();
if (!list || !list.length){ threadsEl.innerHTML = '暂无主题
'; return; }
// 分类筛选
const cats = [];
list.forEach(t => { if (t.category && cats.indexOf(t.category) < 0) cats.push(t.category); });
if (catsEl){
catsEl.innerHTML = `全部
` +
cats.map(c => `${c}
`).join('');
catsEl.querySelectorAll('.ch').forEach(b => b.addEventListener('click', () => {
catsEl.querySelectorAll('.ch').forEach(x => x.classList.remove('active'));
b.classList.add('active');
const c = b.dataset.fcat;
threadsEl.querySelectorAll('.th').forEach(t => { t.style.display = (c === 'all' || t.dataset.fcat === c) ? '' : 'none'; });
}));
}
threadsEl.innerHTML = list.map(t =>
`` +
(t.pinned ? `置顶` : '') +
`${t.name}` +
`${(t.replies || []).length} 回复 · ${t.author_name || '匿名'}
`
).join('');
} catch (e) { /* 保留静态兜底内容 */ }
}
// ---------- 公告(卡片 + 弹窗) ----------
function annEscapeHtml(s) {
return String(s == null ? '' : s)
.replace(/&/g, '&').replace(//g, '>')
.replace(/"/g, '"').replace(/'/g, ''');
}
function annFormatDate(d) {
// "2026-07-09" -> "2026.07.09"
return d ? String(d).replace(/-/g, '.') : '';
}
function annStripHtml(html) {
const tmp = document.createElement('div');
tmp.innerHTML = html || '';
return (tmp.textContent || '').replace(/\s+/g, ' ').trim();
}
async function loadAnnounce(){
const root = document.getElementById('announce');
if (!root) return;
root.innerHTML = '加载中…
';
let list = [];
try {
list = await API.announcements();
} catch (e) { /* 落到空态 */ }
if (!list || !list.length) {
root.innerHTML = '暂无公告
';
return;
}
root.innerHTML = list.map((n, i) => {
const pri = n.priority || 'normal';
const preview = annStripHtml(n.body_html).slice(0, 160);
return `
${annEscapeHtml(annFormatDate(n.date))}
${annEscapeHtml(n.priority_label || pri)}
${annEscapeHtml(n.name)}
${annEscapeHtml(preview)}
阅读全文 →
`;
}).join('');
root.querySelectorAll('.announce-card').forEach(card => {
const idx = +card.dataset.idx;
const open = () => openAnnounceModal(list[idx]);
card.addEventListener('click', open);
card.addEventListener('keydown', e => {
if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); open(); }
});
});
}
// ---- 弹窗(懒创建一次) ----
let _annModal = null;
let _annLastFocus = null;
function ensureAnnounceModal() {
if (_annModal) return _annModal;
_annModal = document.createElement('div');
_annModal.className = 'announce-modal';
_annModal.setAttribute('role', 'presentation');
_annModal.innerHTML = `
`;
document.body.appendChild(_annModal);
_annModal.addEventListener('click', e => {
if (e.target.closest('[data-close]')) closeAnnounceModal();
});
document.addEventListener('keydown', e => {
if (e.key === 'Escape' && _annModal.classList.contains('open')) closeAnnounceModal();
});
return _annModal;
}
function openAnnounceModal(item) {
if (!item) return;
const m = ensureAnnounceModal();
m.querySelector('[data-title]').textContent = item.name || '';
m.querySelector('[data-meta]').textContent =
[annFormatDate(item.date), item.priority_label].filter(Boolean).join(' · ');
// body_html 来自后台管理员撰写,可信;非管理员输入路径不存在,OK 直接渲染
m.querySelector('[data-body]').innerHTML = item.body_html || '(无正文)
';
_annLastFocus = document.activeElement;
m.classList.add('open');
document.body.style.overflow = 'hidden';
// 焦点移入弹窗(关闭按钮)
const closeBtn = m.querySelector('.close');
if (closeBtn) closeBtn.focus({ preventScroll: true });
}
function closeAnnounceModal() {
if (!_annModal || !_annModal.classList.contains('open')) return;
_annModal.classList.remove('open');
document.body.style.overflow = '';
if (_annLastFocus && _annLastFocus.focus) _annLastFocus.focus({ preventScroll: true });
}
document.addEventListener('DOMContentLoaded', () => {
loadRules();
loadForum();
loadAnnounce();
});