import { coverUrl, escapeAttr, escapeHtml, formatCount, formatDate, formatDelta, formatDuration, formatPercent, metricLabel, statusLabel, } from "./format.js"; export function cover(item, className = "thumb") { const url = coverUrl(item.cover); const title = escapeAttr(item.title || "视频封面"); if (!url) { return ``; } return `
${title}
`; } export function rankBadge(rank, className = "rank-badge") { if (!rank) return ""; const top = rank <= 3 ? ` rank-${rank}` : ""; return `${rank}`; } export function avatar(item, className = "avatar") { const url = coverUrl(item?.up_face || item?.avatar); const name = escapeHtml(item?.up_name || "UP"); if (!url) { const initial = String(item?.up_name || "U").trim().slice(0, 1).toUpperCase() || "U"; return ``; } return `
${name}
`; } function growthBaselineTag(growth) { if (growth?.baseline === "zero") return "今日"; if (growth?.baseline === "open") return "入库后"; return ""; } export function videoCard(item, { rank = null, primary = "view", primaryLabel = "", primaryText = null, showStatus = false, } = {}) { const metrics = item.metrics || {}; const hasMetric = Object.prototype.hasOwnProperty.call(metrics, primary); const raw = primaryText !== null ? primaryText : (hasMetric ? metrics[primary] : item[primary]); // 指标排序直接返回原始计数,统一走 formatCount,避免同一列混排 “36375” 与 “1.1万”。 const primaryValue = typeof raw === "number" ? formatCount(raw) : raw; const label = primaryLabel || metricLabel(primary); const growth = item.growth; const delta = growth?.available ? growth.delta : null; const title = escapeHtml(item.title || "(无标题)"); const up = escapeHtml(item.up_name || "未知 UP"); const upMid = item.up_mid; // 卡片本身已经是 ,这里不能再嵌套链接,否则 HTML 解析会截断卡片。 const upLink = upMid ? `${up}` : up; const status = showStatus && item.status ? `${escapeHtml(statusLabel(item.status))}` : ""; const duration = formatDuration(item.duration); const durationBadge = duration === "—" ? "" : `${escapeHtml(duration)}`; return ` ${rankBadge(rank)}
${cover(item, "video-thumb")} ${durationBadge}

${title}

${status} ${escapeHtml(primaryValue ?? "—")}${escapeHtml(label)} ${delta !== null ? `${formatDelta(delta)}${growthBaselineTag(growth)}` : ""} 互动 ${formatPercent(item.interaction_rate)}
`; } export function upCard(item, { rank = null, primary = "efficiency", primaryLabel = "播放/小时", primaryText = null, secondaryLabel = "总播放", secondaryText = null, } = {}) { const value = primaryText !== null ? primaryText : item[primary]; const secondary = secondaryText !== null ? secondaryText : formatCount(item.total_views); const hours = item.content_hours; const follower = item.follower_latest; const followerText = follower === null || follower === undefined ? "粉丝 —" : `粉丝 ${formatCount(follower)}`; return ` ${rankBadge(rank)}
${avatar(item, "up-avatar")}

${escapeHtml(item.up_name || `UP ${item.up_mid}`)}

${formatNumber(item.video_count)} 条视频 ${hours === null || hours === undefined ? "时长未知" : `${formatNumber(hours)} 小时`} ${followerText}
${escapeHtml(value ?? "—")}${escapeHtml(primaryLabel)} ${escapeHtml(secondaryLabel)} ${escapeHtml(secondary)}
`; } export function metricGrid(item) { const metrics = item.metrics || {}; const growth = item.growth; return `
${["view", "like", "coin", "favorite", "share", "reply", "danmaku"].map((metric) => { const extra = metric === "view" && growth?.available ? `${formatDelta(growth.delta)}${growthBaselineTag(growth)}` : ""; return `
${escapeHtml(metricLabel(metric))} ${formatCount(metrics[metric])}${extra}
`; }).join("")}
`; } export function mediaCard(item, { rank = null, highlight = "" } = {}) { const duration = formatDuration(item.duration); const durationBadge = duration === "—" ? "" : `${escapeHtml(duration)}`; return ` ${rankBadge(rank)}
${cover(item, "media-thumb")} ${durationBadge}
${escapeHtml(item.title || "(无标题)")} ${escapeHtml(item.up_name || "未知 UP")} ${escapeHtml(formatDate(item.pubdate))} ${highlight ? `${highlight}` : ""}
`; } export function sectionHeader(title, extra = "") { return `

${escapeHtml(title)}

${extra}
`; } export function emptyState(title, message = "", action = "") { return `
${escapeHtml(title)} ${message ? `

${escapeHtml(message)}

` : ""}${action}
`; } export function badge(text, kind = "subtle") { return `${escapeHtml(text)}`; } export function chip(label, active, data, value = "") { return ``; } export function formatNumber(value) { return value === null || value === undefined ? "—" : Number(value).toLocaleString("zh-CN"); } export function metricChips(metrics, active, dataName = "metric") { return metrics.map((metric) => chip( metricLabel(metric), metric === active, dataName, metric, )).join(""); } export function centerActiveChips(root) { root.querySelectorAll(".chip-row.scroll-x").forEach((row) => { const active = row.querySelector(".chip.active"); if (!active) return; const target = active.offsetLeft - row.clientWidth / 2 + active.clientWidth / 2; row.scrollLeft = Math.max(0, target); }); } export function pager(page, pages) { if (!pages || pages <= 1) return ""; return `
第 ${page} / ${pages} 页
`; }