/** * 主应用逻辑 */ class App { constructor() { this.data = null; this.treeView = null; this.currentFeature = null; this.init(); } /** * 初始化应用 */ async init() { try { await this.loadData(); this.initComponents(); this.bindEvents(); this.updateStats(); } catch (error) { console.error('初始化失败:', error); this.showError('加载失败,请刷新页面重试'); } } /** * 加载数据 */ async loadData() { try { const response = await fetch('data/data.json'); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } this.data = await response.json(); } catch (error) { console.error('加载数据失败:', error); throw error; } } /** * 初始化组件 */ initComponents() { // 初始化树形菜单 const treeContainer = document.getElementById('tree-container'); this.treeView = new TreeView(treeContainer, this.data); // 设置节点点击回调 this.treeView.onNodeClick = (node) => { this.handleNodeClick(node); }; // 默认展开第一层 this.treeView.expandFirstLevel(); } /** * 绑定事件 */ bindEvents() { // 搜索 const searchInput = document.getElementById('search-input'); const searchBtn = document.getElementById('search-btn'); let searchTimer = null; searchInput?.addEventListener('input', (e) => { clearTimeout(searchTimer); searchTimer = setTimeout(() => { this.treeView.search(e.target.value); }, 300); }); searchBtn?.addEventListener('click', () => { this.treeView.search(searchInput.value); }); // 全部折叠 const collapseBtn = document.getElementById('collapse-all'); collapseBtn?.addEventListener('click', () => { this.treeView.collapseAll(); }); // 状态筛选 const filterStatus = document.getElementById('filter-status'); filterStatus?.addEventListener('change', () => { this.filterByStatus(filterStatus.value); }); // 排序 const sortBy = document.getElementById('sort-by'); sortBy?.addEventListener('change', () => { this.sortData(sortBy.value); }); // 主题切换 const themeToggle = document.getElementById('theme-toggle'); themeToggle?.addEventListener('click', () => { this.toggleTheme(); }); } /** * 处理节点点击 * @param {Object} node - 节点数据 */ handleNodeClick(node) { const panelTitle = document.getElementById('panel-title'); const panelInfo = document.getElementById('panel-info'); const cardsContainer = document.getElementById('cards-container'); if (node.type === 'feature' && node.data) { // 点击特征节点,显示搜索结果 this.currentFeature = node.data; // 更新标题 panelTitle.textContent = `"${node.label}" 的搜索结果`; // 构建详细的搜索信息 const metadata = node.data.search_metadata; if (metadata && metadata.status === 'success') { // 有搜索结果,显示完整参数 const params = metadata.search_params || {}; panelInfo.innerHTML = `
搜索词: ${params.keyword || '未知'} 内容类型: ${params.content_type || '未知'} 排序方式: ${params.sort_type || '未知'} 结果: ${metadata.note_count || 0} 条帖子
`; } else if (metadata && metadata.status === 'failed') { // 搜索失败 panelInfo.innerHTML = ` 搜索失败 `; } else if (node.searchWord === null) { // search_word 为 null(重复被过滤) panelInfo.innerHTML = ` 该特征的搜索词与其他特征重复,已被过滤 `; } else { // 待搜索 panelInfo.innerHTML = ` 待搜索 ${node.searchWord ? ` - 搜索词: ${node.searchWord}` : ''} `; } // 渲染帖子卡片 if (node.data.search_result) { const notes = node.data.search_result.data?.data || []; // 存储到全局变量供 CardRenderer 使用 window.__currentNotes = notes; CardRenderer.renderNotes(notes, cardsContainer); } else { cardsContainer.innerHTML = `

该特征暂无搜索结果

`; } } else if (node.type === 'association') { // 点击关联分类,显示特征列表 const features = node.data.特征列表 || []; const jaccardScore = (node.data['Jaccard相似度'] * 100).toFixed(1); panelTitle.textContent = node.label; panelInfo.innerHTML = ` 共 ${features.length} 个特征 Jaccard相似度: ${jaccardScore}% `; this.renderFeatureList(features, cardsContainer); } else if (node.type === 'result') { // 点击结果项,显示概览 panelTitle.textContent = node.label; panelInfo.textContent = `共 ${node.children?.length || 0} 个关联分类`; this.renderResultOverview(node.data, cardsContainer); } else if (node.type === 'note') { // 点击单个帖子,显示该帖子详情 window.__currentNotes = [node.data]; CardRenderer.renderNotes([node.data], cardsContainer); } } /** * 渲染特征列表 * @param {Array} features - 特征数组 * @param {HTMLElement} container - 容器元素 */ renderFeatureList(features, container) { const html = `
${features.map(feature => { const noteCount = feature.search_metadata?.note_count || 0; const status = feature.search_metadata?.status || 'pending'; const statusText = { success: '成功', failed: '失败', pending: '待搜索' }[status] || '未知'; const statusColor = { success: 'var(--secondary-color)', failed: 'var(--danger-color)', pending: 'var(--warning-color)' }[status] || 'var(--text-secondary)'; return ` `; }).join('')}
特征名称 搜索词 帖子数 状态
${feature.特征名称} ${feature.search_word || '-'} ${noteCount} ${statusText}
`; container.innerHTML = html; } /** * 渲染结果概览 * @param {Object} result - 结果数据 * @param {HTMLElement} container - 容器元素 */ renderResultOverview(result, container) { const html = `

基本信息

原始特征名称
${result.原始特征名称}
人设特征名称
${result.最高匹配信息?.人设特征名称 || '-'}
来源层级
${result.来源层级}
关联数量
${result.找到的关联?.length || 0} 个

关联分类

${(result.找到的关联 || []).map(assoc => `
${assoc.目标分类路径}
${assoc.特征列表?.length || 0} 个特征
Jaccard: ${(assoc['Jaccard相似度'] * 100).toFixed(1)}%
`).join('')}
`; container.innerHTML = html; } /** * 按状态筛选 * @param {string} status - 状态值 */ filterByStatus(status) { // TODO: 实现筛选逻辑 console.log('筛选状态:', status); } /** * 排序数据 * @param {string} sortBy - 排序方式 */ sortData(sortBy) { // TODO: 实现排序逻辑 console.log('排序方式:', sortBy); } /** * 切换主题 */ toggleTheme() { const body = document.body; const themeToggle = document.getElementById('theme-toggle'); if (body.classList.contains('dark-theme')) { body.classList.remove('dark-theme'); themeToggle.textContent = '🌙'; localStorage.setItem('theme', 'light'); } else { body.classList.add('dark-theme'); themeToggle.textContent = '☀️'; localStorage.setItem('theme', 'dark'); } } /** * 更新统计数据 */ updateStats() { const statResults = document.getElementById('stat-results'); const statFeatures = document.getElementById('stat-features'); const statNotes = document.getElementById('stat-notes'); // 统计数据 let totalFeatures = 0; let totalNotes = 0; this.data.forEach(result => { (result.找到的关联 || []).forEach(assoc => { (assoc.特征列表 || []).forEach(feature => { totalFeatures++; if (feature.search_metadata?.note_count) { totalNotes += feature.search_metadata.note_count; } }); }); }); statResults.textContent = this.data.length; statFeatures.textContent = totalFeatures; statNotes.textContent = totalNotes; } /** * 显示错误 * @param {string} message - 错误消息 */ showError(message) { const treeContainer = document.getElementById('tree-container'); const cardsContainer = document.getElementById('cards-container'); treeContainer.innerHTML = `

${message}

`; cardsContainer.innerHTML = `

${message}

`; } } // 初始化应用 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => { new App(); }); } else { new App(); } // 恢复主题设置 const savedTheme = localStorage.getItem('theme'); if (savedTheme === 'dark') { document.body.classList.add('dark-theme'); const themeToggle = document.getElementById('theme-toggle'); if (themeToggle) themeToggle.textContent = '☀️'; }