feat(web): include removed in visible stats; cover bang badge

站内展示/统计统一 VISIBLE(含 removed):热度、UP 列表、总览
active_ups/初导入计数、说明页粉丝覆盖、黑名单视频数。采集队列仍
只 active/unfaved。失效片封面右上角感叹号徽标,点击/tap 提示
「该视频已失效」;手动/候选入库仍可复活 removed→active。
This commit is contained in:
bot-ci committed 2026-11-03 23:45:44 +08:00
1 parent 6c73904b25
commit 0c660c6474
7 files changed
+122 -17

No files matched your search

+9
View File
@@ -134,6 +134,15 @@ function toast(message, kind = "info") {
}, 2600);
}
document.addEventListener("click", (event) => {
const mark = event.target.closest("[data-removed-tip]");
if (!mark) return;
event.preventDefault();
event.stopPropagation();
toast("该视频已失效");
}, true);
window.addEventListener("error", (event) => {
const target = event.target;
if (target instanceof HTMLImageElement) {
+9
View File
@@ -13,6 +13,13 @@ export function cover(item, className = "thumb") {
loading="lazy" referrerpolicy="no-referrer"></div>`;
}
export function removedCoverMark(item) {
if (item?.status !== "removed") return "";
return `<button type="button" class="cover-removed-mark" data-removed-tip
title="该视频已失效" aria-label="该视频已失效">!</button>`;
}
export function rankBadge(rank, className = "rank-badge") {
if (!rank) return "";
const top = rank <= 3 ? ` rank-${rank}` : "";
@@ -73,6 +80,7 @@ export function videoCard(item, {
<div class="video-cover">
${cover(item, "video-thumb")}
${durationBadge}
${removedCoverMark(item)}
</div>
<div class="video-card-body">
<h3 class="video-title">${title}</h3>
@@ -150,6 +158,7 @@ export function mediaCard(item, { rank = null, highlight = "" } = {}) {
<div class="media-cover">
${cover(item, "media-thumb")}
${durationBadge}
${removedCoverMark(item)}
</div>
<span class="media-title">${escapeHtml(item.title || "(无标题)")}</span>
<span class="media-byline">
+26
View File
@@ -388,6 +388,32 @@ a.live-chip:hover { border-color: color-mix(in srgb, var(--accent) 55%, var(--bo
background: color-mix(in srgb, var(--accent) 70%, transparent);
color: #fff;
}
.detail-cover-wrap { position: relative; }
.cover-removed-mark {
position: absolute;
z-index: 2;
top: 6px;
right: 6px;
display: grid;
place-items: center;
width: 22px;
height: 22px;
padding: 0;
border: 0;
border-radius: 999px;
background: color-mix(in srgb, var(--warning) 82%, #1a1208);
color: #fff8f0;
font-size: 13px;
font-weight: 700;
line-height: 1;
cursor: help;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
}
.cover-removed-mark:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
}
.cover-duration {
position: absolute;
z-index: 1;
+5 -1
View File
@@ -1,6 +1,7 @@
import { api } from "../api.js";
import {
badge, centerActiveChips, cover, emptyState, metricChips, metricGrid,
removedCoverMark,
sectionHeader, videoCard,
} from "../components.js";
import { lineChart, themeColor } from "../charts.js";
@@ -53,7 +54,10 @@ export async function renderVideo(root, { params, query }) {
</header>
<div class="detail-hero">
${cover(detail, "hero-cover")}
<div class="detail-cover-wrap">
${cover(detail, "hero-cover")}
${removedCoverMark(detail)}
</div>
<div class="detail-hero-body">
<h1>${escapeHtml(detail.title)}</h1>
<div class="detail-meta">