79 lines
4.5 KiB
JavaScript
79 lines
4.5 KiB
JavaScript
const $ = (selector) => document.querySelector(selector);
|
|
const escapeHtml = (value) => String(value ?? '').replace(/[&<>'"]/g, (char) => ({ '&': '&', '<': '<', '>': '>', "'": ''', '"': '"' }[char]));
|
|
const categoryNames = { general: '综合', ai_agent: 'AI 与 Agent', agri_hardware: '农业硬件', agri_solutions: '方案落地', agri_services: '农业服务', policy_market: '政策市场' };
|
|
|
|
function today() {
|
|
const now = new Date();
|
|
const year = now.getFullYear();
|
|
const month = String(now.getMonth() + 1).padStart(2, '0');
|
|
const day = String(now.getDate()).padStart(2, '0');
|
|
return `${year}-${month}-${day}`;
|
|
}
|
|
function formatDate(value) { return value ? new Intl.DateTimeFormat('zh-CN', { year: 'numeric', month: 'long', day: 'numeric', weekday: 'short' }).format(new Date(`${value}T00:00:00`)) : '—'; }
|
|
function formatTime(value) { return value ? new Intl.DateTimeFormat('zh-CN', { month: 'short', day: 'numeric' }).format(new Date(value)) : '近期'; }
|
|
|
|
async function loadHealth() {
|
|
try {
|
|
const response = await fetch('/api/health');
|
|
const label = $('#healthLabel');
|
|
if (response.ok) label.innerHTML = '<span class="status-dot"></span>数据库连接正常';
|
|
else throw new Error();
|
|
} catch { $('#healthLabel').innerHTML = '<span class="status-dot error"></span>数据库暂不可用'; }
|
|
}
|
|
|
|
function renderArticle(article) {
|
|
const title = article.translated_title || article.title || '无标题';
|
|
const summary = article.translated_summary || article.summary || '暂无摘要';
|
|
const category = categoryNames[article.section] || categoryNames[article.category] || '综合';
|
|
return `<article class="article">
|
|
<div class="article-meta"><span class="tag">${escapeHtml(category)}</span><span>${escapeHtml(article.source_name || '未知来源')}</span><span>·</span><span>${escapeHtml(formatTime(article.published_at))}</span></div>
|
|
<h3><a href="${escapeHtml(article.url)}" target="_blank" rel="noreferrer">${escapeHtml(title)}</a></h3>
|
|
<p>${escapeHtml(summary)}</p>
|
|
</article>`;
|
|
}
|
|
|
|
function renderReports(payload) {
|
|
$('#reportCount').textContent = payload.total ?? 0;
|
|
const reports = payload.reports || [];
|
|
const articleCount = reports.reduce((sum, report) => sum + (report.articles || []).length, 0);
|
|
$('#articleCount').textContent = articleCount;
|
|
$('#sourceCount').textContent = new Set(reports.flatMap((report) => (report.articles || []).map((item) => item.source_name).filter(Boolean))).size;
|
|
if (!reports.length) {
|
|
$('#reportList').innerHTML = '<div class="empty-state"><strong>还没有符合条件的日报</strong><span>n8n 完成第一次抓取后,内容会出现在这里。</span></div>';
|
|
return;
|
|
}
|
|
$('#reportList').innerHTML = reports.map((report) => `<article class="report-card card">
|
|
<div class="report-top"><div><div class="report-date">${escapeHtml(formatDate(report.report_date))}</div><h2>${escapeHtml(report.title)}</h2><p class="report-intro">${escapeHtml(report.introduction || '今日资讯已按主题整理。')}</p></div><span class="report-status">${report.status === 'published' ? '已发布' : '草稿'}</span></div>
|
|
<div class="article-grid">${(report.articles || []).map(renderArticle).join('')}</div>
|
|
</article>`).join('');
|
|
}
|
|
|
|
async function loadReports() {
|
|
const params = new URLSearchParams();
|
|
const date = $('#dateInput').value;
|
|
const q = $('#searchInput').value.trim();
|
|
const category = $('#categoryInput').value;
|
|
if (date) params.set('date', date);
|
|
if (q) params.set('q', q);
|
|
if (category) params.set('category', category);
|
|
$('#reportList').innerHTML = '<div class="empty-state"><span>正在读取日报…</span></div>';
|
|
try {
|
|
const response = await fetch(`/api/reports?${params}`);
|
|
const payload = await response.json();
|
|
if (!response.ok) throw new Error(payload.error || '读取失败');
|
|
renderReports(payload);
|
|
} catch (error) {
|
|
$('#reportList').innerHTML = `<div class="empty-state"><strong>暂时无法读取日报</strong><span>${escapeHtml(error.message)}</span></div>`;
|
|
}
|
|
}
|
|
|
|
$('#todayLabel').textContent = formatDate(today());
|
|
$('#dateInput').value = today();
|
|
$('#dateInput').addEventListener('change', loadReports);
|
|
$('#categoryInput').addEventListener('change', loadReports);
|
|
let searchTimer;
|
|
$('#searchInput').addEventListener('input', () => { clearTimeout(searchTimer); searchTimer = setTimeout(loadReports, 280); });
|
|
$('#clearFilters').addEventListener('click', () => { $('#dateInput').value = ''; $('#categoryInput').value = ''; $('#searchInput').value = ''; loadReports(); });
|
|
loadHealth();
|
|
loadReports();
|