Files
ewoooc/templates/trends.html
OoO a9b5385615
All checks were successful
CD Pipeline / deploy (push) Successful in 1m4s
fix: 收斂 PChome 工具頁新版樣式
2026-05-17 22:13:46 +08:00

175 lines
6.2 KiB
HTML

{% extends "ewoooc_base.html" %}
{% block title %}趨勢資料 - EwoooC{% endblock %}
{% block extra_css %}
<style>
.trends-page {
color: var(--momo-text-primary);
}
.trends-table-head th {
background: var(--momo-bg-paper) !important;
color: var(--momo-text-secondary) !important;
font-family: var(--momo-font-mono, monospace);
font-size: var(--momo-text-label);
font-weight: 800;
letter-spacing: 0.04em;
}
.trends-empty {
padding: var(--momo-space-5) !important;
color: var(--momo-text-secondary) !important;
text-align: center;
}
.trends-count-badge {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 22px;
padding: 3px 8px;
background: var(--momo-tag-caramel-bg);
border: 1px solid var(--momo-tag-caramel-border);
border-radius: var(--momo-radius-sm);
color: var(--momo-tag-caramel-text);
font-family: var(--momo-font-mono, monospace);
font-size: var(--momo-text-label);
font-weight: 800;
line-height: 1;
}
</style>
{% endblock %}
{% block content %}
<div class="container-fluid py-4 trends-page">
<div class="page-header">
<h1><i class="fas fa-chart-line me-2"></i>趨勢資料</h1>
<p>Google News、PTT、Dcard、YouTube 趨勢訊號</p>
</div>
<div class="row g-3 mb-3">
<div class="col-md-3">
<select class="form-select" id="sourceFilter">
<option value="">全部來源</option>
<option value="google_news">Google News</option>
<option value="ptt">PTT</option>
<option value="dcard">Dcard</option>
<option value="youtube">YouTube</option>
</select>
</div>
<div class="col-md-3">
<input class="form-control" id="categoryFilter" placeholder="分類">
</div>
<div class="col-md-3">
<select class="form-select" id="daysFilter">
<option value="1">近 1 天</option>
<option value="7" selected>近 7 天</option>
<option value="30">近 30 天</option>
</select>
</div>
<div class="col-md-3">
<button class="btn btn-primary w-100" id="refreshBtn">
<i class="fas fa-rotate me-1"></i>更新
</button>
</div>
</div>
<div class="row g-3">
<div class="col-lg-4">
<div class="card h-100">
<div class="card-header"><i class="fas fa-fire me-2"></i>熱門關鍵字</div>
<div class="card-body" id="keywordsBox">
<div class="text-center py-4"><div class="spinner-border text-primary" role="status"></div></div>
</div>
</div>
</div>
<div class="col-lg-8">
<div class="card h-100">
<div class="card-header"><i class="fas fa-newspaper me-2"></i>趨勢記錄</div>
<div class="table-responsive">
<table class="table table-hover mb-0">
<thead class="trends-table-head">
<tr>
<th>日期</th>
<th>來源</th>
<th>分類</th>
<th>標題</th>
<th>熱度</th>
</tr>
</thead>
<tbody id="recordsBody">
<tr><td colspan="5" class="text-center py-4"><div class="spinner-border text-primary" role="status"></div></td></tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
<script>
function escapeHtml(value) {
return String(value ?? '').replace(/[&<>"']/g, char => ({
'&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;'
}[char]));
}
function filters() {
const params = new URLSearchParams();
const source = document.getElementById('sourceFilter').value;
const category = document.getElementById('categoryFilter').value.trim();
const days = document.getElementById('daysFilter').value;
if (source) params.set('source', source);
if (category) params.set('category', category);
params.set('days', days);
return params;
}
async function loadTrends() {
const params = filters();
params.set('limit', '50');
const [recordsResponse, keywordsResponse] = await Promise.all([
fetch(`/api/trends/records?${params}`),
fetch(`/api/trends/keywords?${params}`)
]);
const records = await recordsResponse.json();
const keywords = await keywordsResponse.json();
const recordsBody = document.getElementById('recordsBody');
if (!records.success || !records.data.length) {
recordsBody.innerHTML = '<tr><td colspan="5" class="trends-empty">尚無趨勢資料</td></tr>';
} else {
recordsBody.innerHTML = records.data.map(item => `
<tr>
<td>${escapeHtml(item.trend_date)}</td>
<td>${escapeHtml(item.source)}</td>
<td>${escapeHtml(item.category)}</td>
<td class="text-truncate" style="max-width: 420px;">${escapeHtml(item.title || item.keyword)}</td>
<td>${escapeHtml(item.popularity_score || '')}</td>
</tr>
`).join('');
}
const keywordsBox = document.getElementById('keywordsBox');
if (!keywords.success || !keywords.data.length) {
keywordsBox.innerHTML = '<div class="trends-empty">尚無關鍵字</div>';
} else {
keywordsBox.innerHTML = keywords.data.map(item => `
<div class="d-flex justify-content-between align-items-center border-bottom py-2">
<span>${escapeHtml(item.keyword)}</span>
<span class="trends-count-badge">${escapeHtml(item.total_mentions || 0)}</span>
</div>
`).join('');
}
}
document.getElementById('refreshBtn').addEventListener('click', loadTrends);
document.getElementById('sourceFilter').addEventListener('change', loadTrends);
document.getElementById('daysFilter').addEventListener('change', loadTrends);
document.addEventListener('DOMContentLoaded', loadTrends);
</script>
{% endblock %}