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() { return new Date().toISOString().slice(0, 10); } 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 = '数据库连接正常'; 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(title)}

${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();