From d25fbaa1984d7ce6c2b97a5fe800af997aebe907 Mon Sep 17 00:00:00 2001 From: meijiali <你的邮箱@xxx.com> Date: Mon, 6 Jul 2026 17:07:31 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E7=8E=B0=E6=9C=89?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2=E4=B8=BA=20shadcn=20=E9=A3=8E=E6=A0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/static/app.css | 528 ++++++++++++++++++----- app/templates/base.html | 2 +- app/templates/hotspots/report.html | 12 +- app/templates/index.html | 26 +- app/templates/items/detail.html | 16 +- app/templates/partials/report_panel.html | 10 +- app/templates/partials/task_rows.html | 15 +- app/templates/tasks/detail.html | 54 ++- 8 files changed, 519 insertions(+), 144 deletions(-) diff --git a/app/static/app.css b/app/static/app.css index cee9210..aa1b0c2 100644 --- a/app/static/app.css +++ b/app/static/app.css @@ -1,203 +1,537 @@ +:root { + --background: #f8fafc; + --foreground: #0f172a; + --muted: #f1f5f9; + --muted-foreground: #64748b; + --card: #ffffff; + --card-foreground: #0f172a; + --border: #e2e8f0; + --input: #cbd5e1; + --primary: #0f172a; + --primary-foreground: #ffffff; + --secondary: #f1f5f9; + --secondary-foreground: #0f172a; + --accent: #f8fafc; + --accent-foreground: #0f172a; + --destructive: #dc2626; + --success: #16a34a; + --warning: #d97706; + --ring: rgba(15, 23, 42, 0.18); + --radius: 8px; +} + body { - background: - linear-gradient(180deg, #f4f7fb 0%, #eef3f8 42%, #f8fafc 100%); - color: #182235; + background: var(--background); + color: var(--foreground); + font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; + letter-spacing: 0; min-height: 100vh; } +a { + color: inherit; +} + +.container { + max-width: 1180px; +} + .app-navbar { - background: rgba(255, 255, 255, 0.94) !important; - border-bottom: 1px solid #dfe7f1; - box-shadow: 0 10px 30px rgba(19, 34, 56, 0.04); + background: rgba(255, 255, 255, 0.92) !important; + border-bottom: 1px solid var(--border); + backdrop-filter: blur(14px); +} + +.app-navbar .container { + min-height: 56px; } .navbar-brand { - color: #102033; + color: var(--foreground); + font-size: 15px; + letter-spacing: 0; } .nav-link { - color: #526173; + border-radius: 6px; + color: var(--muted-foreground); + font-size: 14px; + padding: 6px 10px; +} + +.nav-link:hover { + background: var(--secondary); + color: var(--foreground); +} + +.breadcrumb { + color: var(--muted-foreground); + font-size: 13px; + margin-bottom: 20px; +} + +.breadcrumb a { + color: var(--muted-foreground); + text-decoration: none; +} + +.breadcrumb a:hover { + color: var(--foreground); } .tool-card, -.card { - background: rgba(255, 255, 255, 0.96); - border-radius: 8px; - border: 1px solid #dbe4ef; - box-shadow: 0 16px 42px rgba(31, 42, 68, 0.07); +.card, +.accordion-item { + background: var(--card); + border: 1px solid var(--border); + border-radius: var(--radius); + box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04); + color: var(--card-foreground); overflow: hidden; } +.card-body { + padding: 20px; +} + .card-header, .section-heading { - background: linear-gradient(180deg, #ffffff, #f8fafc); - border-bottom: 1px solid #e4ebf3; - padding: 18px 20px; + background: var(--card); + border-bottom: 1px solid var(--border); + padding: 16px 20px; } .section-heading { align-items: center; display: flex; - justify-content: space-between; gap: 16px; + justify-content: space-between; } .section-heading h2, -.page-heading h1 { +.page-heading h1, +.page-title { + color: var(--foreground); font-size: 22px; - font-weight: 700; + font-weight: 650; letter-spacing: 0; + line-height: 1.25; margin: 0; } +.page-heading { + align-items: flex-start; + display: flex; + gap: 16px; + justify-content: space-between; + margin-bottom: 18px; +} + .section-kicker, .eyebrow { - color: #0e766e; + color: var(--muted-foreground); font-size: 12px; - font-weight: 700; + font-weight: 650; letter-spacing: 0; - margin: 0 0 4px; + margin: 0 0 6px; text-transform: uppercase; } .hero-band { + background: var(--card); + border: 1px solid var(--border); + border-radius: var(--radius); + box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04); + color: var(--foreground); + padding: 30px; +} + +.hero-shell { align-items: flex-end; - background: - linear-gradient(135deg, rgba(12, 22, 40, 0.92), rgba(29, 80, 108, 0.78)), - url("https://images.unsplash.com/photo-1551288049-bebda4e38f71?auto=format&fit=crop&w=1600&q=80"); - background-position: center; - background-size: cover; - border: 1px solid rgba(255, 255, 255, 0.18); - border-radius: 8px; - box-shadow: 0 24px 60px rgba(15, 36, 62, 0.18); - color: #fff; display: flex; + gap: 28px; justify-content: space-between; - min-height: 300px; - padding: 42px; } .hero-copy { - max-width: 680px; + max-width: 700px; } .hero-copy h1 { - font-size: 48px; + color: var(--foreground); + font-size: 34px; font-weight: 700; letter-spacing: 0; - margin-bottom: 14px; + line-height: 1.15; + margin: 0 0 12px; } .hero-copy p:not(.eyebrow) { - color: rgba(255, 255, 255, 0.84); - font-size: 18px; - line-height: 1.7; + color: var(--muted-foreground); + font-size: 15px; + line-height: 1.75; margin: 0; } -.hero-actions { - display: flex; - flex-wrap: wrap; - gap: 12px; +.hero-metrics { + display: grid; + gap: 10px; + grid-template-columns: repeat(3, minmax(84px, 1fr)); + min-width: 320px; } -.page-heading { - align-items: center; - display: flex; - justify-content: space-between; - gap: 16px; +.hero-metric { + background: var(--muted); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 12px; } -.metric-box { - background: linear-gradient(180deg, #f9fbfd, #f3f7fb); - border: 1px solid #e3e9f2; - border-radius: 8px; +.hero-metric strong { + display: block; + font-size: 18px; + line-height: 1.2; +} + +.hero-metric span { + color: var(--muted-foreground); + display: block; + font-size: 12px; + margin-top: 4px; +} + +.btn { + border-radius: 6px; + font-size: 14px; + font-weight: 500; +} + +.btn-primary { + background: var(--primary); + border-color: var(--primary); + color: var(--primary-foreground); +} + +.btn-primary:hover, +.btn-primary:focus { + background: #1e293b; + border-color: #1e293b; +} + +.btn-outline-primary, +.btn-outline-secondary { + background: var(--card); + border-color: var(--border); + color: var(--foreground); +} + +.btn-outline-primary:hover, +.btn-outline-secondary:hover { + background: var(--secondary); + border-color: var(--border); + color: var(--foreground); +} + +.form-label { + color: var(--foreground); + font-size: 13px; + font-weight: 600; +} + +.form-control, +.form-select { + border-color: var(--input); + border-radius: 6px; + color: var(--foreground); + font-size: 14px; + min-height: 40px; +} + +.form-control:focus, +.form-select:focus { + border-color: var(--foreground); + box-shadow: 0 0 0 3px var(--ring); +} + +.form-text { + color: var(--muted-foreground) !important; +} + +.badge { + border-radius: 999px; + font-size: 12px; + font-weight: 600; + letter-spacing: 0; + padding: 5px 9px; +} + +.bg-success, +.text-bg-success { + background-color: #dcfce7 !important; + color: #166534 !important; +} + +.bg-danger, +.text-bg-danger { + background-color: #fee2e2 !important; + color: #991b1b !important; +} + +.bg-warning, +.text-bg-warning { + background-color: #fef3c7 !important; + color: #92400e !important; +} + +.bg-secondary, +.text-bg-secondary { + background-color: var(--secondary) !important; + color: var(--secondary-foreground) !important; +} + +.text-bg-light { + background: var(--secondary) !important; + color: var(--foreground) !important; +} + +.alert { + border-radius: var(--radius); + font-size: 14px; + margin-bottom: 16px; +} + +.alert-info { + background: #eff6ff; + border-color: #bfdbfe; + color: #1e3a8a; +} + +.alert-warning { + background: #fffbeb; + border-color: #fde68a; + color: #92400e; +} + +.alert-danger { + background: #fef2f2; + border-color: #fecaca; + color: #991b1b; +} + +.alert-secondary { + background: var(--secondary); + border-color: var(--border); + color: var(--foreground); +} + +.metric-box, +.stat-card { + background: var(--card); + border: 1px solid var(--border); + border-radius: var(--radius); height: 100%; padding: 16px; } -.metric-box strong { - color: #132033; +.metric-box strong, +.stat-card strong { + color: var(--foreground); display: block; font-size: 18px; + font-weight: 650; + line-height: 1.35; margin: 4px 0; } +.app-table { + color: var(--foreground); + min-width: 760px; +} + .app-table thead th { - background: #f8fafc; - color: #536174; + background: var(--muted); + border-bottom: 1px solid var(--border); + color: var(--muted-foreground); font-size: 12px; - font-weight: 700; - text-transform: uppercase; + font-weight: 650; + height: 42px; + letter-spacing: 0; + text-transform: none; white-space: nowrap; } -.app-table tbody tr { - border-color: #e8eef5; +.app-table td { + border-color: var(--border); + padding-bottom: 14px; + padding-top: 14px; + vertical-align: middle; } .app-table tbody tr:hover { - background: #f7fbff; + background: var(--accent); } .task-number { align-items: center; - background: #e7f0ff; - border: 1px solid #cfe0ff; + background: var(--secondary); + border: 1px solid var(--border); border-radius: 999px; - color: #0b5ed7; + color: var(--foreground); display: inline-flex; - font-weight: 700; - height: 32px; + font-weight: 650; + height: 30px; justify-content: center; - min-width: 32px; - padding: 0 10px; -} - -.task-overview { - border-top: 3px solid #0d6efd; -} - -.hotspot-item { - border-color: #e3eaf2; - margin-bottom: 10px; - overflow: hidden; -} - -.status-panel { - background: #fff; - border: 1px solid #dfe5ef; - border-radius: 8px; - padding: 32px; -} - -.status-panel-danger { - border-color: #f1b8b8; + min-width: 30px; + padding: 0 9px; } .task-progress { - height: 10px; + background: var(--secondary); + border-radius: 999px; + height: 8px; +} + +.progress-bar { + background-color: var(--primary); } .report-progress { - height: 10px; + background: var(--secondary); + height: 8px; +} + +.task-overview { + border-top: 0; +} + +.progress-summary { + align-items: center; + display: flex; + flex-wrap: wrap; + gap: 10px; +} + +.hotspot-item { + border-color: var(--border); + margin-bottom: 10px; +} + +.accordion-button { + background: var(--card); + color: var(--foreground); + font-size: 15px; + font-weight: 600; + gap: 10px; + padding: 16px 18px; +} + +.accordion-button:not(.collapsed) { + background: var(--muted); + box-shadow: none; + color: var(--foreground); +} + +.accordion-button:focus { + border-color: var(--border); + box-shadow: 0 0 0 3px var(--ring); +} + +.content-item-row { + align-items: center; + display: flex; + gap: 14px; + justify-content: space-between; +} + +.content-item-title { + color: var(--foreground); + display: block; + font-weight: 600; + line-height: 1.45; + overflow-wrap: anywhere; +} + +.content-item-meta { + color: var(--muted-foreground); + display: block; + font-size: 12px; + margin-top: 3px; + overflow-wrap: anywhere; +} + +.report-summary { + background: var(--muted); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 16px; +} + +.comment-card { + background: var(--card); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 12px; +} + +.empty-state, +.status-panel { + background: var(--card); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 32px; + text-align: center; +} + +.status-panel-danger { + border-color: #fecaca; +} + +@media (max-width: 992px) { + .hero-shell { + align-items: flex-start; + flex-direction: column; + } + + .hero-metrics { + min-width: 0; + width: 100%; + } } @media (max-width: 768px) { + main.container { + padding-left: 14px; + padding-right: 14px; + } + .hero-band { - align-items: flex-start; - flex-direction: column; - min-height: 360px; - padding: 28px; + padding: 22px; } .hero-copy h1 { - font-size: 36px; + font-size: 28px; + } + + .hero-metrics { + grid-template-columns: 1fr; } .section-heading, - .page-heading { + .page-heading, + .content-item-row { align-items: flex-start; flex-direction: column; } + + .card-body { + padding: 16px; + } + + .content-item-row .btn { + width: 100%; + } + + .accordion-button { + align-items: flex-start; + } } diff --git a/app/templates/base.html b/app/templates/base.html index 04267b7..5ffea48 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -5,7 +5,7 @@ {% block title %}热榜评论分析工具{% endblock %} - + diff --git a/app/templates/hotspots/report.html b/app/templates/hotspots/report.html index 876722b..b216ee4 100644 --- a/app/templates/hotspots/report.html +++ b/app/templates/hotspots/report.html @@ -8,15 +8,19 @@ {% endblock %} {% block content %} -
-

{{ hotspot.title }} 汇总报告

+
+
+

热点报告

+

{{ hotspot.title }} 汇总报告

+
+ 热点 #{{ hotspot.rank }}
{% if report %} {% include "partials/report_panel.html" %} {% else %} -
+
-

报告生成中,请稍候...

+

报告生成中,请稍候...

{% endif %} {% endblock %} diff --git a/app/templates/index.html b/app/templates/index.html index 481fdf3..b628b77 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -3,10 +3,26 @@ {% block title %}热榜评论雷达 - 热榜评论分析工具{% endblock %} {% block content %}
-
-

Hot Comment Radar

-

热榜评论雷达

-

从小红书和抖音热点出发,抓取真实评论,生成 AI 情绪、标签和可导出的分析报告。

+
+
+

Hot Comment Radar

+

热榜评论雷达

+

从小红书和抖音热点出发,抓取真实评论,生成 AI 情绪、标签和可导出的分析报告。

+
+
+
+ 5 + 默认热点 +
+
+ 25 + 默认内容 +
+
+ 1,250 + 评论上限 +
+
@@ -16,7 +32,7 @@

采集配置

创建抓取任务

- 默认规模 5 × 5 × 50 + 默认规模 5 × 5 × 50
diff --git a/app/templates/items/detail.html b/app/templates/items/detail.html index af81949..6dc1572 100644 --- a/app/templates/items/detail.html +++ b/app/templates/items/detail.html @@ -9,21 +9,25 @@ {% endblock %} {% block content %} -
-

{{ item.title or item.source_item_id }}

+
+
+

内容详情

+

{{ item.title or item.source_item_id }}

+
+ {{ item.status }}
{% if report %} {% include "partials/report_panel.html" %} {% else %} -
+
-

报告生成中,请稍候...

+

报告生成中,请稍候...

{% endif %}
评论明细
- +
评论内容情绪标签点赞
{% for comment in comments %} - + {% else %} {% endfor %} diff --git a/app/templates/partials/report_panel.html b/app/templates/partials/report_panel.html index e856d6c..978c78e 100644 --- a/app/templates/partials/report_panel.html +++ b/app/templates/partials/report_panel.html @@ -13,28 +13,28 @@
-
+
评论样本
{{ sample_count }}
{% if item_count is not none %}
-
+
关联内容
{{ item_count }}
{% endif %}
-
+
AI 成功率
{{ rate_percent(task.analysis_success_rate) }}%
-
+
AI 总结

{{ report.summary or "总结生成失败,请查看上方统计数据。" }}

@@ -77,7 +77,7 @@

{{ sentiment_label(key) }}

{% for comment in typical.get(key, []) %} -
+

{{ comment.get("content") }}

点赞 {{ comment.get("like_count", 0) }}
diff --git a/app/templates/partials/task_rows.html b/app/templates/partials/task_rows.html index efd1832..8a30fc3 100644 --- a/app/templates/partials/task_rows.html +++ b/app/templates/partials/task_rows.html @@ -6,9 +6,9 @@
- - - + + + - + {% else %} diff --git a/app/templates/tasks/detail.html b/app/templates/tasks/detail.html index 2744221..5abb6eb 100644 --- a/app/templates/tasks/detail.html +++ b/app/templates/tasks/detail.html @@ -15,15 +15,19 @@ {% set label, cls = status_badge_config(task.status) %} {{ label }} -
+
+
{% set percent = progress_percent(task.processed_items_count, task.total_items_count) %} {% set ai_percent = rate_percent(task.analysis_success_rate) %} {% set target_comments = task.hotspot_limit * task.item_limit_per_hotspot * task.comment_limit_per_item %} -
-

平台:{{ task.platform | platform_label }}

-

创建时间 {{ task.created_at_label }} · 阶段 {{ task.current_stage_label or "等待启动" }}

+
+
+ {{ task.platform | platform_label }} + {{ task.current_stage_label or "等待启动" }} +
+

创建时间 {{ task.created_at_label }} · 阶段 {{ task.current_stage_label or "等待启动" }}

-
+
已处理 {{ task.processed_items_count }} / 共 {{ task.total_items_count }} 条内容 {{ percent }}% @@ -31,7 +35,10 @@
-
成功 {{ task.successful_items_count }} / 失败 {{ task.failed_items_count }}
+
+ 成功 {{ task.successful_items_count }} / 失败 {{ task.failed_items_count }} + AI 成功率 {{ ai_percent }}% +
@@ -69,33 +76,40 @@ {% if task.status == "success" and (task.comments_count or 0) < target_comments %}
少于理论上限通常是内容本身评论不足或平台返回不足,不直接代表任务失败。若失败内容数大于 0,请结合失败原因判断。
{% endif %} -
- AI 成功率 {{ ai_percent }}% - {% if task.analysis_status == "insufficient" %} - AI 样本不足 - {% endif %} -
+ {% if task.analysis_status == "insufficient" %} +
AI 成功率 {{ ai_percent }}%,当前样本可能不足,报告结论请结合评论明细判断。
+ {% endif %} {% if task.error_summary %}
{{ task.error_summary }}
{% endif %} -
+
+
{% if task.status == "running" and not hotspots %} -

正在抓取热点数据,请稍候...

+

正在抓取热点数据,请稍候...

{% endif %}
{% for hotspot in hotspots %}

- 查看热点级汇总报告 -
    +
    + 内容条目 {{ hotspot.content_items | length }} + 查看汇总报告 +
    +
      {% for item in hotspot.content_items %} -
    • - {{ item.title or item.summary or item.source_item_id }} {{ item.status }} - 查看详情 +
    • +
      + + {{ item.title or item.summary or item.source_item_id }} + 状态 {{ item.status }} · ID {{ item.source_item_id }} + + 查看详情 +
    • {% endfor %}
评论内容情绪标签点赞
{{ comment.content }}{{ comment.sentiment }}{% for label in comment.labels | from_json %}{{ label }}{% else %}-{% endfor %}{{ comment.like_count or 0 }}
{{ comment.content }}{{ comment.sentiment }}{% for label in comment.labels | from_json %}{{ label }}{% else %}-{% endfor %}{{ comment.like_count or 0 }}
暂无评论数据(该内容无评论或评论抓取为空)
{{ task.display_id or loop.index }} {{ task.platform | platform_label }}{{ task.created_at_label }}{{ task.scale_label }}{{ task.platform | platform_label }}{{ task.created_at_label }}{{ task.scale_label }}
已处理 {{ task.processed_items_count }} / 共 {{ task.total_items_count }} 条内容 @@ -17,9 +17,12 @@
-
成功 {{ task.successful_items_count }} / 失败 {{ task.failed_items_count }}
+
+ 成功 {{ task.successful_items_count }} / 失败 {{ task.failed_items_count }} + 评论 {{ task.comments_count or 0 }} + 报告 {{ task.reports_count or 0 }} +
阶段:{{ task.current_stage_label or "等待启动" }}
-
评论 {{ task.comments_count or 0 }} / 报告 {{ task.reports_count or 0 }}
AI 成功率 {{ ai_percent }}% {% if task.analysis_status == "insufficient" %} @@ -33,7 +36,7 @@
{{ task.error_summary }} {% endif %}
查看查看