// API基础地址
const API_BASE = '';
// 当前选中的文章ID
let currentArticleId = null;
// 页面加载时初始化
document.addEventListener('DOMContentLoaded', () => {
refreshData();
loadConfig();
// 搜索框事件
document.getElementById('article-search').addEventListener('input', (e) => {
searchArticles(e.target.value);
});
// 自动刷新(每30秒)
setInterval(refreshData, 30000);
});
// 刷新所有数据
async function refreshData() {
try {
await Promise.all([
loadStats(),
loadPendingProducts(),
loadHistory(),
loadQuickStats()
]);
updateSystemStatus(true);
} catch (error) {
console.error('刷新数据失败:', error);
updateSystemStatus(false);
}
}
// 更新系统状态显示
function updateSystemStatus(isHealthy) {
const statusDot = document.getElementById('system-status');
const statusText = document.getElementById('status-text');
if (isHealthy) {
statusDot.classList.remove('error');
statusText.textContent = '系统正常';
} else {
statusDot.classList.add('error');
statusText.textContent = '系统异常';
}
}
// 加载统计数据
async function loadStats() {
try {
const response = await fetch(`${API_BASE}/api/system/stats`);
const data = await response.json();
if (data.success) {
document.getElementById('articles-count').textContent = data.stats.total_articles;
document.getElementById('pending-count').textContent = data.stats.pending_products;
document.getElementById('processing-count').textContent = data.stats.processing_products;
document.getElementById('success-count').textContent = data.stats.recent_success;
document.getElementById('failed-count').textContent = data.stats.recent_failed;
}
} catch (error) {
console.error('加载统计失败:', error);
}
}
// 加载快速统计信息
async function loadQuickStats() {
try {
// 加载文章统计
const articlesResponse = await fetch(`${API_BASE}/api/articles?limit=1000`);
const articlesData = await articlesResponse.json();
if (articlesData.success) {
document.getElementById('quick-articles-count').textContent = articlesData.count;
// 计算分类数
const categories = new Set(articlesData.articles.map(a => a.category).filter(c => c));
document.getElementById('quick-categories-count').textContent = categories.size;
// 计算今日新增
const today = new Date().toISOString().split('T')[0];
const todayCount = articlesData.articles.filter(a => a.fetch_date && a.fetch_date.startsWith(today)).length;
document.getElementById('quick-today-count').textContent = todayCount;
}
// 加载失败URL数
const failedResponse = await fetch(`${API_BASE}/api/articles/failed-urls`);
const failedData = await failedResponse.json();
if (failedData.success) {
document.getElementById('quick-failed-count').textContent = failedData.count;
}
} catch (error) {
console.error('加载快速统计失败:', error);
}
}
// 加载待处理产品
async function loadPendingProducts() {
try {
const response = await fetch(`${API_BASE}/api/products/pending`);
const data = await response.json();
const tbody = document.getElementById('pending-table');
if (data.success && data.products.length > 0) {
tbody.innerHTML = data.products.map(product => `
| ${escapeHtml(product.product_name)} |
${escapeHtml(product.category || '-')} |
${escapeHtml(product.subcategory || '-')} |
${product.priority} |
${escapeHtml(product.source)} |
${formatDate(product.created_at)} |
|
`).join('');
} else {
tbody.innerHTML = '| 暂无待处理产品 |
';
}
} catch (error) {
console.error('加载待处理产品失败:', error);
}
}
// 加载文章列表
async function loadArticles() {
try {
const response = await fetch(`${API_BASE}/api/articles?limit=50`);
const data = await response.json();
const container = document.getElementById('articles-list');
if (data.success && data.articles.length > 0) {
container.innerHTML = data.articles.map(article => `
${escapeHtml(article.product_names.join(', '))}
${escapeHtml(article.category || '未分类')}
${formatDate(article.fetch_date)}
${escapeHtml(article.source)}
`).join('');
} else {
container.innerHTML = '暂无文章
';
}
} catch (error) {
console.error('加载文章失败:', error);
}
}
// 搜索文章
async function searchArticles(keyword) {
if (!keyword.trim()) {
loadArticles();
return;
}
try {
const response = await fetch(`${API_BASE}/api/articles/search?q=${encodeURIComponent(keyword)}`);
const data = await response.json();
const container = document.getElementById('articles-list');
if (data.success && data.articles.length > 0) {
container.innerHTML = data.articles.map(article => `
${escapeHtml(article.product_names.join(', '))}
${escapeHtml(article.category || '未分类')}
${formatDate(article.fetch_date)}
${escapeHtml(article.source)}
`).join('');
} else {
container.innerHTML = '未找到相关文章
';
}
} catch (error) {
console.error('搜索文章失败:', error);
}
}
// 加载处理历史
async function loadHistory() {
try {
const response = await fetch(`${API_BASE}/api/products/history?limit=20`);
const data = await response.json();
const tbody = document.getElementById('history-table');
if (data.success && data.history.length > 0) {
tbody.innerHTML = data.history.map(item => `
| ${escapeHtml(item.product_name)} |
${escapeHtml(item.category || '-')} |
${escapeHtml(item.subcategory || '-')} |
${getStatusText(item.status)} |
${escapeHtml(item.review_id || '-')} |
${formatDate(item.submitted_at)} |
|
`).join('');
} else {
tbody.innerHTML = '| 暂无处理历史 |
';
}
} catch (error) {
console.error('加载处理历史失败:', error);
}
}
// 加载系统配置
async function loadConfig() {
try {
const response = await fetch(`${API_BASE}/api/system/config`);
const data = await response.json();
if (data.success) {
document.getElementById('auto-process-enabled').value = data.config.auto_process_enabled;
document.getElementById('process-interval').value = data.config.process_interval;
document.getElementById('batch-size').value = data.config.batch_size;
}
} catch (error) {
console.error('加载配置失败:', error);
}
}
// 保存配置
async function saveConfig() {
const config = {
auto_process_enabled: document.getElementById('auto-process-enabled').value,
process_interval: document.getElementById('process-interval').value,
batch_size: document.getElementById('batch-size').value
};
try {
const response = await fetch(`${API_BASE}/api/system/config`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(config)
});
const data = await response.json();
if (data.success) {
showToast('配置已保存', 'success');
} else {
showToast('保存失败: ' + data.error, 'error');
}
} catch (error) {
showToast('保存配置失败', 'error');
}
}
// 显示添加产品模态框
function showAddProductModal() {
document.getElementById('add-product-modal').classList.add('active');
document.getElementById('add-product-form').reset();
}
// 显示添加文章模态框
function showAddArticleModal() {
document.getElementById('add-article-modal').classList.add('active');
document.getElementById('add-article-form').reset();
}
// 关闭模态框
function closeModal(modalId) {
document.getElementById(modalId).classList.remove('active');
}
// 添加产品
async function addProduct() {
const batchProducts = document.getElementById('batch-products').value.trim();
let productsData;
if (batchProducts) {
// 批量添加
try {
productsData = JSON.parse(batchProducts);
} catch (e) {
showToast('JSON格式错误', 'error');
return;
}
} else {
// 单个添加
const productName = document.getElementById('product-name').value.trim();
if (!productName) {
showToast('请输入产品名称', 'error');
return;
}
productsData = {
product_name: productName,
category: document.getElementById('product-category').value.trim(),
subcategory: document.getElementById('product-subcategory').value.trim(),
priority: parseInt(document.getElementById('product-priority').value),
source: 'manual'
};
}
try {
const response = await fetch(`${API_BASE}/api/products/pending`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(productsData)
});
const data = await response.json();
if (data.success) {
showToast(`成功添加 ${data.added_count || 1} 个产品`, 'success');
closeModal('add-product-modal');
loadPendingProducts();
loadStats();
} else {
showToast('添加失败: ' + data.error, 'error');
}
} catch (error) {
showToast('添加产品失败', 'error');
}
}
// 添加文章
async function addArticle() {
const products = document.getElementById('article-products').value.trim();
const summary = document.getElementById('article-summary').value.trim();
const content = document.getElementById('article-content').value.trim();
const source = document.getElementById('article-source').value.trim();
if (!products || !summary || !content || !source) {
showToast('请填写必填字段', 'error');
return;
}
const articleData = {
product_names: products.split(',').map(p => p.trim()),
category: document.getElementById('article-category').value.trim(),
keywords: document.getElementById('article-keywords').value.split(',').map(k => k.trim()),
summary: summary,
content: content,
source: source,
url: document.getElementById('article-url').value.trim()
};
try {
const response = await fetch(`${API_BASE}/api/articles`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(articleData)
});
const data = await response.json();
if (data.success) {
showToast('文章添加成功', 'success');
closeModal('add-article-modal');
loadArticles();
loadStats();
} else {
showToast('添加失败: ' + data.error, 'error');
}
} catch (error) {
showToast('添加文章失败', 'error');
}
}
// 处理单个产品
async function processProduct(productName, category, subcategory) {
if (!confirm(`确定要处理产品 "${productName}" 吗?`)) {
return;
}
try {
const response = await fetch(`${API_BASE}/api/products/process`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
product_name: productName,
category: category,
subcategory: subcategory
})
});
const data = await response.json();
if (data.success) {
showToast(data.message, 'success');
if (data.new_products && data.new_products.length > 0) {
showToast(`发现 ${data.new_products.length} 个新产品`, 'success');
}
} else {
showToast('处理失败: ' + data.error || data.message, 'error');
}
refreshData();
} catch (error) {
showToast('处理产品失败', 'error');
}
}
// 批量处理
async function processBatch() {
if (!confirm('确定要批量处理待处理产品吗?')) {
return;
}
try {
const response = await fetch(`${API_BASE}/api/products/process/batch`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ limit: 5 })
});
const data = await response.json();
if (data.success) {
showToast(`已处理 ${data.processed} 个产品`, 'success');
} else {
showToast('批量处理失败', 'error');
}
refreshData();
} catch (error) {
showToast('批量处理失败', 'error');
}
}
// 移除待处理产品
async function removeProduct(productName) {
if (!confirm(`确定要从待处理列表移除 "${productName}" 吗?`)) {
return;
}
try {
const response = await fetch(`${API_BASE}/api/products/pending/${encodeURIComponent(productName)}`, {
method: 'DELETE'
});
const data = await response.json();
if (data.success) {
showToast('产品已移除', 'success');
loadPendingProducts();
loadStats();
} else {
showToast('移除失败: ' + data.error, 'error');
}
} catch (error) {
showToast('移除产品失败', 'error');
}
}
// 显示文章详情
async function showArticleDetail(articleId) {
currentArticleId = articleId;
try {
const response = await fetch(`${API_BASE}/api/articles/${articleId}`);
const data = await response.json();
if (data.success) {
const article = data.article;
const body = document.getElementById('article-detail-body');
body.innerHTML = `
摘要总结
${escapeHtml(article.summary)}
具体内容
${escapeHtml(article.content)}
`;
document.getElementById('article-detail-modal').classList.add('active');
}
} catch (error) {
showToast('加载文章详情失败', 'error');
}
}
// 删除文章
async function deleteArticle() {
if (!currentArticleId) {
return;
}
if (!confirm('确定要删除这篇文章吗?')) {
return;
}
try {
const response = await fetch(`${API_BASE}/api/articles/${currentArticleId}`, {
method: 'DELETE'
});
const data = await response.json();
if (data.success) {
showToast('文章已删除', 'success');
closeModal('article-detail-modal');
loadArticles();
loadStats();
} else {
showToast('删除失败: ' + data.error, 'error');
}
} catch (error) {
showToast('删除文章失败', 'error');
}
}
// 显示处理详情
function showProcessDetail(details) {
const body = document.getElementById('process-detail-body');
if (details) {
if (details.error) {
body.innerHTML = `
错误信息
${escapeHtml(details.error)}
`;
} else if (details.data) {
body.innerHTML = `
提交数据
${JSON.stringify(details.data, null, 2)}
`;
} else {
body.innerHTML = '无详细信息
';
}
} else {
body.innerHTML = '无详细信息
';
}
document.getElementById('process-detail-modal').classList.add('active');
}
// 显示提示消息
function showToast(message, type = '') {
const toast = document.getElementById('toast');
toast.textContent = message;
toast.className = `toast active ${type}`;
setTimeout(() => {
toast.classList.remove('active');
}, 3000);
}
// 辅助函数:HTML转义
function escapeHtml(text) {
if (!text) return '';
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// 辅助函数:日期格式化
function formatDate(dateString) {
if (!dateString) return '-';
const date = new Date(dateString);
return date.toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
});
}
// 辅助函数:获取优先级样式类
function getPriorityClass(priority) {
if (priority >= 8) return 'priority-high';
if (priority >= 5) return 'priority-medium';
return 'priority-low';
}
// 辅助函数:获取状态文本
function getStatusText(status) {
const statusMap = {
'submitted': '已提交',
'processing': '处理中',
'failed': '失败',
'error': '错误',
'completed': '已完成'
};
return statusMap[status] || status;
}
// ========== 互联网搜索功能 ==========
let currentSearchResult = null;
// 执行互联网搜索
async function doInternetSearch() {
const keyword = document.getElementById('internet-search-keyword').value.trim();
const maxResults = parseInt(document.getElementById('internet-search-count').value) || 10;
if (!keyword) {
showToast('请输入搜索关键词', 'error');
return;
}
// 显示搜索状态
const statusDiv = document.getElementById('internet-search-status');
const resultsDiv = document.getElementById('internet-search-results');
const searchBtn = document.getElementById('search-btn');
statusDiv.innerHTML = ' 正在搜索...';
resultsDiv.innerHTML = '搜索中...
';
searchBtn.disabled = true;
try {
const response = await fetch(`${API_BASE}/api/articles/internet-search`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ keyword, max_results: maxResults })
});
const data = await response.json();
if (data.success) {
statusDiv.innerHTML = ` 搜索完成,找到 ${data.count} 条结果`;
if (data.results.length > 0) {
resultsDiv.innerHTML = data.results.map((r, i) => `
${i + 1}
${escapeHtml(r.title)}
来源: ${escapeHtml(r.source)}
`).join('');
// 存储搜索结果供后续使用
window.lastSearchResults = data.results;
} else {
resultsDiv.innerHTML = '未找到相关结果
';
}
} else {
statusDiv.innerHTML = ` 搜索失败: ${escapeHtml(data.error)}`;
resultsDiv.innerHTML = '搜索失败
';
}
} catch (error) {
statusDiv.innerHTML = ' 搜索出错';
resultsDiv.innerHTML = '搜索出错,请稍后重试
';
console.error('搜索错误:', error);
}
searchBtn.disabled = false;
}
// 显示搜索结果详情
function showSearchResultDetail(index) {
if (!window.lastSearchResults || !window.lastSearchResults[index]) {
return;
}
currentSearchResult = window.lastSearchResults[index];
const body = document.getElementById('search-result-body');
body.innerHTML = `
提示
点击"抓取内容"按钮可以获取页面详细内容,然后保存到内容库。
`;
document.getElementById('search-result-modal').classList.add('active');
}
// 抓取并显示搜索结果内容
async function fetchAndShowResult(index) {
if (!window.lastSearchResults || !window.lastSearchResults[index]) {
return;
}
const result = window.lastSearchResults[index];
currentSearchResult = result;
showToast('正在抓取页面内容...', '');
try {
const response = await fetch(`${API_BASE}/api/articles/fetch`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
url: result.url,
product_names: [result.title],
category: ''
})
});
const data = await response.json();
if (data.success) {
// 更新当前搜索结果,添加抓取的内容
currentSearchResult = {
...result,
fetched: true,
fetchedContent: data.data
};
const body = document.getElementById('search-result-body');
body.innerHTML = `
页面内容
${escapeHtml(data.data.content.substring(0, 2000))}${data.data.content.length > 2000 ? '\n... (内容过长,已截断)' : ''}
`;
document.getElementById('search-result-modal').classList.add('active');
showToast('内容抓取成功', 'success');
} else {
showToast('抓取失败: ' + data.error, 'error');
}
} catch (error) {
showToast('抓取出错', 'error');
console.error('抓取错误:', error);
}
}
// 快速保存搜索结果到内容库
async function quickSaveResult(index) {
if (!window.lastSearchResults || !window.lastSearchResults[index]) {
return;
}
const result = window.lastSearchResults[index];
// 先抓取内容再保存
showToast('正在抓取并保存...', '');
try {
const response = await fetch(`${API_BASE}/api/articles/fetch`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
url: result.url,
product_names: [result.title],
category: ''
})
});
const data = await response.json();
if (data.success) {
showToast('已保存到内容库', 'success');
loadArticles();
loadStats();
} else {
showToast('保存失败: ' + data.error, 'error');
}
} catch (error) {
showToast('保存出错', 'error');
}
}
// 从详情模态框保存到内容库
async function saveSearchResultToLibrary() {
if (!currentSearchResult) {
return;
}
// 如果还没有抓取内容,先抓取
if (!currentSearchResult.fetched) {
await fetchAndShowResult(window.lastSearchResults.findIndex(r => r.url === currentSearchResult.url));
return;
}
showToast('已保存到内容库', 'success');
closeModal('search-result-modal');
loadArticles();
loadStats();
}