fix merge conflicts
@@ -59,6 +59,19 @@ const API = {
|
||||
method: 'POST', credentials: _cred,
|
||||
})).json();
|
||||
},
|
||||
async achievements() {
|
||||
return (await fetch(_url('/game/api/achievements'), { credentials: _cred })).json();
|
||||
},
|
||||
async mails() {
|
||||
return (await fetch(_url('/game/api/mails'), { credentials: _cred })).json();
|
||||
},
|
||||
async mailRead(mailId) {
|
||||
return (await fetch(_url('/game/api/mail/read'), {
|
||||
method: 'POST', credentials: _cred,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ mail_id: mailId }),
|
||||
})).json();
|
||||
},
|
||||
async codex() {
|
||||
return (await fetch(_url('/game/api/codex'), { credentials: _cred })).json();
|
||||
},
|
||||
@@ -136,4 +149,6 @@ const API = {
|
||||
async codexRaces() {
|
||||
return (await fetch(_worldUrl('/yt_world/api/codex/races'), { credentials: _wcred })).json();
|
||||
},
|
||||
// 暴露后端 base,供图片等静态资源 URL 拼接(本地跨域→8069;线上同域→'')
|
||||
base: API_CONFIG.base,
|
||||
};
|
||||
|
||||
@@ -53,6 +53,102 @@ async function loadRules(){
|
||||
} catch (e) { /* 保留静态兜底内容 */ }
|
||||
}
|
||||
|
||||
// ---------- 图鉴 ----------
|
||||
async function loadCodex(){
|
||||
const grid = document.getElementById('codexGrid');
|
||||
const catsEl = document.getElementById('codexCats');
|
||||
const subsEl = document.getElementById('codexSubs');
|
||||
const searchEl= document.getElementById('codexSearch');
|
||||
const countEl = document.getElementById('codexCount');
|
||||
if (!grid) return;
|
||||
|
||||
let data;
|
||||
try { data = await API.codex(); }
|
||||
catch (e) { return; } // 失败时保留静态兜底内容
|
||||
|
||||
const groups = (data && data.groups) || [];
|
||||
if (!groups.length){
|
||||
grid.innerHTML = '<div style="grid-column:1/-1;text-align:center;color:var(--text-dim);padding:60px 0">暂无图鉴数据</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
// 选中状态
|
||||
let curGroup = groups[0].key;
|
||||
let curSub = 'all';
|
||||
let curQ = '';
|
||||
|
||||
function renderCats(){
|
||||
if (!catsEl) return;
|
||||
catsEl.innerHTML = groups.map(g =>
|
||||
`<button class="${g.key === curGroup ? 'active' : ''}" data-gk="${g.key}">${g.label}</button>`
|
||||
).join('');
|
||||
catsEl.querySelectorAll('button').forEach(b => b.addEventListener('click', () => {
|
||||
curGroup = b.dataset.gk; curSub = 'all'; curQ = '';
|
||||
if (searchEl) searchEl.value = '';
|
||||
renderCats(); renderSubs(); renderGrid();
|
||||
}));
|
||||
}
|
||||
|
||||
function renderSubs(){
|
||||
if (!subsEl) return;
|
||||
const g = groups.find(x => x.key === curGroup);
|
||||
const subs = (g && g.subs) || [];
|
||||
subsEl.innerHTML = `<button class="${curSub === 'all' ? 'active' : ''}" data-sn="all">全部</button>` +
|
||||
subs.map(s => `<button class="${curSub === s.name ? 'active' : ''}" data-sn="${s.name}">${s.name}</button>`).join('');
|
||||
subsEl.querySelectorAll('button').forEach(b => b.addEventListener('click', () => {
|
||||
curSub = b.dataset.sn; renderSubs(); renderGrid();
|
||||
}));
|
||||
}
|
||||
|
||||
function renderGrid(){
|
||||
const g = groups.find(x => x.key === curGroup);
|
||||
if (!g){ grid.innerHTML = ''; return; }
|
||||
// 按子分类筛选
|
||||
let items = [];
|
||||
(g.subs || []).forEach(s => {
|
||||
(s.items || []).forEach(it => {
|
||||
if (curSub !== 'all' && it.sub !== curSub) return;
|
||||
items.push(it);
|
||||
});
|
||||
});
|
||||
// 搜索过滤
|
||||
if (curQ){
|
||||
const q = curQ.toLowerCase();
|
||||
items = items.filter(it =>
|
||||
(it.name || '').toLowerCase().includes(q) ||
|
||||
(it.desc || '').replace(/<[^>]+>/g, '').toLowerCase().includes(q)
|
||||
);
|
||||
}
|
||||
if (countEl) countEl.textContent = items.length + ' 项';
|
||||
if (!items.length){
|
||||
grid.innerHTML = '<div style="grid-column:1/-1;text-align:center;color:var(--text-dim);padding:60px 0">未找到匹配项</div>';
|
||||
return;
|
||||
}
|
||||
const base = API.base || '';
|
||||
grid.innerHTML = items.map(it => {
|
||||
const thumb = it.image
|
||||
? `<div class="thumb" style="background:url('${base}${it.image}') center/cover"></div>`
|
||||
: `<div class="thumb"></div>`;
|
||||
return `<div class="cx">
|
||||
${thumb}
|
||||
<div class="body">
|
||||
<div class="nm">${it.name || ''}</div>
|
||||
<div class="desc">${it.desc || ''}</div>
|
||||
<div class="meta"><span class="sub">${it.sub || ''}</span><span class="tier">${it.tier || ''}</span></div>
|
||||
</div>
|
||||
</div>`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
if (searchEl){
|
||||
searchEl.addEventListener('input', () => { curQ = searchEl.value.trim(); renderGrid(); });
|
||||
}
|
||||
|
||||
renderCats();
|
||||
renderSubs();
|
||||
renderGrid();
|
||||
}
|
||||
|
||||
// ---------- 论坛 ----------
|
||||
async function loadForum(){
|
||||
const threadsEl = document.getElementById('threads');
|
||||
@@ -155,6 +251,7 @@ async function loadStory(){
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
loadPages();
|
||||
loadRules();
|
||||
loadCodex();
|
||||
loadForum();
|
||||
loadAnnounce();
|
||||
loadStory();
|
||||
|
||||
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 1.7 MiB |
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 1.5 MiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 1.5 MiB |
|
After Width: | Height: | Size: 1.5 MiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 1.5 MiB |