feat(ui): densify home and ranking video cards
Drop nested home panels, overlay rank and duration on covers, and keep metrics fully visible by moving dates onto the author row.
This commit is contained in:
1 parent
2641ba6f87
commit
374b138041
3 files changed
+165
-86
No files matched your search
@@ -13,10 +13,10 @@ export function cover(item, className = "thumb") {
|
|||||||
loading="lazy" referrerpolicy="no-referrer"></div>`;
|
loading="lazy" referrerpolicy="no-referrer"></div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function rankBadge(rank) {
|
export function rankBadge(rank, className = "rank-badge") {
|
||||||
if (!rank) return "";
|
if (!rank) return "";
|
||||||
const top = rank <= 3 ? ` rank-${rank}` : "";
|
const top = rank <= 3 ? ` rank-${rank}` : "";
|
||||||
return `<span class="rank-badge${top}">${rank}</span>`;
|
return `<span class="${className}${top}">${rank}</span>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function avatar(item, className = "avatar") {
|
export function avatar(item, className = "avatar") {
|
||||||
@@ -51,19 +51,25 @@ export function videoCard(item, {
|
|||||||
const upLink = upMid ? `<span class="up-name" data-up-mid="${upMid}">${up}</span>` : up;
|
const upLink = upMid ? `<span class="up-name" data-up-mid="${upMid}">${up}</span>` : up;
|
||||||
const status = showStatus && item.status
|
const status = showStatus && item.status
|
||||||
? `<span class="badge status-${escapeAttr(item.status)}">${escapeHtml(statusLabel(item.status))}</span>` : "";
|
? `<span class="badge status-${escapeAttr(item.status)}">${escapeHtml(statusLabel(item.status))}</span>` : "";
|
||||||
|
const duration = formatDuration(item.duration);
|
||||||
|
const durationBadge = duration === "—"
|
||||||
|
? ""
|
||||||
|
: `<span class="cover-duration">${escapeHtml(duration)}</span>`;
|
||||||
return `
|
return `
|
||||||
<a class="video-card${rank ? "" : " no-rank"}" href="/video/${item.aid}" data-aid="${item.aid}">
|
<a class="video-card" href="/video/${item.aid}" data-aid="${item.aid}">
|
||||||
${rankBadge(rank)}
|
${rankBadge(rank)}
|
||||||
${cover(item, "video-thumb")}
|
<div class="video-cover">
|
||||||
|
${cover(item, "video-thumb")}
|
||||||
|
${durationBadge}
|
||||||
|
</div>
|
||||||
<div class="video-card-body">
|
<div class="video-card-body">
|
||||||
<h3 class="video-title">${title}</h3>
|
<h3 class="video-title">${title}</h3>
|
||||||
<div class="video-meta">
|
<div class="video-byline">
|
||||||
<span>${upLink}</span>
|
<div class="video-up">${upLink}</div>
|
||||||
<span>${escapeHtml(formatDate(item.pubdate))}</span>
|
<span class="video-date">${escapeHtml(formatDate(item.pubdate))}</span>
|
||||||
<span>${escapeHtml(formatDuration(item.duration))}</span>
|
|
||||||
${status}
|
|
||||||
</div>
|
</div>
|
||||||
<div class="video-stats">
|
<div class="video-stats">
|
||||||
|
${status}
|
||||||
<span class="primary-stat">
|
<span class="primary-stat">
|
||||||
<b>${escapeHtml(primaryValue ?? "—")}</b><small>${escapeHtml(label)}</small>
|
<b>${escapeHtml(primaryValue ?? "—")}</b><small>${escapeHtml(label)}</small>
|
||||||
</span>
|
</span>
|
||||||
@@ -88,7 +94,9 @@ export function upCard(item, {
|
|||||||
return `
|
return `
|
||||||
<a class="up-card" href="/up/${item.up_mid}">
|
<a class="up-card" href="/up/${item.up_mid}">
|
||||||
${rankBadge(rank)}
|
${rankBadge(rank)}
|
||||||
${avatar(item, "up-avatar")}
|
<div class="up-cover">
|
||||||
|
${avatar(item, "up-avatar")}
|
||||||
|
</div>
|
||||||
<div class="up-card-body">
|
<div class="up-card-body">
|
||||||
<h3 class="video-title">${escapeHtml(item.up_name || `UP ${item.up_mid}`)}</h3>
|
<h3 class="video-title">${escapeHtml(item.up_name || `UP ${item.up_mid}`)}</h3>
|
||||||
<div class="video-meta">
|
<div class="video-meta">
|
||||||
@@ -120,12 +128,22 @@ export function metricGrid(item) {
|
|||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mediaCard(item, { rank = null, meta = "", highlight = "" } = {}) {
|
export function mediaCard(item, { rank = null, highlight = "" } = {}) {
|
||||||
|
const duration = formatDuration(item.duration);
|
||||||
|
const durationBadge = duration === "—"
|
||||||
|
? ""
|
||||||
|
: `<span class="cover-duration">${escapeHtml(duration)}</span>`;
|
||||||
return `<a class="media-card" href="/video/${item.aid}" data-aid="${item.aid}">
|
return `<a class="media-card" href="/video/${item.aid}" data-aid="${item.aid}">
|
||||||
${rank === null || rank === undefined ? "" : `<span class="media-rank">${escapeHtml(String(rank))}</span>`}
|
${rankBadge(rank)}
|
||||||
${cover(item, "media-thumb")}
|
<div class="media-cover">
|
||||||
<strong class="media-title">${escapeHtml(item.title || "(无标题)")}</strong>
|
${cover(item, "media-thumb")}
|
||||||
${meta ? `<span class="media-meta">${meta}</span>` : ""}
|
${durationBadge}
|
||||||
|
</div>
|
||||||
|
<span class="media-title">${escapeHtml(item.title || "(无标题)")}</span>
|
||||||
|
<span class="media-byline">
|
||||||
|
<span class="media-up">${escapeHtml(item.up_name || "未知 UP")}</span>
|
||||||
|
<span class="media-date">${escapeHtml(formatDate(item.pubdate))}</span>
|
||||||
|
</span>
|
||||||
${highlight ? `<span class="media-highlight">${highlight}</span>` : ""}
|
${highlight ? `<span class="media-highlight">${highlight}</span>` : ""}
|
||||||
</a>`;
|
</a>`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -297,6 +297,7 @@ h1, h2, h3, strong, b { font-weight: 600; }
|
|||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
.section-head h2 { font-size: 16px; font-weight: 600; }
|
.section-head h2 { font-size: 16px; font-weight: 600; }
|
||||||
|
.home-block { min-width: 0; }
|
||||||
.section-note { color: var(--muted); font-size: 12px; margin: 0 0 12px; }
|
.section-note { color: var(--muted); font-size: 12px; margin: 0 0 12px; }
|
||||||
.warning-text { color: var(--warning); }
|
.warning-text { color: var(--warning); }
|
||||||
.danger { color: var(--danger); }
|
.danger { color: var(--danger); }
|
||||||
@@ -343,8 +344,7 @@ a.live-chip:hover { border-color: color-mix(in srgb, var(--accent) 55%, var(--bo
|
|||||||
.video-card, .up-card {
|
.video-card, .up-card {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 24px 96px minmax(0, 1fr);
|
align-items: stretch;
|
||||||
align-items: start;
|
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
@@ -353,50 +353,75 @@ a.live-chip:hover { border-color: color-mix(in srgb, var(--accent) 55%, var(--bo
|
|||||||
transition: border-color 0.15s cubic-bezier(0.22, 1, 0.36, 1),
|
transition: border-color 0.15s cubic-bezier(0.22, 1, 0.36, 1),
|
||||||
background-color 0.15s cubic-bezier(0.22, 1, 0.36, 1);
|
background-color 0.15s cubic-bezier(0.22, 1, 0.36, 1);
|
||||||
}
|
}
|
||||||
|
.video-card { grid-template-columns: 128px minmax(0, 1fr); }
|
||||||
|
.up-card { grid-template-columns: 64px minmax(0, 1fr); align-items: center; }
|
||||||
.video-card:hover, .up-card:hover {
|
.video-card:hover, .up-card:hover {
|
||||||
border-color: color-mix(in srgb, var(--accent) 45%, var(--border));
|
border-color: color-mix(in srgb, var(--accent) 45%, var(--border));
|
||||||
background: color-mix(in srgb, var(--accent) 5%, var(--surface));
|
background: color-mix(in srgb, var(--accent) 5%, var(--surface));
|
||||||
}
|
}
|
||||||
.video-card.no-rank, .up-card.no-rank { grid-template-columns: 96px minmax(0, 1fr); }
|
.video-cover, .media-cover, .up-cover { position: relative; overflow: hidden; }
|
||||||
.video-card.no-rank .video-thumb, .up-card.no-rank .video-thumb { grid-column: 1; }
|
.video-cover, .media-cover { border-radius: 6px; }
|
||||||
.video-card.no-rank .video-card-body, .up-card.no-rank .up-card-body { grid-column: 2; }
|
.up-cover { width: 64px; height: 64px; }
|
||||||
.rank-badge {
|
.rank-badge {
|
||||||
grid-column: 1;
|
position: absolute;
|
||||||
grid-row: 1;
|
z-index: 1;
|
||||||
align-self: start;
|
top: 0;
|
||||||
|
left: 0;
|
||||||
display: grid;
|
display: grid;
|
||||||
place-items: center;
|
place-items: center;
|
||||||
width: 24px;
|
min-width: 28px;
|
||||||
height: 24px;
|
height: 22px;
|
||||||
border-radius: 6px;
|
padding: 0 7px;
|
||||||
background: transparent;
|
border-radius: inherit;
|
||||||
color: var(--muted);
|
border-top-right-radius: 0;
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.54);
|
||||||
|
color: #fff;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
line-height: 1;
|
||||||
|
pointer-events: none;
|
||||||
|
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.55);
|
||||||
|
}
|
||||||
|
.rank-badge.rank-1 {
|
||||||
|
background: color-mix(in srgb, var(--accent) 70%, transparent);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
.cover-duration {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: center;
|
||||||
|
padding: 4px 6px;
|
||||||
|
background: rgba(0, 0, 0, 0.4);
|
||||||
|
color: #fff;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 1;
|
||||||
|
pointer-events: none;
|
||||||
|
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.55);
|
||||||
}
|
}
|
||||||
.rank-badge.rank-1 { background: var(--accent); color: var(--accent-contrast); }
|
|
||||||
.rank-badge.rank-2, .rank-badge.rank-3 { background: var(--surface-2); color: var(--text); }
|
|
||||||
.video-thumb, .hero-cover, .media-thumb, .candidate-thumb {
|
.video-thumb, .hero-cover, .media-thumb, .candidate-thumb {
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
background: var(--surface-2);
|
background: var(--surface-2);
|
||||||
}
|
}
|
||||||
.video-thumb {
|
.video-thumb { width: 100%; aspect-ratio: 16 / 9; }
|
||||||
grid-column: 2;
|
|
||||||
grid-row: 1;
|
|
||||||
width: 96px;
|
|
||||||
aspect-ratio: 16 / 9;
|
|
||||||
}
|
|
||||||
.video-thumb img, .hero-cover img, .media-thumb img, .candidate-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
|
.video-thumb img, .hero-cover img, .media-thumb img, .candidate-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
|
||||||
.cover-placeholder { display: grid; place-items: center; color: var(--muted); font-size: 11px; }
|
.cover-placeholder { display: grid; place-items: center; color: var(--muted); font-size: 11px; }
|
||||||
.cover-placeholder img.image-error { opacity: 0; }
|
.cover-placeholder img.image-error { opacity: 0; }
|
||||||
.video-card-body, .up-card-body { grid-column: 3; grid-row: 1; min-width: 0; }
|
.video-card-body, .up-card-body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
.up-avatar {
|
.up-avatar {
|
||||||
grid-column: 2;
|
|
||||||
grid-row: 1;
|
|
||||||
justify-self: center;
|
|
||||||
align-self: center;
|
|
||||||
width: 64px;
|
width: 64px;
|
||||||
height: 64px;
|
height: 64px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
@@ -409,7 +434,6 @@ a.live-chip:hover { border-color: color-mix(in srgb, var(--accent) 55%, var(--bo
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
.up-avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
|
.up-avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
|
||||||
.up-card.no-rank > .up-avatar { grid-column: 1; }
|
|
||||||
.avatar-placeholder { text-transform: uppercase; }
|
.avatar-placeholder { text-transform: uppercase; }
|
||||||
.video-title {
|
.video-title {
|
||||||
display: -webkit-box;
|
display: -webkit-box;
|
||||||
@@ -420,8 +444,26 @@ a.live-chip:hover { border-color: color-mix(in srgb, var(--accent) 55%, var(--bo
|
|||||||
line-height: 1.35;
|
line-height: 1.35;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
/* 标题固定占两行高度(line-height 1.35 × 2),让单行与两行标题的行高一致。 */
|
/* 标题固定占两行高度,让单行与两行标题的卡片高度一致。 */
|
||||||
.video-card .video-title, .media-title { min-height: 2.7em; }
|
.video-card .video-title { min-height: 2.7em; }
|
||||||
|
.media-title { min-height: 2.8em; }
|
||||||
|
.video-byline {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: 4px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.video-up {
|
||||||
|
min-width: 0;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
.video-meta {
|
.video-meta {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
@@ -434,12 +476,24 @@ a.live-chip:hover { border-color: color-mix(in srgb, var(--accent) 55%, var(--bo
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
align-items: baseline;
|
align-items: baseline;
|
||||||
gap: 4px 10px;
|
gap: 4px 8px;
|
||||||
margin-top: 6px;
|
margin-top: 6px;
|
||||||
color: var(--muted);
|
color: var(--muted);
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
|
min-width: 0;
|
||||||
}
|
}
|
||||||
.primary-stat { display: inline-flex; align-items: baseline; gap: 4px; color: var(--text); }
|
.video-card .video-stats {
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
margin-top: auto;
|
||||||
|
padding-top: 8px;
|
||||||
|
}
|
||||||
|
.video-date {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: 12px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.primary-stat { display: inline-flex; flex: 0 0 auto; align-items: baseline; gap: 4px; color: var(--text); white-space: nowrap; }
|
||||||
.primary-stat b { font-size: 15px; font-weight: 600; }
|
.primary-stat b { font-size: 15px; font-weight: 600; }
|
||||||
.primary-stat small { color: var(--muted); font-size: 11px; }
|
.primary-stat small { color: var(--muted); font-size: 11px; }
|
||||||
.secondary-stat { white-space: nowrap; }
|
.secondary-stat { white-space: nowrap; }
|
||||||
@@ -451,12 +505,13 @@ a.live-chip:hover { border-color: color-mix(in srgb, var(--accent) 55%, var(--bo
|
|||||||
/* 统一的响应式媒体网格:移动端 2 列(图片自动缩放),PC 端 3 列。 */
|
/* 统一的响应式媒体网格:移动端 2 列(图片自动缩放),PC 端 3 列。 */
|
||||||
.media-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
|
.media-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
|
||||||
@media (min-width: 900px) { .media-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
|
@media (min-width: 900px) { .media-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
|
||||||
/* 统一的竖排媒体卡:图 + 两行截断标题 + 作者/数据行 + 高亮数据行。 */
|
/* 竖排媒体卡:封面(时长)+ 两行标题 + 单行 UP + 日期/指标底栏。 */
|
||||||
.media-card {
|
.media-card {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 6px;
|
height: 100%;
|
||||||
|
gap: 8px;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
@@ -468,33 +523,45 @@ a.live-chip:hover { border-color: color-mix(in srgb, var(--accent) 55%, var(--bo
|
|||||||
border-color: color-mix(in srgb, var(--accent) 45%, var(--border));
|
border-color: color-mix(in srgb, var(--accent) 45%, var(--border));
|
||||||
background: color-mix(in srgb, var(--accent) 5%, var(--surface));
|
background: color-mix(in srgb, var(--accent) 5%, var(--surface));
|
||||||
}
|
}
|
||||||
.media-rank {
|
|
||||||
position: absolute;
|
|
||||||
top: 10px;
|
|
||||||
left: 10px;
|
|
||||||
z-index: 1;
|
|
||||||
display: grid;
|
|
||||||
place-items: center;
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
border-radius: 6px;
|
|
||||||
background: rgba(0, 0, 0, 0.62);
|
|
||||||
color: #fff;
|
|
||||||
font-size: 11px;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
.media-thumb { width: 100%; aspect-ratio: 16 / 9; }
|
.media-thumb { width: 100%; aspect-ratio: 16 / 9; }
|
||||||
.media-title {
|
.media-title {
|
||||||
display: -webkit-box;
|
display: -webkit-box;
|
||||||
-webkit-line-clamp: 2;
|
-webkit-line-clamp: 2;
|
||||||
-webkit-box-orient: vertical;
|
-webkit-box-orient: vertical;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
font-size: 13px;
|
font-size: 12px;
|
||||||
line-height: 1.35;
|
line-height: 1.4;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
.media-byline {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 8px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.media-up {
|
||||||
|
min-width: 0;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
.media-date {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: 11px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.media-highlight {
|
||||||
|
margin-top: auto;
|
||||||
|
color: var(--accent-strong);
|
||||||
|
font-size: 11px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
.media-meta { color: var(--muted); font-size: 11px; }
|
|
||||||
.media-highlight { color: var(--accent-strong); font-size: 11px; font-weight: 600; }
|
|
||||||
|
|
||||||
.status-grid {
|
.status-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
@@ -928,9 +995,7 @@ a.live-chip:hover { border-color: color-mix(in srgb, var(--accent) 55%, var(--bo
|
|||||||
.up-stats { grid-template-columns: repeat(5, minmax(0, 1fr)); }
|
.up-stats { grid-template-columns: repeat(5, minmax(0, 1fr)); }
|
||||||
.detail-hero { flex-direction: row; align-items: flex-start; }
|
.detail-hero { flex-direction: row; align-items: flex-start; }
|
||||||
.hero-cover { width: 300px; flex: 0 0 300px; }
|
.hero-cover { width: 300px; flex: 0 0 300px; }
|
||||||
.video-thumb { width: 112px; }
|
.video-card { grid-template-columns: 144px minmax(0, 1fr); }
|
||||||
.video-card, .up-card { grid-template-columns: 24px 112px minmax(0, 1fr); }
|
|
||||||
.video-card.no-rank, .up-card.no-rank { grid-template-columns: 112px minmax(0, 1fr); }
|
|
||||||
.secondary-stat.optional { display: inline; }
|
.secondary-stat.optional { display: inline; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -983,9 +1048,7 @@ a.live-chip:hover { border-color: color-mix(in srgb, var(--accent) 55%, var(--bo
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 380px) {
|
@media (max-width: 380px) {
|
||||||
.video-card, .up-card { grid-template-columns: 20px 80px minmax(0, 1fr); gap: 8px; }
|
.video-card { grid-template-columns: 104px minmax(0, 1fr); gap: 8px; }
|
||||||
.video-card.no-rank, .up-card.no-rank { grid-template-columns: 80px minmax(0, 1fr); }
|
|
||||||
.video-thumb { width: 80px; }
|
|
||||||
.metric-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
.metric-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||||
.candidate-card { grid-template-columns: 80px minmax(0, 1fr); }
|
.candidate-card { grid-template-columns: 80px minmax(0, 1fr); }
|
||||||
.candidate-thumb { width: 80px; }
|
.candidate-thumb { width: 80px; }
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { api } from "../api.js";
|
import { api } from "../api.js";
|
||||||
import { emptyState, mediaCard } from "../components.js";
|
import { emptyState, mediaCard } from "../components.js";
|
||||||
import {
|
import {
|
||||||
escapeAttr, escapeHtml, formatCount, formatDate, formatDelta,
|
escapeAttr, escapeHtml, formatCount, formatDelta,
|
||||||
formatDuration, formatPercent, formatScore,
|
formatPercent, formatScore,
|
||||||
} from "../format.js";
|
} from "../format.js";
|
||||||
import { canAccessProtected } from "../session.js";
|
import { canAccessProtected } from "../session.js";
|
||||||
|
|
||||||
@@ -19,7 +19,6 @@ function heatGrid(items) {
|
|||||||
return `<div class="media-grid">
|
return `<div class="media-grid">
|
||||||
${items.map((item, index) => mediaCard(item, {
|
${items.map((item, index) => mediaCard(item, {
|
||||||
rank: index + 1,
|
rank: index + 1,
|
||||||
meta: `${escapeHtml(item.up_name || "未知 UP")} · ${formatCount(item.metrics?.view)} 播放`,
|
|
||||||
highlight: `热度 ${formatScore(item.heat)} · 前 ${Math.max(0, 100 - (item.heat_percentile || 0)).toFixed(0)}%`,
|
highlight: `热度 ${formatScore(item.heat)} · 前 ${Math.max(0, 100 - (item.heat_percentile || 0)).toFixed(0)}%`,
|
||||||
})).join("")}
|
})).join("")}
|
||||||
</div>`;
|
</div>`;
|
||||||
@@ -27,19 +26,18 @@ function heatGrid(items) {
|
|||||||
|
|
||||||
function publishedCard(item) {
|
function publishedCard(item) {
|
||||||
return mediaCard(item, {
|
return mediaCard(item, {
|
||||||
meta: `${escapeHtml(item.up_name || "未知 UP")} · ${escapeHtml(formatDate(item.pubdate))} · ${escapeHtml(formatDuration(item.duration))}`,
|
|
||||||
highlight: `${formatCount(item.metrics?.view)} 播放 · 互动 ${formatPercent(item.interaction_rate)}`,
|
highlight: `${formatCount(item.metrics?.view)} 播放 · 互动 ${formatPercent(item.interaction_rate)}`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function growthCard(item, index) {
|
function growthCard(item, index) {
|
||||||
const delta = item.growth?.delta;
|
const delta = item.growth?.delta;
|
||||||
const deltaText = delta === null || delta === undefined ? "" : ` · ${formatDelta(delta)}`;
|
let highlight = `${formatCount(item.metrics?.view)} 播放`;
|
||||||
return mediaCard(item, {
|
if (delta !== null && delta !== undefined) {
|
||||||
rank: index + 1,
|
const tone = Number(delta) >= 0 ? "up" : "down";
|
||||||
meta: `${escapeHtml(item.up_name || "未知 UP")} · ${escapeHtml(formatDate(item.pubdate))} · ${escapeHtml(formatDuration(item.duration))}`,
|
highlight += ` <span class="${tone}">${formatDelta(delta)}</span>`;
|
||||||
highlight: `${formatCount(item.metrics?.view)} 播放${deltaText}`,
|
}
|
||||||
});
|
return mediaCard(item, { rank: index + 1, highlight });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function renderHome(root) {
|
export async function renderHome(root) {
|
||||||
@@ -96,7 +94,7 @@ export async function renderHome(root) {
|
|||||||
<span>请在 Telegram 中处理 →</span>
|
<span>请在 Telegram 中处理 →</span>
|
||||||
</a>` : ""}
|
</a>` : ""}
|
||||||
|
|
||||||
<section class="panel">
|
<section class="home-block">
|
||||||
<div class="section-head">
|
<div class="section-head">
|
||||||
<h2>最近发布</h2>
|
<h2>最近发布</h2>
|
||||||
<a class="text-link" href="/videos?sort=published&within_days=30">查看全部</a>
|
<a class="text-link" href="/videos?sort=published&within_days=30">查看全部</a>
|
||||||
@@ -108,7 +106,7 @@ export async function renderHome(root) {
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="panel">
|
<section class="home-block">
|
||||||
<div class="section-head">
|
<div class="section-head">
|
||||||
<h2>增长动态 · ${escapeHtml(growthLabel)}</h2>
|
<h2>增长动态 · ${escapeHtml(growthLabel)}</h2>
|
||||||
<a class="text-link" href="/videos?sort=growth&growth_days=${growthDays}">增长榜</a>
|
<a class="text-link" href="/videos?sort=growth&growth_days=${growthDays}">增长榜</a>
|
||||||
@@ -118,7 +116,7 @@ export async function renderHome(root) {
|
|||||||
: emptyState(growthEmptyTitle, growthEmptyHint)}
|
: emptyState(growthEmptyTitle, growthEmptyHint)}
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="panel">
|
<section class="home-block">
|
||||||
<div class="section-head">
|
<div class="section-head">
|
||||||
<h2>热度速览</h2>
|
<h2>热度速览</h2>
|
||||||
<a class="text-link" href="/videos?sort=heat">完整榜单</a>
|
<a class="text-link" href="/videos?sort=heat">完整榜单</a>
|
||||||
|
|||||||
Reference in new issue
Block a user