重做斗鱼粉丝日报信息模板
- 将粉丝日报改为信息优先布局,新增重点信息、话题簇、英雄焦点和热点窗口区块 - 让模板直接展示本地提纯出的有效信息,不再只依赖少量乐子文案 - 补充粉丝日报渲染辅助函数,提升证据簇和高频信息的承载能力
This commit is contained in:
@@ -462,6 +462,159 @@ def _render_fans_metric_cards(metrics: List[Dict[str, str]]) -> str:
|
||||
return "".join(blocks)
|
||||
|
||||
|
||||
def _build_fans_effective_info_lines(payload: Dict[str, Any], limit: int = 6) -> List[str]:
|
||||
"""
|
||||
为粉丝日报补一层“有效信息速览”。
|
||||
这里优先从本地提纯后的主题证据簇中拿事实,不依赖 LLM 自己归纳,
|
||||
这样模板本身就能稳定承载更多有效信息。
|
||||
"""
|
||||
lines: List[str] = []
|
||||
seen = set()
|
||||
|
||||
def push(text: str) -> None:
|
||||
value = str(text or "").strip()
|
||||
if not value or value in seen:
|
||||
return
|
||||
seen.add(value)
|
||||
lines.append(value)
|
||||
|
||||
for item in (payload.get("topic_evidence_clusters", []) or [])[:6]:
|
||||
label = str(item.get("label") or "").strip()
|
||||
count = int(item.get("count", 0) or 0)
|
||||
time_range = str(item.get("time_range") or "").strip()
|
||||
samples = item.get("samples", []) or []
|
||||
sample_text = ""
|
||||
if samples:
|
||||
first_sample = samples[0]
|
||||
sample_text = str(first_sample.get("content") or "").strip()[:42]
|
||||
if label and sample_text:
|
||||
push(f"{label}在 {time_range or '全场'} 持续被讨论,相关弹幕约 {count} 条,代表内容是「{sample_text}」。")
|
||||
elif label:
|
||||
push(f"{label}是今天的高关注话题之一,相关弹幕约 {count} 条。")
|
||||
if len(lines) >= limit:
|
||||
return lines[:limit]
|
||||
|
||||
hero_mentions = payload.get("compact_scene_material", {}).get("semantic_fact_hints", {}).get("hero_mentions", []) or []
|
||||
if hero_mentions:
|
||||
hero_names = [str(item.get("hero") or "").strip() for item in hero_mentions[:4] if str(item.get("hero") or "").strip()]
|
||||
if hero_names:
|
||||
push(f"英雄讨论主要集中在 {'、'.join(hero_names)}。")
|
||||
|
||||
hot_windows = payload.get("local_stats", {}).get("peak_windows", []) or []
|
||||
if hot_windows:
|
||||
top_window = hot_windows[0]
|
||||
push(
|
||||
f"最热窗口出现在 {str(top_window.get('start_time') or '')[-8:-3]},"
|
||||
f"该时段累计弹幕 {int(top_window.get('message_count', 0) or 0)} 条。"
|
||||
)
|
||||
return lines[:limit]
|
||||
|
||||
|
||||
def _render_fans_info_cards(items: List[str]) -> str:
|
||||
blocks = []
|
||||
for item in items[:6]:
|
||||
blocks.append(
|
||||
'<div class="fans-info-card">'
|
||||
f'<div class="fans-info-text">{_escape(item)}</div>'
|
||||
'</div>'
|
||||
)
|
||||
return "".join(blocks)
|
||||
|
||||
|
||||
def _render_topic_clusters(topic_clusters: List[Dict[str, Any]]) -> str:
|
||||
blocks = []
|
||||
for item in topic_clusters[:5]:
|
||||
label = str(item.get("label") or "").strip()
|
||||
if not label:
|
||||
continue
|
||||
count = int(item.get("count", 0) or 0)
|
||||
user_count = int(item.get("user_count", 0) or 0)
|
||||
time_range = str(item.get("time_range") or "").strip()
|
||||
samples = item.get("samples", []) or []
|
||||
sample_lines = []
|
||||
for sample in samples[:3]:
|
||||
content = str(sample.get("content") or "").strip()
|
||||
nickname = str(sample.get("nickname") or "").strip() or "观众"
|
||||
hm = str(sample.get("hm") or "").strip()
|
||||
if content:
|
||||
sample_lines.append(f"{hm} {nickname}:{content[:42]}")
|
||||
sample_html = "".join(f'<li>{_escape(line)}</li>' for line in sample_lines)
|
||||
blocks.append(
|
||||
'<div class="topic-cluster-card">'
|
||||
f'<div class="topic-cluster-title">{_escape(label)}</div>'
|
||||
f'<div class="topic-cluster-meta">{_escape(time_range or "全场")} · {count} 条讨论 · {user_count} 人参与</div>'
|
||||
f'<ul class="topic-cluster-list">{sample_html}</ul>'
|
||||
'</div>'
|
||||
)
|
||||
return "".join(blocks)
|
||||
|
||||
|
||||
def _render_hero_mentions(hero_mentions: List[Dict[str, Any]]) -> str:
|
||||
blocks = []
|
||||
for item in hero_mentions[:4]:
|
||||
hero_name = str(item.get("hero") or "").strip()
|
||||
if not hero_name:
|
||||
continue
|
||||
mention_count = int(item.get("mention_count", 0) or 0)
|
||||
user_count = int(item.get("user_count", 0) or 0)
|
||||
sample_text = ""
|
||||
samples = item.get("samples", []) or []
|
||||
if samples:
|
||||
sample = samples[0]
|
||||
sample_text = str(sample.get("content") or "").strip()[:46]
|
||||
blocks.append(
|
||||
'<div class="hero-mention-card">'
|
||||
f'<div class="hero-mention-name">{_escape(hero_name)}</div>'
|
||||
f'<div class="hero-mention-meta">{mention_count} 次提及 / {user_count} 人讨论</div>'
|
||||
f'<div class="hero-mention-sample">{_escape(sample_text)}</div>'
|
||||
'</div>'
|
||||
)
|
||||
return "".join(blocks)
|
||||
|
||||
|
||||
def _render_hot_window_cards(hot_windows: List[Dict[str, Any]]) -> str:
|
||||
blocks = []
|
||||
for item in hot_windows[:4]:
|
||||
start_time = str(item.get("start_time") or "")[-8:-3]
|
||||
message_count = int(item.get("message_count", 0) or 0)
|
||||
user_count = int(item.get("user_count", 0) or 0)
|
||||
blocks.append(
|
||||
'<div class="fans-hot-window-card">'
|
||||
f'<div class="fans-hot-window-time">{_escape(start_time)}</div>'
|
||||
f'<div class="fans-hot-window-meta">{message_count} 条弹幕 / {user_count} 人参与</div>'
|
||||
'</div>'
|
||||
)
|
||||
return "".join(blocks)
|
||||
|
||||
|
||||
def _render_repeat_digest(payload: Dict[str, Any]) -> str:
|
||||
local_stats = payload.get("local_stats", {}) or {}
|
||||
repeated_messages = local_stats.get("top_repeated_messages", []) or []
|
||||
emotion_bursts = local_stats.get("top_emotion_bursts", []) or []
|
||||
blocks = []
|
||||
for item in repeated_messages[:4]:
|
||||
text = str(item.get("text") or "").strip()
|
||||
count = int(item.get("count", 0) or 0)
|
||||
if text:
|
||||
blocks.append(
|
||||
'<div class="repeat-chip">'
|
||||
f'<span class="repeat-chip-text">{_escape(text[:28])}</span>'
|
||||
f'<span class="repeat-chip-count">{count} 次</span>'
|
||||
'</div>'
|
||||
)
|
||||
for item in emotion_bursts[:4]:
|
||||
text = str(item.get("text") or "").strip()
|
||||
count = int(item.get("count", 0) or 0)
|
||||
if text:
|
||||
blocks.append(
|
||||
'<div class="repeat-chip emotion">'
|
||||
f'<span class="repeat-chip-text">{_escape(text[:18])}</span>'
|
||||
f'<span class="repeat-chip-count">{count} 次</span>'
|
||||
'</div>'
|
||||
)
|
||||
return "".join(blocks)
|
||||
|
||||
|
||||
def _render_badges(top_badges: List[Dict[str, Any]]) -> str:
|
||||
blocks = []
|
||||
for item in top_badges[:6]:
|
||||
@@ -783,6 +936,14 @@ def render_fans_daily_report_html(
|
||||
lead_text = str(sections.get("lead") or "").strip()
|
||||
if not lead_text:
|
||||
lead_text = "今天这场直播的弹幕主打一个集体上头,观众一边盯着画面,一边忙着把梗越刷越离谱。"
|
||||
topic_clusters = payload.get("topic_evidence_clusters", []) or []
|
||||
hero_mentions = (
|
||||
payload.get("compact_scene_material", {})
|
||||
.get("semantic_fact_hints", {})
|
||||
.get("hero_mentions", [])
|
||||
or []
|
||||
)
|
||||
local_stats = payload.get("local_stats", {}) or {}
|
||||
|
||||
renderer = HtmlTemplateRenderer()
|
||||
return renderer.render(
|
||||
@@ -791,8 +952,13 @@ def render_fans_daily_report_html(
|
||||
"title_name": title_name,
|
||||
"subtitle": subtitle,
|
||||
"lead_text": lead_text,
|
||||
# 粉丝版刻意弱化“分析感”,下面几块都只展示娱乐化后的结果。
|
||||
# 粉丝版不再只做“乐子文案展示”,而是补进本地提纯后的有效信息区。
|
||||
"fans_metrics_html": Markup(_render_fans_metric_cards(_build_fans_fun_metrics(payload))),
|
||||
"effective_info_html": Markup(_render_fans_info_cards(_build_fans_effective_info_lines(payload))),
|
||||
"topic_clusters_html": Markup(_render_topic_clusters(topic_clusters)),
|
||||
"hero_mentions_html": Markup(_render_hero_mentions(hero_mentions)),
|
||||
"hot_windows_html": Markup(_render_hot_window_cards(local_stats.get("peak_windows", []) or [])),
|
||||
"repeat_digest_html": Markup(_render_repeat_digest(payload)),
|
||||
"laugh_points_html": Markup(_render_list(laugh_points, item_class="funny-list")),
|
||||
"famous_scenes_html": Markup(_render_fans_scene_cards(famous_scenes)),
|
||||
"meme_rank_html": Markup(_render_rank_cards(meme_rank)),
|
||||
|
||||
@@ -4,95 +4,90 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<style>
|
||||
:root {
|
||||
--bg-1: #fff4d9;
|
||||
--bg-2: #ffe8d6;
|
||||
--bg-3: #ffeef5;
|
||||
--paper: rgba(255, 251, 245, 0.98);
|
||||
--text: #2f1b1b;
|
||||
--muted: #7b5e57;
|
||||
--line: rgba(122, 77, 54, 0.14);
|
||||
--hot-pink: #ff5d8f;
|
||||
--orange: #ff8a3d;
|
||||
--yellow: #ffc857;
|
||||
--red: #f25f5c;
|
||||
--shadow: 0 28px 64px rgba(160, 84, 58, 0.18);
|
||||
--bg-1: #f7efe3;
|
||||
--bg-2: #fde4cf;
|
||||
--bg-3: #fff7ef;
|
||||
--paper: rgba(255, 252, 247, 0.98);
|
||||
--text: #2f241f;
|
||||
--muted: #7a6256;
|
||||
--line: rgba(117, 85, 66, 0.14);
|
||||
--accent: #db5f41;
|
||||
--accent-2: #f3a447;
|
||||
--accent-3: #ff7e67;
|
||||
--shadow: 0 28px 64px rgba(132, 87, 63, 0.16);
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 34px;
|
||||
padding: 28px;
|
||||
background:
|
||||
radial-gradient(circle at 12% 18%, rgba(255, 255, 255, 0.4), transparent 22%),
|
||||
radial-gradient(circle at 88% 10%, rgba(255, 93, 143, 0.14), transparent 24%),
|
||||
linear-gradient(135deg, var(--bg-1) 0%, var(--bg-2) 52%, var(--bg-3) 100%);
|
||||
radial-gradient(circle at 15% 12%, rgba(255,255,255,.45), transparent 22%),
|
||||
radial-gradient(circle at 86% 10%, rgba(255,126,103,.16), transparent 22%),
|
||||
linear-gradient(135deg, var(--bg-1) 0%, var(--bg-2) 46%, var(--bg-3) 100%);
|
||||
font-family: 'Microsoft YaHei', 'PingFang SC', 'Segoe UI', sans-serif;
|
||||
color: var(--text);
|
||||
}
|
||||
.sheet {
|
||||
width: 920px;
|
||||
width: 1080px;
|
||||
margin: 0 auto;
|
||||
background: var(--paper);
|
||||
border-radius: 34px;
|
||||
overflow: hidden;
|
||||
box-shadow: var(--shadow);
|
||||
border: 1px solid rgba(255, 255, 255, 0.75);
|
||||
border: 1px solid rgba(255,255,255,.78);
|
||||
}
|
||||
.hero {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
padding: 34px 38px 30px;
|
||||
background:
|
||||
radial-gradient(circle at 18% 22%, rgba(255,255,255,0.18), transparent 18%),
|
||||
radial-gradient(circle at 90% 20%, rgba(255,255,255,0.16), transparent 20%),
|
||||
linear-gradient(135deg, #ff7b54 0%, #ff5d8f 45%, #ffb703 100%);
|
||||
color: #fffdf7;
|
||||
radial-gradient(circle at 14% 18%, rgba(255,255,255,.2), transparent 16%),
|
||||
radial-gradient(circle at 92% 18%, rgba(255,255,255,.14), transparent 18%),
|
||||
linear-gradient(135deg, #ff875b 0%, #ff6a6a 42%, #f8b84e 100%);
|
||||
color: #fffaf5;
|
||||
}
|
||||
.hero::before {
|
||||
content: "哈哈哈哈";
|
||||
.hero::after {
|
||||
content: "INFO + FUN";
|
||||
position: absolute;
|
||||
right: -10px;
|
||||
top: 20px;
|
||||
font-size: 66px;
|
||||
right: 28px;
|
||||
top: 18px;
|
||||
font-size: 52px;
|
||||
font-weight: 900;
|
||||
letter-spacing: .08em;
|
||||
color: rgba(255,255,255,0.12);
|
||||
transform: rotate(-8deg);
|
||||
color: rgba(255,255,255,.1);
|
||||
transform: rotate(-6deg);
|
||||
}
|
||||
.eyebrow {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 7px 14px;
|
||||
border-radius: 999px;
|
||||
background: rgba(255,255,255,0.16);
|
||||
border: 1px solid rgba(255,255,255,0.2);
|
||||
background: rgba(255,255,255,.14);
|
||||
border: 1px solid rgba(255,255,255,.22);
|
||||
font-size: 12px;
|
||||
letter-spacing: .08em;
|
||||
font-weight: 700;
|
||||
font-weight: 800;
|
||||
}
|
||||
.hero-title {
|
||||
margin: 18px 0 10px;
|
||||
font-size: 42px;
|
||||
line-height: 1.15;
|
||||
font-size: 40px;
|
||||
line-height: 1.16;
|
||||
font-weight: 900;
|
||||
letter-spacing: -0.03em;
|
||||
letter-spacing: -.03em;
|
||||
}
|
||||
.hero-subtitle {
|
||||
font-size: 16px;
|
||||
color: rgba(255, 251, 245, 0.92);
|
||||
color: rgba(255,250,245,.92);
|
||||
}
|
||||
.content {
|
||||
padding: 28px 30px 34px;
|
||||
padding: 26px 28px 32px;
|
||||
}
|
||||
.lead-box {
|
||||
/* 最大总述卡片单独补一点右侧安全区:
|
||||
1. 中文长句在截图里更容易显得右边“贴边”;
|
||||
2. 这里把左右内边距整体拉开,并额外给右侧一点视觉缓冲;
|
||||
3. 不改结构,只修正阅读时的边界观感。 */
|
||||
padding: 22px 30px 22px 26px;
|
||||
padding: 22px 26px;
|
||||
border-radius: 24px;
|
||||
background: linear-gradient(180deg, rgba(255,255,255,0.92), rgba(255,243,237,0.96));
|
||||
border: 1px solid rgba(255, 138, 61, 0.18);
|
||||
box-shadow: 0 14px 30px rgba(186, 101, 69, 0.08);
|
||||
background: linear-gradient(180deg, rgba(255,255,255,.94), rgba(255,244,236,.96));
|
||||
border: 1px solid rgba(219,95,65,.14);
|
||||
box-shadow: 0 14px 30px rgba(171, 106, 75, 0.08);
|
||||
}
|
||||
.lead-kicker {
|
||||
font-size: 13px;
|
||||
@@ -103,8 +98,8 @@
|
||||
}
|
||||
.lead-text {
|
||||
font-size: 20px;
|
||||
line-height: 1.85;
|
||||
color: #4b2e2b;
|
||||
line-height: 1.82;
|
||||
color: #4b332b;
|
||||
font-weight: 600;
|
||||
}
|
||||
.fans-metric-grid {
|
||||
@@ -115,9 +110,9 @@
|
||||
}
|
||||
.fans-metric-card {
|
||||
padding: 18px 18px 16px;
|
||||
border-radius: 22px;
|
||||
background: linear-gradient(180deg, rgba(255,255,255,0.96), rgba(255,245,240,0.92));
|
||||
border: 1px solid rgba(255, 138, 61, 0.16);
|
||||
border-radius: 20px;
|
||||
background: linear-gradient(180deg, rgba(255,255,255,.96), rgba(255,245,239,.92));
|
||||
border: 1px solid rgba(219,95,65,.12);
|
||||
}
|
||||
.fans-metric-label {
|
||||
font-size: 13px;
|
||||
@@ -125,22 +120,23 @@
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.fans-metric-value {
|
||||
font-size: 30px;
|
||||
font-size: 28px;
|
||||
line-height: 1;
|
||||
font-weight: 900;
|
||||
color: #d9485f;
|
||||
color: #cf593e;
|
||||
word-break: break-all;
|
||||
}
|
||||
.fans-metric-hint {
|
||||
margin-top: 8px;
|
||||
font-size: 12px;
|
||||
color: #8b6b63;
|
||||
color: #8f7368;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.section {
|
||||
margin-top: 18px;
|
||||
padding: 24px;
|
||||
padding: 22px;
|
||||
border-radius: 26px;
|
||||
background: linear-gradient(180deg, rgba(255,255,255,0.95), rgba(255,248,244,0.94));
|
||||
background: linear-gradient(180deg, rgba(255,255,255,.96), rgba(255,249,244,.94));
|
||||
border: 1px solid var(--line);
|
||||
}
|
||||
.section-title {
|
||||
@@ -148,20 +144,155 @@
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 16px;
|
||||
font-size: 26px;
|
||||
font-size: 24px;
|
||||
font-weight: 900;
|
||||
color: #5f2a2a;
|
||||
color: #5b2d23;
|
||||
}
|
||||
.section-title .icon {
|
||||
width: 14px;
|
||||
height: 30px;
|
||||
height: 28px;
|
||||
border-radius: 999px;
|
||||
background: linear-gradient(180deg, #ff5d8f, #ffb703);
|
||||
box-shadow: 0 6px 16px rgba(255, 93, 143, 0.22);
|
||||
background: linear-gradient(180deg, var(--accent), var(--accent-2));
|
||||
box-shadow: 0 6px 14px rgba(219,95,65,.2);
|
||||
}
|
||||
.info-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
.fans-info-card {
|
||||
padding: 16px 16px 15px;
|
||||
border-radius: 18px;
|
||||
background: linear-gradient(180deg, rgba(255,255,255,.98), rgba(255,245,240,.94));
|
||||
border: 1px solid rgba(219,95,65,.12);
|
||||
min-height: 112px;
|
||||
}
|
||||
.fans-info-text {
|
||||
font-size: 15px;
|
||||
line-height: 1.76;
|
||||
color: #523935;
|
||||
font-weight: 600;
|
||||
}
|
||||
.topic-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 14px;
|
||||
}
|
||||
.topic-cluster-card {
|
||||
padding: 18px 18px 14px;
|
||||
border-radius: 20px;
|
||||
background: linear-gradient(180deg, rgba(255,255,255,.98), rgba(255,244,237,.94));
|
||||
border: 1px solid rgba(219,95,65,.12);
|
||||
}
|
||||
.topic-cluster-title {
|
||||
font-size: 18px;
|
||||
font-weight: 900;
|
||||
color: #6d2f22;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.topic-cluster-meta {
|
||||
font-size: 12px;
|
||||
color: #8d6c61;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.topic-cluster-list {
|
||||
margin: 0;
|
||||
padding-left: 18px;
|
||||
}
|
||||
.topic-cluster-list li {
|
||||
margin: 8px 0;
|
||||
font-size: 14px;
|
||||
line-height: 1.66;
|
||||
color: #4d3832;
|
||||
}
|
||||
.hero-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
.hero-mention-card {
|
||||
padding: 16px;
|
||||
border-radius: 18px;
|
||||
background: linear-gradient(135deg, rgba(255,255,255,.98), rgba(255,241,232,.94));
|
||||
border: 1px solid rgba(243,164,71,.22);
|
||||
}
|
||||
.hero-mention-name {
|
||||
font-size: 17px;
|
||||
line-height: 1.4;
|
||||
font-weight: 900;
|
||||
color: #6b311f;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.hero-mention-meta {
|
||||
font-size: 12px;
|
||||
color: #8c6d61;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.hero-mention-sample {
|
||||
font-size: 14px;
|
||||
line-height: 1.65;
|
||||
color: #533833;
|
||||
}
|
||||
.timeline-layout {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, .95fr) minmax(0, 1.05fr);
|
||||
gap: 14px;
|
||||
}
|
||||
.hot-window-stack,
|
||||
.repeat-chip-wrap,
|
||||
.meme-rank-stack {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
}
|
||||
.fans-hot-window-card,
|
||||
.repeat-digest-panel,
|
||||
.rank-panel {
|
||||
padding: 14px 16px;
|
||||
border-radius: 18px;
|
||||
background: linear-gradient(180deg, rgba(255,255,255,.98), rgba(255,245,239,.94));
|
||||
border: 1px solid rgba(219,95,65,.12);
|
||||
}
|
||||
.fans-hot-window-time {
|
||||
font-size: 18px;
|
||||
font-weight: 900;
|
||||
color: #c84f3f;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.fans-hot-window-meta {
|
||||
font-size: 14px;
|
||||
color: #5c433b;
|
||||
}
|
||||
.repeat-chip-wrap {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
.repeat-chip {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
padding: 12px 14px;
|
||||
border-radius: 14px;
|
||||
background: rgba(255,255,255,.9);
|
||||
border: 1px solid rgba(219,95,65,.1);
|
||||
}
|
||||
.repeat-chip.emotion {
|
||||
background: rgba(255,247,236,.94);
|
||||
}
|
||||
.repeat-chip-text {
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
color: #4d3932;
|
||||
font-weight: 700;
|
||||
}
|
||||
.repeat-chip-count {
|
||||
flex-shrink: 0;
|
||||
font-size: 12px;
|
||||
color: #a05441;
|
||||
font-weight: 800;
|
||||
}
|
||||
.two-col {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1.1fr) minmax(280px, 0.9fr);
|
||||
grid-template-columns: minmax(0, 1.08fr) minmax(280px, .92fr);
|
||||
gap: 16px;
|
||||
}
|
||||
.funny-list {
|
||||
@@ -171,29 +302,14 @@
|
||||
.funny-list li {
|
||||
margin: 10px 0;
|
||||
color: #533835;
|
||||
font-size: 16px;
|
||||
font-size: 15px;
|
||||
line-height: 1.76;
|
||||
}
|
||||
.meme-rank-stack {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
}
|
||||
.rank-panel {
|
||||
/* 右侧梗王榜单独补一个承托容器:
|
||||
1. 恢复背景层和阴影层,避免内容少时看起来像直接贴在 section 背景上;
|
||||
2. 用更浅的暖色渐变和轻阴影,把右栏边界重新拉出来;
|
||||
3. 保持和整张粉丝日报同一套视觉语言,不做突兀的深色描边。 */
|
||||
padding: 14px;
|
||||
border-radius: 22px;
|
||||
background: linear-gradient(180deg, rgba(255,255,255,0.98), rgba(255,244,238,0.94));
|
||||
border: 1px solid rgba(242, 95, 92, 0.12);
|
||||
box-shadow: 0 14px 28px rgba(186, 101, 69, 0.08);
|
||||
}
|
||||
.meme-rank-card {
|
||||
padding: 14px 16px;
|
||||
border-radius: 18px;
|
||||
background: linear-gradient(135deg, rgba(255,255,255,0.98), rgba(255,240,232,0.95));
|
||||
border: 1px solid rgba(242, 95, 92, 0.18);
|
||||
border-radius: 16px;
|
||||
background: linear-gradient(135deg, rgba(255,255,255,.98), rgba(255,240,232,.95));
|
||||
border: 1px solid rgba(242,95,92,.18);
|
||||
}
|
||||
.meme-rank-no {
|
||||
color: #d9485f;
|
||||
@@ -216,22 +332,21 @@
|
||||
.fans-scene-card {
|
||||
padding: 16px 16px 15px;
|
||||
border-radius: 18px;
|
||||
background:
|
||||
linear-gradient(180deg, rgba(255,255,255,0.98), rgba(255,245,240,0.95));
|
||||
border: 1px solid rgba(255, 138, 61, 0.16);
|
||||
min-height: 108px;
|
||||
background: linear-gradient(180deg, rgba(255,255,255,.98), rgba(255,245,240,.95));
|
||||
border: 1px solid rgba(255,138,61,.16);
|
||||
min-height: 102px;
|
||||
}
|
||||
.fans-scene-quote {
|
||||
color: #5b3c37;
|
||||
font-size: 15px;
|
||||
line-height: 1.8;
|
||||
line-height: 1.75;
|
||||
}
|
||||
.closing-box {
|
||||
margin-top: 16px;
|
||||
padding: 20px 22px;
|
||||
border-radius: 24px;
|
||||
background: linear-gradient(135deg, rgba(255, 93, 143, 0.1), rgba(255, 184, 0, 0.12));
|
||||
border: 1px solid rgba(255, 93, 143, 0.16);
|
||||
background: linear-gradient(135deg, rgba(255,126,103,.1), rgba(243,164,71,.14));
|
||||
border: 1px solid rgba(255,126,103,.16);
|
||||
}
|
||||
.closing-kicker {
|
||||
color: #c2410c;
|
||||
@@ -257,23 +372,44 @@
|
||||
</head>
|
||||
<body>
|
||||
<div class="sheet">
|
||||
<!-- 头图区:强调“粉丝向整活日报”的氛围,先把情绪拉起来。 -->
|
||||
<div class="hero">
|
||||
<div class="eyebrow">DOUYU FANS FUN REPORT</div>
|
||||
<div class="hero-title">{{ title_name }} 的弹幕乐子日报</div>
|
||||
<div class="eyebrow">DOUYU FANS INFO REPORT</div>
|
||||
<div class="hero-title">{{ title_name }} 的粉丝日报</div>
|
||||
<div class="hero-subtitle">{{ subtitle }}</div>
|
||||
</div>
|
||||
<div class="content">
|
||||
<!-- 总述区:用一段话快速交代今天直播间到底好笑在哪。 -->
|
||||
<div class="lead-box">
|
||||
<div class="lead-kicker">今晚气氛速报</div>
|
||||
<div class="lead-kicker">今晚气氛总述</div>
|
||||
<div class="lead-text">{{ lead_text }}</div>
|
||||
</div>
|
||||
|
||||
<!-- 轻量指标区:保留真实数据来源,但展示口径更偏“乐子视角”。 -->
|
||||
<div class="fans-metric-grid">{{ fans_metrics_html }}</div>
|
||||
|
||||
<!-- 笑点区:左侧放梗概,右侧放梗王榜,方便一眼扫完。 -->
|
||||
<div class="section">
|
||||
<div class="section-title"><span class="icon"></span><span>今日重点信息</span></div>
|
||||
<div class="info-grid">{{ effective_info_html }}</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-title"><span class="icon"></span><span>核心讨论话题</span></div>
|
||||
<div class="topic-grid">{{ topic_clusters_html }}</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-title"><span class="icon"></span><span>英雄与对局焦点</span></div>
|
||||
<div class="hero-grid">{{ hero_mentions_html }}</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-title"><span class="icon"></span><span>热点窗口与共识梗</span></div>
|
||||
<div class="timeline-layout">
|
||||
<div class="hot-window-stack">{{ hot_windows_html }}</div>
|
||||
<div class="repeat-digest-panel">
|
||||
<div class="repeat-chip-wrap">{{ repeat_digest_html }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-title"><span class="icon"></span><span>今日笑点</span></div>
|
||||
<div class="two-col">
|
||||
@@ -284,19 +420,17 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 名场面区:尽量让内容保留“弹幕原声感”,增强现场回放味道。 -->
|
||||
<div class="section">
|
||||
<div class="section-title"><span class="icon"></span><span>弹幕名场面</span></div>
|
||||
<div class="scene-grid">{{ famous_scenes_html }}</div>
|
||||
</div>
|
||||
|
||||
<!-- 收尾句:给整张图一个群聊转发时更像“梗图文案”的落点。 -->
|
||||
<div class="closing-box">
|
||||
<div class="closing-kicker">收尾播报</div>
|
||||
<div class="closing-text">{{ closing_text }}</div>
|
||||
</div>
|
||||
|
||||
<div class="footer-note">ABOT · Douyu Fans Fun Report</div>
|
||||
<div class="footer-note">ABOT · Douyu Fans Info Report</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user