// js/ui/reference-manager-ui.js // 参考文献管理UI (function(global) { 'use strict'; /** * 参考文献管理UI类 */ class ReferenceManagerUI { constructor() { this.currentDocumentId = null; this.references = []; this.filteredReferences = []; this.selectedReferences = new Set(); this.sortColumn = 'index'; this.sortDirection = 'asc'; } /** * 初始化UI */ initialize() { this.createManagerModal(); this.attachEventListeners(); console.log('[ReferenceManagerUI] Initialized.'); } /** * 创建管理界面模态框 */ createManagerModal() { const modal = document.createElement('div'); modal.id = 'reference-manager-modal'; modal.className = 'reference-modal'; modal.innerHTML = `

参考文献管理

总计: 0 已验证: 0 有DOI: 0
# 作者 标题 年份 期刊/会议 DOI 摘要 标签 操作
暂无文献数据,请点击"提取文献"按钮开始
`; document.body.appendChild(modal); } /** * 创建编辑模态框 */ createEditModal() { const modal = document.createElement('div'); modal.id = 'reference-edit-modal'; modal.className = 'reference-modal'; modal.innerHTML = `

编辑文献

`; document.body.appendChild(modal); } /** * 绑定事件监听 */ attachEventListeners() { // 关闭按钮 document.addEventListener('click', (e) => { if (e.target.classList.contains('reference-modal-close') || e.target.id === 'ref-close-btn') { this.closeManager(); } }); // 提取文献 document.getElementById('ref-extract-btn')?.addEventListener('click', () => { this.extractReferences(); }); // 丰富元数据 document.getElementById('ref-enrich-doi-btn')?.addEventListener('click', () => { this.enrichMetadata(); }); // 配置元数据更新策略 document.getElementById('ref-enrich-config-btn')?.addEventListener('click', () => { this.showEnrichmentConfigModal(); }); // 添加文献 document.getElementById('ref-add-btn')?.addEventListener('click', () => { this.showEditModal(); }); // 导出 document.getElementById('ref-export-btn')?.addEventListener('click', () => { this.showExportMenu(); }); // 搜索 document.getElementById('ref-search-input')?.addEventListener('input', (e) => { this.filterReferences(e.target.value); }); // 筛选 document.getElementById('ref-filter-select')?.addEventListener('change', (e) => { this.applyFilter(e.target.value); }); // 全选 document.getElementById('ref-select-all')?.addEventListener('change', (e) => { this.toggleSelectAll(e.target.checked); }); // 批量删除 document.getElementById('ref-batch-delete-btn')?.addEventListener('click', () => { this.batchDelete(); }); // 表格排序 document.querySelectorAll('[data-sort]').forEach(th => { th.addEventListener('click', () => { this.sortTable(th.dataset.sort); }); }); } /** * 显示管理界面 * @param {string} documentId - 文档ID */ async show(documentId) { this.currentDocumentId = documentId; await this.loadReferences(); const modal = document.getElementById('reference-manager-modal'); if (modal) { modal.style.display = 'flex'; // 锁定背景滚动,避免模态框打开时页面抖动 this._prevBodyOverflow = document.body.style.overflow; document.body.style.overflow = 'hidden'; } } /** * 关闭管理界面 */ closeManager() { const modal = document.getElementById('reference-manager-modal'); if (modal) { modal.style.display = 'none'; } if (this._prevBodyOverflow !== undefined) { document.body.style.overflow = this._prevBodyOverflow; this._prevBodyOverflow = undefined; } } /** * 加载文献数据 */ async loadReferences() { if (!this.currentDocumentId) return; const data = await global.ReferenceStorage.loadReferences(this.currentDocumentId); if (data && data.references) { this.references = data.references; this.filteredReferences = [...this.references]; this.renderTable(); this.updateStats(); } } /** * 渲染表格 */ renderTable() { const tbody = document.getElementById('ref-table-body'); if (!tbody) return; if (this.filteredReferences.length === 0) { tbody.innerHTML = ` 暂无文献数据 `; return; } tbody.innerHTML = this.filteredReferences.map(ref => ` ${ref.index + 1} ${this.formatAuthors(ref.authors)} ${ref.title || '未提取'} ${ref.year || '-'} ${ref.journal || '-'} ${ref.doi ? `${ref.doi}` : ref.doiFallback ? `
⚠️ 🔍
` : '-'} ${this.formatAbstract(ref.abstract)} ${this.renderTags(ref.tags)} `).join(''); // 绑定操作按钮事件 tbody.querySelectorAll('.ref-action-btn').forEach(btn => { btn.addEventListener('click', (e) => { const action = e.currentTarget.dataset.action; const index = parseInt(e.currentTarget.dataset.index); this.handleAction(action, index); }); }); // 绑定复选框事件 tbody.querySelectorAll('.ref-checkbox').forEach(checkbox => { checkbox.addEventListener('change', (e) => { const index = parseInt(e.currentTarget.dataset.index); if (e.currentTarget.checked) { this.selectedReferences.add(index); } else { this.selectedReferences.delete(index); } this.updateSelectionInfo(); }); }); } /** * 格式化作者显示 */ formatAuthors(authors) { if (!authors || authors.length === 0) { return '未提取'; } if (authors.length === 1) { return authors[0]; } return `${authors[0]} 等 ${authors.length} 人`; } /** * 格式化摘要显示 */ formatAbstract(abstract) { if (!abstract) { return '-'; } // 限制长度,显示前100个字符 const maxLength = 100; if (abstract.length <= maxLength) { return abstract; } return abstract.substring(0, maxLength) + '...'; } /** * 渲染标签 */ renderTags(tags) { if (!tags || tags.length === 0) { return '-'; } return tags.map(tag => `${tag}`).join(' '); } /** * 更新统计信息 */ updateStats() { const statsEl = document.getElementById('ref-stats'); if (!statsEl) return; const total = this.references.length; const withDOI = this.references.filter(ref => !!ref.doi).length; statsEl.innerHTML = ` 总计: ${total} 已验证: ${withDOI} 有DOI: ${withDOI} `; } /** * 更新选中信息 */ updateSelectionInfo() { const info = document.getElementById('ref-selection-info'); const deleteBtn = document.getElementById('ref-batch-delete-btn'); if (this.selectedReferences.size === 0) { info.textContent = '未选中'; deleteBtn.disabled = true; } else { info.textContent = `已选中 ${this.selectedReferences.size} 项`; deleteBtn.disabled = false; } } /** * 提取文献 */ async extractReferences() { // 获取当前文档内容 const markdown = await this.getCurrentDocumentContent(); if (!markdown) { alert('无法获取文档内容'); return; } // 检测参考文献 const section = global.ReferenceDetector.detectReferenceSection(markdown); if (!section) { alert('未检测到参考文献部分。请确保文档包含"References"或"参考文献"等标题。'); return; } console.log(`检测到 ${section.entries.length} 条文献`); // 让用户选择提取方式 this.showExtractionMethodModal(section); } /** * 显示提取方式选择模态框 */ showExtractionMethodModal(section) { // 创建模态框 const modal = document.createElement('div'); modal.className = 'reference-modal'; modal.style.display = 'flex'; modal.innerHTML = `

选择提取方式

检测到 ${section.entries.length} 条文献

正则表达式提取

使用规则匹配,速度快,适合格式规范的文献

  • ✓ 速度快,无需API
  • ✓ 支持标准格式(APA、IEEE等)
  • ✗ 格式不规范时可能失败

AI智能提取

使用AI理解文献内容,准确度高,适合任何格式

  • ✓ 支持任意格式
  • ✓ 准确度高
  • ✗ 需要API,速度较慢

混合模式(推荐)

先用正则提取,失败的文献再用AI处理

  • ✓ 兼顾速度和准确度
  • ✓ 最大化成功率
  • ✓ 节省API调用
`; // 添加到页面 document.body.appendChild(modal); // 绑定点击事件 modal.querySelectorAll('.extraction-method-item').forEach(item => { item.addEventListener('click', () => { const method = item.dataset.method; document.body.removeChild(modal); this.startExtraction(section, method); }); }); // 关闭按钮 modal.querySelector('.reference-modal-close').addEventListener('click', () => { document.body.removeChild(modal); }); modal.querySelector('#extraction-cancel-btn').addEventListener('click', () => { document.body.removeChild(modal); }); // 点击背景关闭 modal.addEventListener('click', (e) => { if (e.target === modal) { document.body.removeChild(modal); } }); } /** * 开始提取 */ async startExtraction(section, method) { if (method === 'regex') { // 纯正则提取 const extracted = global.ReferenceExtractor.batchExtract(section.entries); this.saveExtractedReferences(extracted); } else if (method === 'ai') { // 纯AI提取 await this.extractWithAI(section.entries); } else if (method === 'hybrid') { // 混合模式 const extracted = global.ReferenceExtractor.batchExtract(section.entries); const needsAI = extracted.filter(e => e.needsAIProcessing); if (needsAI.length > 0) { const message = `正则成功提取: ${extracted.length - needsAI.length} 条\n` + `需要AI处理: ${needsAI.length} 条\n\n` + `是否继续使用AI处理剩余文献?`; if (confirm(message)) { await this.processWithAI(extracted); } else { this.saveExtractedReferences(extracted); } } else { this.saveExtractedReferences(extracted); } } } /** * 纯AI提取 */ async extractWithAI(entries) { // 获取API配置 const apiConfig = await this.getAPIConfig(); if (!apiConfig) { alert('请先配置AI模型'); return; } // 显示进度 this.showProgress('正在使用AI提取文献...'); try { // 创建原始引用对象 const rawReferences = entries.map(entry => ({ rawText: entry.text, lineStart: entry.lineStart, lineEnd: entry.lineEnd, needsAIProcessing: true })); const processed = await global.ReferenceAIProcessor.smartProcessReferences( rawReferences, apiConfig, 'auto', (progress) => { this.updateProgress( `处理进度: ${progress.processed}/${progress.total} (${progress.batchIndex + 1}/${progress.totalBatches} 批)` ); } ); this.hideProgress(); this.saveExtractedReferences(processed); } catch (error) { this.hideProgress(); alert('AI处理失败: ' + error.message); } } /** * 显示处理选项 */ showProcessingOptions(extracted) { const needsAI = extracted.filter(e => e.needsAIProcessing).length; const message = `检测到 ${extracted.length} 条文献\n` + `正则表达式成功提取: ${extracted.length - needsAI} 条\n` + `需要AI处理: ${needsAI} 条\n\n` + `是否使用AI处理剩余文献?`; if (needsAI > 0 && confirm(message)) { this.processWithAI(extracted); } else { // 直接保存正则提取的结果 this.saveExtractedReferences(extracted); } } /** * AI处理文献 */ async processWithAI(extracted) { // 获取API配置 const apiConfig = await this.getAPIConfig(); if (!apiConfig) { alert('请先配置AI模型'); return; } // 显示进度 this.showProgress('正在使用AI处理文献...'); try { const processed = await global.ReferenceAIProcessor.smartProcessReferences( extracted, apiConfig, 'auto', (progress) => { this.updateProgress( `处理进度: ${progress.processed}/${progress.total} (${progress.batchIndex + 1}/${progress.totalBatches} 批)` ); } ); this.hideProgress(); this.saveExtractedReferences(processed); } catch (error) { this.hideProgress(); alert('AI处理失败: ' + error.message); } } /** * 保存提取的文献 */ async saveExtractedReferences(references) { // 建立索引 const markdown = await this.getCurrentDocumentContent(); if (markdown && window.ReferenceIndexer) { const indexedReferences = window.ReferenceIndexer.buildIndex( this.currentDocumentId, markdown, references ); references = indexedReferences; } const success = await global.ReferenceStorage.saveReferences( this.currentDocumentId, references, { extractedAt: new Date().toISOString(), method: 'auto' } ); if (success) { alert(`成功保存 ${references.length} 条文献`); await this.loadReferences(); } else { alert('保存失败'); } } /** * 获取当前文档内容 */ async getCurrentDocumentContent() { // 方式1: 从 window.data 获取(详情页) if (window.data && window.data.ocr) { return window.data.ocr; } // 方式2: 从currentOcrResult获取(主页面) if (window.currentOcrResult && window.currentOcrResult.markdown) { return window.currentOcrResult.markdown; } // 方式3: 从currentHistoryData获取 if (window.currentHistoryData && window.currentHistoryData.ocrResult) { return window.currentHistoryData.ocrResult; } console.error('[ReferenceManagerUI] 无法获取文档内容'); return null; } /** * 获取API配置(使用与Chatbot相同的配置获取方式) */ async getAPIConfig() { // 使用与Chatbot相同的配置获取函数 if (typeof window.MessageSender?.getChatbotConfig === 'function') { const config = window.MessageSender.getChatbotConfig(); if (!config || !config.apiKey) { alert('请先配置AI模型和API Key'); return null; } // 如果是自定义模型,使用Chatbot的buildCustomApiConfig if (config.model === 'custom' || config.model.startsWith('custom_source_')) { const endpoint = config.cms.apiEndpoint || config.cms.apiBaseUrl; if (!endpoint || !config.cms.modelId) { alert('自定义模型配置不完整'); return null; } // 使用Chatbot的buildCustomApiConfig函数(保证一致性) if (typeof window.ApiConfigBuilder?.buildCustomApiConfig === 'function') { const builtConfig = window.ApiConfigBuilder.buildCustomApiConfig( config.apiKey, endpoint, config.cms.modelId || config.cms.preferredModelId, config.cms.requestFormat, parseFloat(config.cms.temperature) || 0.1, parseInt(config.cms.max_tokens) || 4000, { endpointMode: config.cms.endpointMode || 'auto' } ); console.log('[ReferenceManagerUI] 使用buildCustomApiConfig构建的配置:', builtConfig); return builtConfig; } console.error('[ReferenceManagerUI] buildCustomApiConfig函数不可用'); return null; } // 预设模型 return global.ReferenceAIProcessor.buildAPIConfig(config.model, config.apiKey); } // 备用方案:使用旧的获取方式 const settings = typeof loadSettings === 'function' ? loadSettings() : {}; const model = settings.selectedTranslationModel || 'gemini'; const apiKey = settings.apiKeys?.[model] || ''; if (!apiKey) { alert('请先配置AI模型和API Key'); return null; } return global.ReferenceAIProcessor.buildAPIConfig(model, apiKey); } /** * 处理操作 */ handleAction(action, index) { const ref = this.references.find(r => r.index === index); if (!ref) return; switch (action) { case 'edit': this.showEditModal(ref); break; case 'view': this.viewInDocument(ref); break; case 'delete': if (confirm('确定要删除这条文献吗?')) { this.deleteReference(index); } break; } } /** * 显示编辑模态框 */ showEditModal(ref = null) { if (!document.getElementById('reference-edit-modal')) { this.createEditModal(); } const modal = document.getElementById('reference-edit-modal'); // 填充数据 if (ref) { document.getElementById('edit-authors').value = (ref.authors || []).join(', '); document.getElementById('edit-title').value = ref.title || ''; document.getElementById('edit-year').value = ref.year || ''; document.getElementById('edit-journal').value = ref.journal || ''; document.getElementById('edit-volume').value = ref.volume || ''; document.getElementById('edit-issue').value = ref.issue || ''; document.getElementById('edit-pages').value = ref.pages || ''; document.getElementById('edit-doi').value = ref.doi || ''; document.getElementById('edit-url').value = ref.url || ''; document.getElementById('edit-type').value = ref.type || 'journal'; document.getElementById('edit-tags').value = (ref.tags || []).join(', '); document.getElementById('edit-abstract').value = ref.abstract || ''; } modal.style.display = 'flex'; // 绑定保存按钮 const saveBtn = document.getElementById('ref-edit-save-btn'); saveBtn.onclick = () => this.saveEdit(ref); // 绑定取消按钮 const cancelBtn = document.getElementById('ref-edit-cancel-btn'); cancelBtn.onclick = () => { modal.style.display = 'none'; }; } /** * 保存编辑 */ async saveEdit(originalRef) { const updates = { authors: document.getElementById('edit-authors').value.split(',').map(s => s.trim()).filter(Boolean), title: document.getElementById('edit-title').value, year: parseInt(document.getElementById('edit-year').value) || null, journal: document.getElementById('edit-journal').value, volume: document.getElementById('edit-volume').value, issue: document.getElementById('edit-issue').value, pages: document.getElementById('edit-pages').value, doi: document.getElementById('edit-doi').value, url: document.getElementById('edit-url').value, type: document.getElementById('edit-type').value, tags: document.getElementById('edit-tags').value.split(',').map(s => s.trim()).filter(Boolean), abstract: document.getElementById('edit-abstract').value }; if (originalRef) { // 更新现有文献 await global.ReferenceStorage.updateReference(this.currentDocumentId, originalRef.index, updates); } else { // 添加新文献 await global.ReferenceStorage.addReference(this.currentDocumentId, updates); } document.getElementById('reference-edit-modal').style.display = 'none'; await this.loadReferences(); } /** * 在文档中查看 */ viewInDocument(ref) { // 使用索引器滚动到文献在原文中的位置 if (window.ReferenceIndexer) { const success = window.ReferenceIndexer.scrollToReference( this.currentDocumentId, ref.index ); if (success) { // 关闭管理器以显示原文 this.closeManager(); } else { alert('无法定位到原文位置,文献可能不在当前文档中'); } } else { console.error('[ReferenceManagerUI] Indexer not available'); alert('索引器未加载,无法定位原文位置'); } } /** * 删除文献 */ async deleteReference(index) { await global.ReferenceStorage.removeReference(this.currentDocumentId, index); await this.loadReferences(); } /** * 批量删除 */ async batchDelete() { if (!confirm(`确定要删除选中的 ${this.selectedReferences.size} 条文献吗?`)) { return; } // 从大到小删除,避免索引变化 const indices = Array.from(this.selectedReferences).sort((a, b) => b - a); for (const index of indices) { await global.ReferenceStorage.removeReference(this.currentDocumentId, index); } this.selectedReferences.clear(); await this.loadReferences(); } /** * 筛选文献 */ filterReferences(query) { if (!query) { this.filteredReferences = [...this.references]; } else { const lowerQuery = query.toLowerCase(); this.filteredReferences = this.references.filter(ref => { const searchText = [ ref.title, ...(ref.authors || []), ref.journal, ref.doi ].filter(Boolean).join(' ').toLowerCase(); return searchText.includes(lowerQuery); }); } this.renderTable(); } /** * 应用筛选器 */ applyFilter(filter) { switch (filter) { case 'all': this.filteredReferences = [...this.references]; break; case 'recent': this.filteredReferences = this.references.filter(ref => ref.tags && ref.tags.includes('Recent') ); break; case 'classic': this.filteredReferences = this.references.filter(ref => ref.tags && ref.tags.includes('Classic') ); break; case 'verified': this.filteredReferences = this.references.filter(ref => ref.doi); break; } this.renderTable(); } /** * 排序表格 */ sortTable(column) { if (this.sortColumn === column) { this.sortDirection = this.sortDirection === 'asc' ? 'desc' : 'asc'; } else { this.sortColumn = column; this.sortDirection = 'asc'; } this.filteredReferences.sort((a, b) => { let aVal = a[column]; let bVal = b[column]; if (column === 'authors') { aVal = (a.authors || [])[0] || ''; bVal = (b.authors || [])[0] || ''; } if (aVal === bVal) return 0; if (aVal == null) return 1; if (bVal == null) return -1; const comparison = aVal < bVal ? -1 : 1; return this.sortDirection === 'asc' ? comparison : -comparison; }); this.renderTable(); } /** * 全选/取消全选 */ toggleSelectAll(checked) { this.selectedReferences.clear(); if (checked) { this.filteredReferences.forEach(ref => { this.selectedReferences.add(ref.index); }); } document.querySelectorAll('.ref-checkbox').forEach(checkbox => { checkbox.checked = checked; }); this.updateSelectionInfo(); } /** * 显示导出菜单 */ showExportMenu() { const options = ['BibTeX', 'JSON', 'CSV']; const choice = prompt(`选择导出格式:\n${options.map((o, i) => `${i + 1}. ${o}`).join('\n')}`); if (!choice) return; const index = parseInt(choice) - 1; if (index >= 0 && index < options.length) { this.exportReferences(options[index].toLowerCase()); } } /** * 导出文献 */ exportReferences(format) { let content = ''; let filename = ''; switch (format) { case 'bibtex': content = global.ReferenceStorage.exportToBibTeX(this.currentDocumentId); filename = 'references.bib'; break; case 'json': content = global.ReferenceStorage.exportToJSON(this.currentDocumentId); filename = 'references.json'; break; case 'csv': content = this.exportToCSV(); filename = 'references.csv'; break; } if (content) { this.downloadFile(content, filename); } } /** * 导出为CSV */ exportToCSV() { const headers = ['Index', 'Authors', 'Title', 'Year', 'Journal', 'Volume', 'Issue', 'Pages', 'DOI', 'URL', 'Type', 'Tags']; const rows = this.references.map(ref => [ ref.index + 1, (ref.authors || []).join('; '), ref.title || '', ref.year || '', ref.journal || '', ref.volume || '', ref.issue || '', ref.pages || '', ref.doi || '', ref.url || '', ref.type || '', (ref.tags || []).join('; ') ]); const csv = [headers, ...rows].map(row => row.map(cell => `"${String(cell).replace(/"/g, '""')}"`).join(',') ).join('\n'); return csv; } /** * 下载文件 */ downloadFile(content, filename) { const blob = new Blob([content], { type: 'text/plain' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = filename; a.click(); URL.revokeObjectURL(url); } /** * 显示进度 */ showProgress(message) { // 简单实现,可以改进为更好的进度条 const progress = document.createElement('div'); progress.id = 'ref-progress'; progress.className = 'ref-progress'; progress.innerHTML = `
${message}
`; document.body.appendChild(progress); } /** * 更新进度 */ updateProgress(message) { const progress = document.getElementById('ref-progress'); if (progress) { progress.querySelector('.ref-progress-content').textContent = message; } } /** * 隐藏进度 */ hideProgress() { const progress = document.getElementById('ref-progress'); if (progress) { progress.remove(); } } /** * 显示元数据更新策略配置模态框 */ showEnrichmentConfigModal() { const config = this.getEnrichmentConfig(); const modal = document.createElement('div'); modal.className = 'ref-modal'; modal.innerHTML = `

元数据更新策略配置

配置从外部数据源获取数据后,如何更新现有文献的各个字段

更新策略说明:

  • 总是更新:用新数据覆盖现有数据(推荐用于DOI)
  • 仅为空时更新:只有字段为空时才填充(保守策略)
  • 优先新数据:新数据更完整时使用新数据(智能策略)
  • 保持原数据:不更新该字段(保护原始数据)
`; document.body.appendChild(modal); // 绑定事件 modal.querySelector('.ref-modal-close').addEventListener('click', () => modal.remove()); modal.addEventListener('click', (e) => { if (e.target === modal) modal.remove(); }); // 恢复默认 document.getElementById('enrichment-config-reset').addEventListener('click', () => { const defaultConfig = this.getDefaultEnrichmentConfig(); Object.keys(defaultConfig).forEach(field => { const select = modal.querySelector(`[data-field="${field}"]`); if (select) select.value = defaultConfig[field]; }); }); // 保存配置 document.getElementById('enrichment-config-save').addEventListener('click', () => { const newConfig = {}; modal.querySelectorAll('[data-field]').forEach(select => { newConfig[select.dataset.field] = select.value; }); this.saveEnrichmentConfig(newConfig); alert('配置已保存'); modal.remove(); }); } /** * 获取默认元数据更新策略 */ getDefaultEnrichmentConfig() { return { doi: 'always', // DOI总是更新 abstract: 'if_empty', // 摘要仅为空时更新 authors: 'if_empty', // 作者仅为空时更新 year: 'if_empty', // 年份仅为空时更新 journal: 'prefer_new', // 期刊优先新数据 url: 'if_empty' // URL仅为空时更新 }; } /** * 获取元数据更新策略配置 */ getEnrichmentConfig() { try { const saved = localStorage.getItem('referenceEnrichmentConfig'); return saved ? JSON.parse(saved) : this.getDefaultEnrichmentConfig(); } catch (error) { console.warn('[ReferenceManagerUI] Failed to load enrichment config:', error); return this.getDefaultEnrichmentConfig(); } } /** * 保存元数据更新策略配置 */ saveEnrichmentConfig(config) { try { localStorage.setItem('referenceEnrichmentConfig', JSON.stringify(config)); } catch (error) { console.error('[ReferenceManagerUI] Failed to save enrichment config:', error); } } /** * 丰富元数据(新版) */ async enrichMetadata() { if (!this.references || this.references.length === 0) { alert('请先提取文献'); return; } if (!window.DOIResolver) { alert('DOI解析器未加载'); return; } // 获取配置 const config = this.getEnrichmentConfig(); // 根据配置决定需要更新的文献 const needsEnrichment = this.references.filter(ref => { if (!ref.title) return false; // 检查是否有任何字段需要更新 return ( (config.doi !== 'keep_original' && (!ref.doi || config.doi === 'always')) || (config.abstract !== 'keep_original' && (!ref.abstract || config.abstract === 'always' || config.abstract === 'prefer_new')) || (config.authors !== 'keep_original' && (!ref.authors || config.authors === 'always' || config.authors === 'prefer_new')) || (config.year !== 'keep_original' && (!ref.year || config.year === 'always' || config.year === 'prefer_new')) || (config.journal !== 'keep_original' && (!ref.journal || config.journal === 'always' || config.journal === 'prefer_new')) ); }); if (needsEnrichment.length === 0) { alert('根据当前配置,没有文献需要丰富元数据'); return; } // 构建确认消息 let strategyMsg = '更新策略:\n'; Object.keys(config).forEach(field => { const label = { doi: 'DOI', abstract: '摘要', authors: '作者', year: '年份', journal: '期刊', url: 'URL' }[field] || field; const strategy = { always: '总是更新', if_empty: '仅为空时更新', prefer_new: '优先新数据', keep_original: '保持原数据' }[config[field]] || config[field]; strategyMsg += `${label}: ${strategy}\n`; }); const confirmed = confirm( `检测到 ${needsEnrichment.length}/${this.references.length} 条文献需要丰富元数据\n\n` + `${strategyMsg}\n` + `将通过 CrossRef、OpenAlex、arXiv、PubMed 并发查询\n` + `失败的文献将使用 Semantic Scholar 托底\n\n` + `是否继续?` ); if (!confirmed) return; this.showProgress(`准备丰富 ${needsEnrichment.length} 条文献的元数据...`); try { // 创建resolver const resolver = window.DOIResolver.create(); const results = await resolver.batchResolve(needsEnrichment, (progress) => { if (progress.phase === 'fallback') { this.updateProgress(`Semantic Scholar 托底查询中...`); } else { this.updateProgress(`正在丰富元数据: ${progress.completed}/${progress.total}`); } }); this.hideProgress(); let successCount = 0; let fallbackCount = 0; // 应用更新策略 results.forEach(result => { if (result.success && result.resolved) { const originalRef = this.references.find(r => r === result.original); if (originalRef) { // 检查是否是fallback(Google搜索链接) if (result.resolved.fallback) { originalRef.doiFallback = true; originalRef.doiFallbackUrl = result.resolved.url; originalRef.doiFallbackMessage = result.resolved.message; fallbackCount++; } else { // 应用字段更新策略 const resolved = result.resolved; // DOI if (resolved.doi && this.shouldUpdateField(originalRef, 'doi', resolved.doi, config.doi)) { originalRef.doi = resolved.doi; successCount++; } // 摘要 if (resolved.abstract && this.shouldUpdateField(originalRef, 'abstract', resolved.abstract, config.abstract)) { originalRef.abstract = resolved.abstract; } // 作者 if (resolved.authors && this.shouldUpdateField(originalRef, 'authors', resolved.authors, config.authors)) { originalRef.authors = resolved.authors; } // 年份 if (resolved.year && this.shouldUpdateField(originalRef, 'year', resolved.year, config.year)) { originalRef.year = resolved.year; } // 期刊 if (resolved.journal && this.shouldUpdateField(originalRef, 'journal', resolved.journal, config.journal)) { originalRef.journal = resolved.journal; } // URL if (resolved.url && this.shouldUpdateField(originalRef, 'url', resolved.url, config.url)) { originalRef.url = resolved.url; } } } } }); await global.ReferenceStorage.saveReferences(this.currentDocumentId, this.references, { updatedAt: new Date().toISOString(), metadataEnriched: true }); await this.loadReferences(); let message = `元数据丰富完成\n\n成功: ${successCount}/${needsEnrichment.length}`; if (fallbackCount > 0) { message += `\n未找到: ${fallbackCount}(已生成搜索链接)`; } const failedCount = needsEnrichment.length - successCount - fallbackCount; if (failedCount > 0) { message += `\n失败: ${failedCount}`; } alert(message); } catch (error) { this.hideProgress(); alert('元数据丰富失败: ' + error.message); console.error('[ReferenceManagerUI] Metadata enrichment failed:', error); } } /** * 判断是否应该更新字段 */ shouldUpdateField(originalRef, fieldName, newValue, strategy) { const originalValue = originalRef[fieldName]; switch (strategy) { case 'always': return true; case 'if_empty': return !originalValue || (Array.isArray(originalValue) && originalValue.length === 0); case 'prefer_new': // 如果原值为空,使用新值 if (!originalValue || (Array.isArray(originalValue) && originalValue.length === 0)) { return true; } // 如果新值更完整(例如作者列表更长),使用新值 if (Array.isArray(newValue) && Array.isArray(originalValue)) { return newValue.length > originalValue.length; } // 如果新值更长(例如摘要更详细),使用新值 if (typeof newValue === 'string' && typeof originalValue === 'string') { return newValue.length > originalValue.length * 1.2; // 至少长20%才算更完整 } return false; case 'keep_original': default: return false; } } } // 创建全局实例 const ui = new ReferenceManagerUI(); // 导出API global.ReferenceManagerUI = ui; // 自动初始化 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => ui.initialize()); } else { ui.initialize(); } console.log('[ReferenceManagerUI] Reference manager UI loaded.'); })(window);