import { api } from "../api.js"; import { centerActiveChips, emptyState, sectionHeader } from "../components.js"; import { lineChart, themeColor } from "../charts.js"; import { escapeAttr, escapeHtml, formatCount, formatDelta, formatNumber, } from "../format.js"; import { replaceQuery } from "../router.js"; const RANGES = [["7", "7 天"], ["30", "30 天"], ["90", "90 天"], ["3650", "全部"]]; export async function renderFans(root, { query }) { const days = query.get("days") || "90"; const mid = query.get("mid"); const start = query.get("start") || ""; const end = query.get("end") || ""; const customRange = Boolean(start || end); const fans = await api("/api/fans", { days: customRange ? null : days, start, end, mid, }); const options = fans.options || []; root.innerHTML = `

粉丝

${escapeHtml(fans.name || "粉丝追踪")}

${options.length > 1 ? `` : ""}
${!fans.available ? emptyState("暂无粉丝数据", "每日快照完成后会开始记录粉丝数。") : `
当前粉丝 ${formatCount(fans.follower)} ${escapeHtml(fans.stat_date || "—")} · 首次记录 ${escapeHtml(fans.first_date || "—")}
日增${fans.delta_1d === null ? "—" : formatDelta(fans.delta_1d)}
7 日净增${fans.delta_7d === null ? "—" : formatDelta(fans.delta_7d)}
累计增长(${escapeHtml(fans.first_date || "—")} 以来)${formatDelta(fans.delta_cum)}
${sectionHeader("粉丝曲线")}
${RANGES.map(([value, label]) => ``).join("")}
至 ${customRange ? `` : ""}

只绘制粉丝数,不叠加 reaction 视频数据;缺失日断开,不插值。 ${fans.mode === "monthly" ? "当前范围超过一年,按每月粉丝峰值所在日期绘制。" : `当前按日绘制(${escapeHtml(fans.range_start || "—")} ~ ${escapeHtml(fans.range_end || "—")})。`}

${sectionHeader("最近记录")}

默认展示最近 ${fans.recent_days || 14} 天;更早的数据只在曲线中显示。

${(fans.recent_points || []).slice().reverse().map((point) => ` `).join("")}
日期粉丝较上一成功日
${escapeHtml(point.stat_date)} ${point.ok ? formatNumber(point.follower) : "—"} ${point.delta === null || point.delta === undefined ? "—" : formatDelta(point.delta)}
`}
`; root.querySelectorAll("[data-days]").forEach((button) => button.addEventListener("click", () => { replaceQuery({ days: button.dataset.days, start: null, end: null }); })); root.querySelector("#fan-mid")?.addEventListener("change", (event) => { replaceQuery({ mid: event.target.value }); }); root.querySelector("#fan-range")?.addEventListener("submit", (event) => { event.preventDefault(); const form = new FormData(event.target); const nextStart = String(form.get("start") || "").trim(); const nextEnd = String(form.get("end") || "").trim(); if (nextStart && nextEnd && nextStart > nextEnd) { window.alert("开始日期不能晚于结束日期"); return; } replaceQuery({ start: nextStart || null, end: nextEnd || null, days: null }); }); root.querySelector("#fan-range-clear")?.addEventListener("click", () => { replaceQuery({ start: null, end: null, days: null }); }); centerActiveChips(root); const chartElement = root.querySelector("#fans-chart"); if (chartElement) { lineChart(chartElement, { dates: fans.dates || [], values: fans.series || [], name: "粉丝", color: themeColor("accent"), scale: true, }); } }