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) {
if (!value) return '—';
const match = String(value).match(/^(\d{4})-(\d{2})-(\d{2})/);
if (!match) return String(value);
const [, year, month, day] = match;
const date = new Date(Number(year), Number(month) - 1, Number(day));
return new Intl.DateTimeFormat('zh-CN', {
year: 'numeric', month: 'long', day: 'numeric', weekday: 'short',
}).format(date);
}
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 = '数据库连接正常';
else throw new Error();
} catch { $('#healthLabel').innerHTML = '数据库暂不可用'; }
}
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 `
${escapeHtml(category)}${escapeHtml(article.source_name || '未知来源')}·${escapeHtml(formatTime(article.published_at))}
${escapeHtml(summary)}
`;
}
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 = '
还没有符合条件的日报n8n 完成第一次抓取后,内容会出现在这里。
';
return;
}
$('#reportList').innerHTML = reports.map((report) => `
${escapeHtml(formatDate(report.report_date))}
${escapeHtml(report.title)}
${escapeHtml(report.introduction || '今日资讯已按主题整理。')}
${report.status === 'published' ? '已发布' : '草稿'}
${(report.articles || []).map(renderArticle).join('')}
`).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 = '正在读取日报…
';
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 = `暂时无法读取日报${escapeHtml(error.message)}
`;
}
}
$('#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();