Files
re-xxm/reaction_board/web/static/views/candidates.js
T

75 lines
3.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { api } from "../api.js";
import { cover, emptyState, sectionHeader } from "../components.js";
import {
escapeAttr, escapeHtml, formatCount, formatDate, formatDuration,
} from "../format.js";
function candidateCard(item) {
const score = Number(item.score || 0);
return `<article class="candidate-card">
${cover(item, "candidate-thumb")}
<div class="candidate-body">
<div class="candidate-top">
<span class="score-badge ${score >= 4 ? "high" : score >= 2 ? "mid" : "low"}">score ${score}</span>
${item.notified_at ? `<span class="badge subtle">已通知</span>` : `<span class="badge warning">未通知</span>`}
<span class="badge subtle">剩余 ${item.days_left} 天</span>
</div>
<h3 class="video-title">${escapeHtml(item.title || "(无标题)")}</h3>
<div class="video-meta">
<span>${escapeHtml(item.up_name || "未知 UP")}</span>
<span>发布 ${escapeHtml(formatDate(item.pubdate))}</span>
<span>${escapeHtml(formatDuration(item.duration))}</span>
<span>${escapeHtml(item.channel || "")}</span>
</div>
<div class="video-stats">
<span class="secondary-stat">播放 ${formatCount(item.play)}</span>
<span class="secondary-stat">点赞 ${formatCount(item.like)}</span>
<span class="secondary-stat">收藏 ${formatCount(item.favorite)}</span>
<span class="secondary-stat">弹幕 ${formatCount(item.danmaku)}</span>
<span class="secondary-stat">评论 ${formatCount(item.review)}</span>
</div>
<div class="candidate-actions">
${item.url ? `<a class="button ghost" href="${escapeAttr(item.url)}" target="_blank" rel="noreferrer">打开 B 站</a>` : ""}
<span class="readonly-note">请在 Telegram 中收藏 / 忽略 / 拉黑</span>
</div>
</div>
</article>`;
}
export async function renderCandidates(root) {
const data = await api("/api/candidates");
const pending = data.items.filter((item) => item.decision === null);
const decided = data.items.filter((item) => item.decision !== null);
root.innerHTML = `<section class="page candidates-page">
<header class="page-head">
<div>
<h1>候选池</h1>
<p class="page-sub">${formatCount(data.pending)} 条待确认 · ${formatCount(data.total)} 条总计 · 保留 ${data.ttl_days} 天</p>
</div>
</header>
${data.items.length ? `<div class="notice-row">
<strong>候选处理</strong>
<span>${data.telegram_enabled ? "请在 Telegram 消息中收藏、忽略或拉黑。" : "Telegram 未启用,请使用 CLI 或数据库处理候选。"}</span>
</div>` : ""}
<section class="panel">
${sectionHeader("待确认")}
${pending.length ? `<div class="candidate-list">${pending.map(candidateCard).join("")}</div>`
: emptyState("没有待确认候选", "搜索发现达到阈值的视频会出现在这里,同时发送 Telegram 通知。")}
</section>
${decided.length ? `<details class="panel details-panel">
<summary>已处理候选(${decided.length})</summary>
<div class="candidate-list">
${decided.map((item) => `<div class="decided-row">
<span class="badge subtle">${escapeHtml(item.decision)}</span>
<a href="${escapeAttr(item.url || "#")}" target="_blank" rel="noreferrer">${escapeHtml(item.title)}</a>
<span>${escapeHtml(item.up_name || "")}</span>
</div>`).join("")}
</div>
</details>` : ""}
</section>`;
}