@@ -0,0 +1,155 @@
|
||||
// src/js/tools/baiduHotTool.js
|
||||
import BaseTool from '../baseTool.js';
|
||||
|
||||
class BaiduHotTool extends BaseTool {
|
||||
constructor() {
|
||||
super('baidu-hot', '百度热搜');
|
||||
this.abortController = null;
|
||||
// 定义分榜信息
|
||||
this.boards = [
|
||||
{ id: 'hot-search', name: '热搜榜' },
|
||||
{ id: 'hot-meme', name: '热梗榜' },
|
||||
{ id: 'finance', name: '财经榜' },
|
||||
{ id: 'livelihood', name: '民生榜' },
|
||||
];
|
||||
}
|
||||
|
||||
render() {
|
||||
return `
|
||||
<div class="page-container" style="display: flex; flex-direction: column; height: 100%;">
|
||||
<div class="section-header">
|
||||
<button id="back-to-toolbox-btn" class="back-btn ripple"><i class="fas fa-arrow-left"></i> 返回工具箱</button>
|
||||
<h1 style="flex-grow: 1; text-align: center;">${this.name}</h1>
|
||||
</div>
|
||||
<div class="sys-info-tabs baidu-hot-tabs" id="baidu-hot-tabs">
|
||||
${this.boards.map(board => `
|
||||
<button class="sys-info-tab" data-board-name="${board.name}">${board.name}</button>
|
||||
`).join('')}
|
||||
</div>
|
||||
<div id="baidu-hot-results-container" class="content-area" style="padding: 0 20px 10px 10px; flex-grow: 1; overflow-y: auto;">
|
||||
<div class="loading-container">
|
||||
<img src="./assets/loading.gif" alt="加载中..." class="loading-gif">
|
||||
<p class="loading-text">正在加载热搜...</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
init() {
|
||||
this._log('工具已初始化');
|
||||
document.getElementById('back-to-toolbox-btn')?.addEventListener('click', () => {
|
||||
window.mainPage.navigateTo('toolbox');
|
||||
window.mainPage.updateActiveNavButton(document.getElementById('toolbox-btn'));
|
||||
});
|
||||
|
||||
const tabs = document.querySelectorAll('#baidu-hot-tabs .sys-info-tab');
|
||||
tabs.forEach(tab => {
|
||||
tab.addEventListener('click', (e) => {
|
||||
tabs.forEach(t => t.classList.remove('active'));
|
||||
e.currentTarget.classList.add('active');
|
||||
const boardName = e.currentTarget.dataset.boardName;
|
||||
this._fetchAndRenderData(boardName);
|
||||
});
|
||||
});
|
||||
|
||||
// 默认加载第一个榜单
|
||||
if (tabs.length > 0) {
|
||||
tabs[0].click();
|
||||
}
|
||||
}
|
||||
|
||||
async _fetchAndRenderData(boardName) {
|
||||
if (this.abortController) this.abortController.abort();
|
||||
this.abortController = new AbortController();
|
||||
|
||||
const resultsContainer = document.getElementById('baidu-hot-results-container');
|
||||
if (!resultsContainer) return;
|
||||
|
||||
resultsContainer.innerHTML = `
|
||||
<div class="loading-container">
|
||||
<img src="./assets/loading.gif" alt="加载中..." class="loading-gif">
|
||||
<p class="loading-text">正在加载 ${boardName}...</p>
|
||||
</div>`;
|
||||
|
||||
try {
|
||||
const apiUrl = `https://api.suyanw.cn/api/bdrs.php?msg=${encodeURIComponent(boardName)}`;
|
||||
const response = await fetch(apiUrl, { signal: this.abortController.signal });
|
||||
if (!response.ok) throw new Error(`网络请求失败: ${response.status}`);
|
||||
|
||||
const blob = await response.blob();
|
||||
await window.electronAPI.addTraffic(blob.size);
|
||||
const textData = await blob.text();
|
||||
|
||||
this._log(`成功获取 ${boardName} 数据`);
|
||||
this._renderList(textData);
|
||||
} catch (error) {
|
||||
if (error.name === 'AbortError') return;
|
||||
const errorMessage = error.message.includes('API') ? error.message : `解析数据失败或网络异常`;
|
||||
this._log(`获取 ${boardName} 失败: ${errorMessage}`);
|
||||
resultsContainer.innerHTML = `<div class="loading-container"><p class="error-message"><i class="fas fa-exclamation-triangle"></i> 获取失败: ${errorMessage}</p></div>`;
|
||||
}
|
||||
}
|
||||
|
||||
_renderList(textData) {
|
||||
const resultsContainer = document.getElementById('baidu-hot-results-container');
|
||||
const lines = textData.split('\n').filter(line => line.trim() !== '' && !line.startsWith('----'));
|
||||
|
||||
if (lines.length === 0) {
|
||||
throw new Error('API返回数据为空或格式无法解析');
|
||||
}
|
||||
|
||||
// [修复] 改为使用 Flex 布局的 div 结构 (灵动岛风格),而非之前的 table
|
||||
const itemsHtml = lines.map((line, index) => {
|
||||
const match = line.match(/^(\d+):(.*)/);
|
||||
if (!match) return '';
|
||||
|
||||
const rank = match[1];
|
||||
const title = match[2].trim();
|
||||
const searchUrl = `https://www.baidu.com/s?wd=${encodeURIComponent(title)}`;
|
||||
|
||||
let rankClass = '';
|
||||
let iconClass = 'fa-fire';
|
||||
let iconColor = 'var(--text-secondary)';
|
||||
|
||||
if (rank <= 3) {
|
||||
rankClass = `rank-top-3 rank-${rank}`; // 复用 hotboardTool 的样式
|
||||
iconClass = 'fa-fire-alt';
|
||||
iconColor = 'var(--error-color)';
|
||||
}
|
||||
|
||||
// 使用 island-card 样式 (在 style.css 中已定义)
|
||||
return `
|
||||
<div class="island-card ripple" style="height: auto; min-height: 60px; margin-bottom: 10px; padding: 15px;" data-link="${searchUrl}">
|
||||
<div class="island-icon-box" style="width: 40px; height: 40px; border-radius: 10px; background: rgba(var(--bg-color-rgb), 0.5);">
|
||||
<span class="hotboard-rank ${rankClass}" style="margin: 0; font-size: 16px;">${rank}</span>
|
||||
</div>
|
||||
|
||||
<div class="island-content" style="flex-grow: 1; padding-left: 15px;">
|
||||
<h3 class="island-title" style="margin: 0; white-space: normal;">${title}</h3>
|
||||
</div>
|
||||
|
||||
<div class="island-action">
|
||||
<i class="fas fa-chevron-right arrow-icon"></i>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}).join('');
|
||||
|
||||
resultsContainer.innerHTML = `<div style="animation: contentFadeIn 0.5s;">${itemsHtml}</div>`;
|
||||
|
||||
resultsContainer.querySelectorAll('.island-card[data-link]').forEach(card => {
|
||||
card.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
window.electronAPI.openExternalLink(e.currentTarget.dataset.link);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
destroy() {
|
||||
if (this.abortController) this.abortController.abort();
|
||||
this._log('工具已销毁');
|
||||
super.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
export default BaiduHotTool;
|
||||
Reference in New Issue
Block a user