feat: 提交热榜评论分析工具 MVP 基线
This commit is contained in:
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>{% block title %}热榜评论分析工具{% endblock %}</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="/static/app.css" rel="stylesheet">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="/static/app.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-lg bg-body-tertiary border-bottom">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="/">热榜评论分析工具</a>
|
||||
<div class="navbar-nav">
|
||||
<a class="nav-link" href="/">任务列表</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<main class="container py-4">
|
||||
{% block breadcrumbs %}
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item active" aria-current="page">首页</li>
|
||||
</ol>
|
||||
</nav>
|
||||
{% endblock %}
|
||||
{% block content %}{% endblock %}
|
||||
</main>
|
||||
<footer class="border-top py-3">
|
||||
<div class="container text-muted small">内部演示工具 | 仅供学习参考</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,26 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}{{ hotspot.title }} 汇总报告 - 热榜评论分析工具{% endblock %}
|
||||
{% block breadcrumbs %}
|
||||
<nav aria-label="breadcrumb"><ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="/">首页</a></li>
|
||||
<li class="breadcrumb-item"><a href="/tasks/{{ task.id }}">任务 #{{ task.id }}</a></li>
|
||||
<li class="breadcrumb-item active">汇总报告</li>
|
||||
</ol></nav>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h1 class="h3 mb-0">{{ hotspot.title }} 汇总报告</h1>
|
||||
<div class="d-flex gap-2">
|
||||
<a class="btn btn-outline-primary" href="/api/export/hotspots/{{ hotspot.id }}.md">导出 Markdown 报告</a>
|
||||
<a class="btn btn-outline-secondary" href="/api/export/hotspots/{{ hotspot.id }}/comments.csv">导出热点评论 CSV</a>
|
||||
</div>
|
||||
</div>
|
||||
{% if report %}
|
||||
<section class="card"><div class="card-body">
|
||||
<p class="lead">{{ report.summary }}</p>
|
||||
<pre class="bg-light p-3">{{ report.markdown_content }}</pre>
|
||||
</div></section>
|
||||
{% else %}
|
||||
<div class="alert alert-secondary">报告生成中,请稍候...</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,66 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}任务列表 - 热榜评论分析工具{% endblock %}
|
||||
{% block content %}
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h1 class="h3 mb-0">任务列表</h1>
|
||||
<button class="btn btn-outline-secondary btn-sm" onclick="window.location.href='/'">手动刷新</button>
|
||||
</div>
|
||||
|
||||
<section class="card mb-4">
|
||||
<div class="card-header">创建抓取任务</div>
|
||||
<div class="card-body">
|
||||
<div id="form-error" class="alert alert-danger d-none" role="alert"></div>
|
||||
<form id="task-form" onsubmit="submitTask(event)">
|
||||
<div class="row g-3 align-items-end">
|
||||
<div class="col-md-3">
|
||||
<label class="form-label" for="platform">平台</label>
|
||||
<select class="form-select" id="platform" name="platform">
|
||||
<option value="xiaohongshu">小红书</option>
|
||||
<option value="douyin">抖音</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label" for="hotspot_limit">热点数</label>
|
||||
<input class="form-control scale-input" id="hotspot_limit" name="hotspot_limit" type="number" min="1" max="10" value="5">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label" for="item_limit_per_hotspot">每热点内容</label>
|
||||
<input class="form-control scale-input" id="item_limit_per_hotspot" name="item_limit_per_hotspot" type="number" min="1" max="10" value="5">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label" for="comment_limit_per_item">每内容评论</label>
|
||||
<input class="form-control scale-input" id="comment_limit_per_item" name="comment_limit_per_item" type="number" min="10" max="100" value="50">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<button class="btn btn-primary w-100" id="submit-btn" type="submit">开始抓取</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-text text-muted mt-2" id="scale-hint">
|
||||
预计最多抓取:<strong id="scale-calc">1250</strong> 条评论(实际数量可能受平台返回数量、去重、失败、限流影响)
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="card">
|
||||
<div class="card-header">历史任务</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>任务 ID</th>
|
||||
<th>平台</th>
|
||||
<th>创建时间</th>
|
||||
<th>配置规模</th>
|
||||
<th>进度</th>
|
||||
<th>状态</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="task-table-body">
|
||||
{% include "partials/task_rows.html" %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,34 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}{{ item.title or item.source_item_id }} 详情 - 热榜评论分析工具{% endblock %}
|
||||
{% block breadcrumbs %}
|
||||
<nav aria-label="breadcrumb"><ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="/">首页</a></li>
|
||||
<li class="breadcrumb-item"><a href="/tasks/{{ task.id }}">任务 #{{ task.id }}</a></li>
|
||||
<li class="breadcrumb-item"><a href="/hotspots/{{ hotspot.id }}/report">热点 #{{ hotspot.rank }}:{{ hotspot.title }}</a></li>
|
||||
<li class="breadcrumb-item active">{{ item.title or item.source_item_id }}</li>
|
||||
</ol></nav>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h1 class="h3 mb-0">{{ item.title or item.source_item_id }}</h1>
|
||||
<div class="d-flex gap-2">
|
||||
<a class="btn btn-outline-primary" href="/api/export/items/{{ item.id }}.md">导出 Markdown 报告</a>
|
||||
<a class="btn btn-outline-secondary" href="/api/export/items/{{ item.id }}/comments.csv">导出评论 CSV</a>
|
||||
</div>
|
||||
</div>
|
||||
{% if report %}
|
||||
<section class="card mb-4"><div class="card-body">
|
||||
<p class="lead">{{ report.summary }}</p>
|
||||
<pre class="bg-light p-3">{{ report.markdown_content }}</pre>
|
||||
</div></section>
|
||||
{% endif %}
|
||||
<section class="card"><div class="card-header">评论明细</div><div class="table-responsive">
|
||||
<table class="table mb-0"><thead><tr><th>评论内容</th><th>情绪</th><th>标签</th><th>点赞</th></tr></thead><tbody>
|
||||
{% for comment in comments %}
|
||||
<tr><td>{{ comment.content }}</td><td>{{ comment.sentiment }}</td><td>{% for label in comment.labels | from_json %}<span class="badge bg-secondary me-1">{{ label }}</span>{% else %}-{% endfor %}</td><td>{{ comment.like_count or 0 }}</td></tr>
|
||||
{% else %}
|
||||
<tr><td colspan="4" class="text-center text-muted">暂无评论数据(该内容无评论或评论抓取为空)</td></tr>
|
||||
{% endfor %}
|
||||
</tbody></table>
|
||||
</div></section>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,7 @@
|
||||
{% macro label_tags(labels_json) %}
|
||||
{% for label in labels_json | from_json %}
|
||||
<span class="badge bg-secondary me-1">{{ label }}</span>
|
||||
{% else %}
|
||||
<span class="text-muted">-</span>
|
||||
{% endfor %}
|
||||
{% endmacro %}
|
||||
@@ -0,0 +1,10 @@
|
||||
{% macro sentiment_badge(sentiment) %}
|
||||
{% set config = {
|
||||
"positive": ("正向", "bg-success"),
|
||||
"neutral": ("中性", "bg-warning text-dark"),
|
||||
"negative": ("负向", "bg-danger"),
|
||||
"unknown": ("未知", "bg-secondary"),
|
||||
} %}
|
||||
{% set label, style = config.get(sentiment, ("未知", "bg-secondary")) %}
|
||||
<span class="badge {{ style }}">{{ label }}</span>
|
||||
{% endmacro %}
|
||||
@@ -0,0 +1,4 @@
|
||||
{% macro status_badge(status) %}
|
||||
{% set label, style = status_badge_config(status) %}
|
||||
<span class="badge {{ style }}">{{ label }}</span>
|
||||
{% endmacro %}
|
||||
@@ -0,0 +1,21 @@
|
||||
{% for task in tasks %}
|
||||
{% set status_label, status_class = status_badge_config(task.status) %}
|
||||
<tr>
|
||||
<td><code>{{ task.id }}</code></td>
|
||||
<td>{{ task.platform | platform_label }}</td>
|
||||
<td>{{ task.created_at }}</td>
|
||||
<td><small class="text-muted">热点 {{ task.hotspot_limit }} / 内容 {{ task.item_limit_per_hotspot }} / 评论 {{ task.comment_limit_per_item }}</small></td>
|
||||
<td>{{ task.successful_items_count }} / {{ task.total_items_count }}</td>
|
||||
<td>
|
||||
<span class="badge {{ status_class }}">{{ status_label }}</span>
|
||||
{% if task.error_stage or task.error_type %}
|
||||
<br><small class="text-muted">{{ task.error_stage }} / {{ task.error_type }}</small>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td><a class="btn btn-sm btn-outline-primary" href="/tasks/{{ task.id }}">查看</a></td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="7" class="text-center text-muted py-4">还没有任何任务,请在上方创建第一个任务</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
@@ -0,0 +1,47 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}任务 #{{ task.id }} - 热榜评论分析工具{% endblock %}
|
||||
{% block breadcrumbs %}
|
||||
<nav aria-label="breadcrumb"><ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="/">首页</a></li>
|
||||
<li class="breadcrumb-item active">任务 #{{ task.id }}</li>
|
||||
</ol></nav>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h1 class="h3 mb-0">任务 #{{ task.id }}</h1>
|
||||
<button class="btn btn-outline-secondary btn-sm" onclick="window.location.href='/'">手动刷新</button>
|
||||
</div>
|
||||
<section class="card mb-4"><div class="card-body">
|
||||
{% set label, cls = status_badge_config(task.status) %}
|
||||
<p>平台:{{ task.platform | platform_label }} <span class="badge {{ cls }}">{{ label }}</span></p>
|
||||
<p>成功 {{ task.successful_items_count }} / 共 {{ task.total_items_count }} 条内容</p>
|
||||
{% if task.error_message %}<div class="alert alert-danger">{{ task.error_stage }} / {{ task.error_type }}:{{ task.error_message }}</div>{% endif %}
|
||||
</div></section>
|
||||
{% if task.status == "running" and not hotspots %}
|
||||
<div class="text-center text-muted py-5"><div class="spinner-border text-warning mb-3"></div><p>正在抓取热点数据,请稍候...</p></div>
|
||||
{% endif %}
|
||||
<div class="accordion" id="hotspot-list">
|
||||
{% for hotspot in hotspots %}
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button {% if not loop.first %}collapsed{% endif %}" type="button" data-bs-toggle="collapse" data-bs-target="#hotspot-{{ hotspot.id }}">
|
||||
热点 #{{ hotspot.rank }}:{{ hotspot.title }}
|
||||
</button>
|
||||
</h2>
|
||||
<div id="hotspot-{{ hotspot.id }}" class="accordion-collapse collapse {% if loop.first %}show{% endif %}" data-bs-parent="#hotspot-list">
|
||||
<div class="accordion-body">
|
||||
<a class="btn btn-sm btn-outline-primary mb-2" href="/hotspots/{{ hotspot.id }}/report">查看热点级汇总报告</a>
|
||||
<ul class="list-group">
|
||||
{% for item in hotspot.content_items %}
|
||||
<li class="list-group-item d-flex justify-content-between">
|
||||
<span>{{ item.title or item.summary or item.source_item_id }} <small class="text-muted">{{ item.status }}</small></span>
|
||||
<a class="btn btn-sm btn-outline-secondary" href="/items/{{ item.id }}">查看详情</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user