function renderArticleList() { const container = document.getElementById('articleList'); if (allArticles.length === 0) { container.innerHTML = '
📰
暂无文章
'; return; } container.innerHTML = allArticles.map((a, i) => ` `).join(''); } function toggleArticle(checkbox) { const path = checkbox.dataset.path; if (checkbox.checked) { if (!selectedArticles.includes(path)) selectedArticles.push(path); } else { selectedArticles = selectedArticles.filter(p => p !== path); } updateSelectedCount(); } function selectAll() { allArticles.forEach(a => { if (!selectedArticles.includes(a.path)) selectedArticles.push(a.path); }); document.querySelectorAll('.article-checkbox').forEach(cb => cb.checked = true); updateSelectedCount(); showToast(`已选择 ${selectedArticles.length} 篇文章`, 'success'); } function clearSelection() { selectedArticles = []; document.querySelectorAll('.article-checkbox').forEach(cb => cb.checked = false); updateSelectedCount(); showToast('已清除选择', 'info'); } function updateSelectedCount() { document.getElementById('selectedCount').textContent = selectedArticles.length; const nextBtn = document.getElementById('nextToBriefing'); nextBtn.disabled = selectedArticles.length === 0; nextBtn.innerHTML = selectedArticles.length === 0 ? '请选择文章' : `已选 ${selectedArticles.length} 篇,继续`; } function renderPagination() { const container = document.getElementById('pagination'); if (!container || totalPages <= 1) { if (container) container.innerHTML = ''; return; } container.innerHTML = `
第 ${currentPage} / ${totalPages} 页
`; } function updateSelectedInfo() { const info = document.getElementById('selectedArticlesInfo'); if (info) info.textContent = `${selectedArticles.length} 篇文章`; } // Generate briefing function generateBriefing() { if (selectedArticles.length === 0) { showToast('请先选择文章', 'warning'); return; } const btn = document.getElementById('generateBtn'); const output = document.getElementById('briefingOutput'); const progress = document.getElementById('briefingProgress'); const status = document.getElementById('briefingStatus'); btn.disabled = true; btn.classList.add('loading'); btn.innerHTML = '生成中...'; progress.classList.remove('hidden'); output.innerHTML = '
正在生成简报...
'; status.textContent = '正在调用 AI 模型生成简报...'; fetch('/api/briefing/generate', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ article_paths: selectedArticles, language: document.getElementById('outputLang').value, style: document.getElementById('briefingStyle').value, show_source: document.getElementById('showSource').checked }) }) .then(r => r.json()) .then(data => { btn.disabled = false; btn.classList.remove('loading'); btn.innerHTML = '✨ 生成完成'; progress.querySelector('.progress-fill').style.width = '100%'; if (data.success) { currentBriefing = data.data?.content || ''; output.innerHTML = '
' + formatMarkdown(currentBriefing) + '
'; status.innerHTML = '✓ 生成成功'; document.getElementById('nextToTTS').style.display = 'inline-flex'; showToast('简报生成成功', 'success'); } else { output.innerHTML = '
生成失败:' + (data.error || '未知错误') + '
'; status.innerHTML = '✗ 生成失败'; showToast('生成失败:' + data.error, 'error'); } }) .catch(err => { btn.disabled = false; btn.classList.remove('loading'); output.innerHTML = '
错误:' + err.message + '
'; status.innerHTML = '✗ 错误'; showToast('网络错误:' + err.message, 'error'); }); } function formatMarkdown(text) { return text .replace(/^### (.*$)/gim, '

$1

') .replace(/^## (.*$)/gim, '

$1

') .replace(/^# (.*$)/gim, '

$1

') .replace(/\*\*(.*?)\*\*/gim, '$1') .replace(/\*(.*?)\*/gim, '$1') .replace(/^- (.*$)/gim, '
  • $1
  • ') .replace(/\n/gim, '
    '); } function exportBriefing() { if (!currentBriefing) { showToast('没有可导出的内容', 'warning'); return; } const blob = new Blob([currentBriefing], { type: 'text/markdown' }); const a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = 'briefing_' + new Date().toISOString().split('T')[0] + '.md'; a.click(); showToast('导出成功', 'success'); } // TTS function loadTtsEngines() { fetch('/api/tts/engines') .then(r => r.json()) .then(data => { const select = document.getElementById('ttsEngine'); select.innerHTML = ''; (data.engines || []).forEach(e => { const opt = document.createElement('option'); opt.value = e.name; opt.textContent = e.name + (e.offline ? ' (离线)' : ''); select.appendChild(opt); }); if (select.options.length === 0) { select.innerHTML = ''; } }); } function loadVoices() { // Load voices for selected engine if needed } function generateAudio() { const text = document.getElementById('ttsContent').value.trim(); if (!text) { showToast('请输入播报内容', 'warning'); return; } const btn = document.getElementById('ttsBtn'); const status = document.getElementById('ttsStatus'); btn.disabled = true; btn.classList.add('loading'); status.textContent = '正在生成音频...'; fetch('/api/tts', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ text: text, engine: document.getElementById('ttsEngine').value, language: document.getElementById('outputLang').value, rate: parseFloat(document.getElementById('ttsSpeed').value) }) }) .then(r => r.json()) .then(data => { btn.disabled = false; btn.classList.remove('loading'); if (data.success) { currentAudioPath = data.data?.audio_path || ''; document.getElementById('audioPlayer').src = '/api/audio?path=' + encodeURIComponent(currentAudioPath); document.getElementById('playBtn').disabled = false; document.getElementById('saveBtn').disabled = false; status.innerHTML = '✓ 音频生成成功'; showToast('音频生成成功', 'success'); } else { status.innerHTML = '✗ 生成失败'; showToast('生成失败:' + data.error, 'error'); } }) .catch(err => { btn.disabled = false; status.innerHTML = '✗ 错误'; showToast('网络错误:' + err.message, 'error'); }); } function playAudio() { const player = document.getElementById('audioPlayer'); if (player.src) { player.play(); } else { showToast('请先生成音频', 'warning'); } } function saveAudio() { const currentPath = currentAudioPath; if (!currentPath) { showToast('请先生成音频', 'warning'); return; } const link = document.createElement('a'); link.href = '/api/audio?path=' + encodeURIComponent(currentPath); link.download = 'briefing_' + new Date().toISOString().split('T')[0] + '.mp3'; link.click(); showToast('保存成功', 'success'); } function startOver() { selectedArticles = []; currentBriefing = ''; currentAudioPath = ''; document.getElementById('briefingOutput').innerHTML = '
    📝
    点击"生成简报"开始...
    '; document.getElementById('ttsContent').value = ''; document.getElementById('audioPlayer').src = ''; document.getElementById('playBtn').disabled = true; document.getElementById('saveBtn').disabled = true; document.getElementById('nextToTTS').style.display = 'none'; goToStep(1); showToast('已重新开始', 'info'); } // Utility function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } // Initialize document.addEventListener('DOMContentLoaded', function() { // Load initial models loadModels(); // Set default dates const today = new Date().toISOString().split('T')[0]; const lastWeek = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString().split('T')[0]; document.getElementById('dateFrom').value = lastWeek; document.getElementById('dateTo').value = today; // Keyboard shortcuts document.addEventListener('keydown', function(e) { if (e.ctrlKey || e.metaKey) { if (e.key === 'Enter') { e.preventDefault(); if (currentStep < 5) goToStep(currentStep + 1); } } }); });