Add operations overview and category usage tracking

This commit is contained in:
wuyang
2026-11-04 11:10:29 +08:00
parent 7fc9d4d843
commit dde9742f9c
5 changed files with 47 additions and 6 deletions
+17 -2
View File
@@ -106,11 +106,24 @@ function renderLedger() {
table.append(head, body); wrapper.append(table); host.append(wrapper);
}
function showView(view) {
for (const name of ['report', 'evolution', 'daily']) $(`#${name}-view`).hidden = view !== name;
for (const [selector, active] of [['#show-report', view === 'report'], ['#show-evolution', view === 'evolution'], ['#show-daily', view === 'daily']]) {
for (const name of ['overview', 'report', 'evolution', 'daily']) $(`#${name}-view`).hidden = view !== name;
for (const [selector, active] of [['#show-overview', view === 'overview'], ['#show-report', view === 'report'], ['#show-evolution', view === 'evolution'], ['#show-daily', view === 'daily']]) {
$(selector).classList.toggle('active', active); $(selector).setAttribute('aria-pressed', String(active));
}
}
async function openOverview() {
showView('overview'); history.replaceState(null, '', '/');
const host = $('#overview-content'); host.replaceChildren(el('p', '正在读取运营全景…', 'muted'));
try {
const [data, queue] = await Promise.all([api('/api/overview'), api('/api/workqueue')]); host.replaceChildren(); const strategy = data.strategy || {}, market = data.market || {};
const hero = el('section', undefined, 'report-section'); hero.append(el('h2', '方向与当前阶段'), el('p', strategy.mission || '方向待补充'), el('p', strategy.phase || '', 'muted')); const pillars = el('ul'); for (const item of strategy.pillars || []) pillars.append(el('li', item)); hero.append(pillars, el('p', strategy.cadence || '', 'muted')); host.append(hero);
const works = el('section', undefined, 'report-section'); works.append(el('h2', '已发布作品'), el('p', `采样时间:${market.observed_at ? dateTime(market.observed_at) : '暂无'};播放量不是曝光或完播率。`, 'muted'));
for (const v of market.account_videos || []) { const p = el('p'); if (v.bvid) { const a = el('a', v.title); a.href = `https://www.bilibili.com/video/${v.bvid}/`; a.target = '_blank'; a.rel = 'noopener noreferrer'; p.append(a); } else p.append(el('span', v.title)); p.append(document.createTextNode(` · 播放 ${v.views ?? '未知'} · 点赞 ${v.likes ?? '未知'} · 评论 ${v.comments ?? '未知'}`)); works.append(p); } host.append(works);
const budget = el('section', undefined, 'report-section'); budget.append(el('h2', '计划投入'), el('p', (strategy.budget || []).map(x => `${x.name} ${x.percent}%`).join(' · ')), el('p', strategy.budget_note || '', 'muted')); host.append(budget);
const work = el('section', undefined, 'report-section'); work.append(el('h2', '持续工作')); const cats = {content:'内容',engineering:'工程',research:'研究',community:'社区',management:'管理'}, status = {pending:'待继续',running:'进行中',done:'已完成',blocked:'需要处理'}, list = el('ul'); for (const t of queue.tasks || []) if (t.status !== 'done') list.append(el('li', `${cats[t.category] || '管理'} · ${t.title} · ${status[t.status] || t.status}`)); work.append(list); host.append(work);
const trends = el('section', undefined, 'report-section'); trends.append(el('h2', 'B 站公开榜单快照'), el('p', '单时点观察,基于标题和公开数据,不能代表趋势或保证效果。', 'muted')); const ul = el('ul'); for (const v of market.ranking || []) { const li = el('li'); if (v.bvid) { const a = el('a', v.title); a.href = `https://www.bilibili.com/video/${v.bvid}/`; a.target = '_blank'; a.rel = 'noopener noreferrer'; li.append(a); } else li.append(el('span', v.title)); li.append(document.createTextNode(` · ${v.owner} · ${v.views ?? '未知'} 播放`)); ul.append(li); } trends.append(ul); host.append(trends);
} catch (error) { host.replaceChildren(el('p', error.message, 'error')); }
}
async function openDaily() {
showView('daily');
const host = $('#daily-content'); host.replaceChildren(el('p', '正在读取每日记录…', 'muted'));
@@ -190,7 +203,9 @@ async function loadDashboard() {
if (selected && dashboard.reports.some(r => r.id === selected)) await openReport(selected); else renderReport(dashboard.latest);
if (initialView === 'evolution') await openEvolution();
if (initialView === 'daily') await openDaily();
if (!selected && !initialView) await openOverview();
}
$('#show-overview').addEventListener('click', openOverview);
$('#show-evolution').addEventListener('click', openEvolution);
$('#show-daily').addEventListener('click', openDaily);
$('#show-report').addEventListener('click', () => { showView('report'); history.replaceState(null, '', currentReportId ? `/?report=${encodeURIComponent(currentReportId)}` : '/'); });