// 知识库管理相关功能 let knowledgeCategories = []; let knowledgeItems = []; let currentEditingItemId = null; let isSavingKnowledgeItem = false; // 防止重复提交 let retrievalLogsData = []; // 存储检索日志数据,用于详情查看 let knowledgePagination = { currentPage: 1, pageSize: 10, // 每页分类数(改为按分类分页) total: 0, currentCategory: '' }; let searchTimeout = null; // 搜索防抖定时器 // 加载知识分类 async function loadKnowledgeCategories() { try { // 添加时间戳参数避免缓存 const timestamp = Date.now(); const response = await apiFetch(`/api/knowledge/categories?_t=${timestamp}`, { method: 'GET', headers: { 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0' } }); if (!response.ok) { throw new Error('获取分类失败'); } const data = await response.json(); // 检查知识库功能是否启用 if (data.enabled === false) { // 功能未启用,显示友好提示 const container = document.getElementById('knowledge-items-list'); if (container) { container.innerHTML = `
📚

知识库功能未启用

${data.message || '请前往系统设置启用知识检索功能'}

`; } return []; } knowledgeCategories = data.categories || []; // 更新分类筛选下拉框 const filterDropdown = document.getElementById('knowledge-category-filter-dropdown'); if (filterDropdown) { filterDropdown.innerHTML = '
全部
'; knowledgeCategories.forEach(category => { const option = document.createElement('div'); option.className = 'custom-select-option'; option.setAttribute('data-value', category); option.textContent = category; option.onclick = function() { selectKnowledgeCategory(category); }; filterDropdown.appendChild(option); }); } return knowledgeCategories; } catch (error) { console.error('加载分类失败:', error); // 只在非功能未启用的情况下显示错误 if (!error.message.includes('知识库功能未启用')) { showNotification('加载分类失败: ' + error.message, 'error'); } return []; } } // 加载知识项列表(支持按分类分页,默认不加载完整内容) async function loadKnowledgeItems(category = '', page = 1, pageSize = 10) { try { // 更新分页状态 knowledgePagination.currentCategory = category; knowledgePagination.currentPage = page; knowledgePagination.pageSize = pageSize; // 构建URL(按分类分页模式,不包含完整内容) const timestamp = Date.now(); const offset = (page - 1) * pageSize; let url = `/api/knowledge/items?categoryPage=true&limit=${pageSize}&offset=${offset}&_t=${timestamp}`; if (category) { url += `&category=${encodeURIComponent(category)}`; } const response = await apiFetch(url, { method: 'GET', headers: { 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0' } }); if (!response.ok) { throw new Error('获取知识项失败'); } const data = await response.json(); // 检查知识库功能是否启用 if (data.enabled === false) { // 功能未启用,显示友好提示(如果还没有显示的话) const container = document.getElementById('knowledge-items-list'); if (container && !container.querySelector('.empty-state')) { container.innerHTML = `
📚

知识库功能未启用

${data.message || '请前往系统设置启用知识检索功能'}

`; } knowledgeItems = []; knowledgePagination.total = 0; renderKnowledgePagination(); return []; } // 处理按分类分页的响应数据 const categoriesWithItems = data.categories || []; knowledgePagination.total = data.total || 0; // 总分类数 renderKnowledgeItemsByCategories(categoriesWithItems); // 如果选择了单个分类,不显示分页(因为只显示一个分类) if (category) { const paginationContainer = document.getElementById('knowledge-pagination'); if (paginationContainer) { paginationContainer.innerHTML = ''; } } else { renderKnowledgePagination(); } return categoriesWithItems; } catch (error) { console.error('加载知识项失败:', error); // 只在非功能未启用的情况下显示错误 if (!error.message.includes('知识库功能未启用')) { showNotification('加载知识项失败: ' + error.message, 'error'); } return []; } } // 渲染知识项列表(按分类分页的数据结构) function renderKnowledgeItemsByCategories(categoriesWithItems) { const container = document.getElementById('knowledge-items-list'); if (!container) return; if (categoriesWithItems.length === 0) { container.innerHTML = '
暂无知识项
'; return; } // 计算总项数和分类数 const totalItems = categoriesWithItems.reduce((sum, cat) => sum + (cat.items?.length || 0), 0); const categoryCount = categoriesWithItems.length; // 更新统计信息 updateKnowledgeStats(categoriesWithItems, categoryCount); // 渲染分类及知识项 let html = '
'; categoriesWithItems.forEach(categoryData => { const category = categoryData.category || '未分类'; const categoryItems = categoryData.items || []; const categoryCount = categoryData.itemCount || categoryItems.length; html += `

${escapeHtml(category)}

${categoryCount} 项
${categoryItems.map(item => renderKnowledgeItemCard(item)).join('')}
`; }); html += '
'; container.innerHTML = html; } // 渲染知识项列表(向后兼容,用于按项分页的旧代码) function renderKnowledgeItems(items) { const container = document.getElementById('knowledge-items-list'); if (!container) return; if (items.length === 0) { container.innerHTML = '
暂无知识项
'; return; } // 按分类分组 const groupedByCategory = {}; items.forEach(item => { const category = item.category || '未分类'; if (!groupedByCategory[category]) { groupedByCategory[category] = []; } groupedByCategory[category].push(item); }); // 更新统计信息 updateKnowledgeStats(items, Object.keys(groupedByCategory).length); // 渲染分组后的内容 const categories = Object.keys(groupedByCategory).sort(); let html = '
'; categories.forEach(category => { const categoryItems = groupedByCategory[category]; const categoryCount = categoryItems.length; html += `

${escapeHtml(category)}

${categoryCount} 项
${categoryItems.map(item => renderKnowledgeItemCard(item)).join('')}
`; }); html += '
'; container.innerHTML = html; } // 渲染分页控件(按分类分页) function renderKnowledgePagination() { const container = document.getElementById('knowledge-pagination'); if (!container) return; const { currentPage, pageSize, total } = knowledgePagination; const totalPages = Math.ceil(total / pageSize); // total是总分类数 if (totalPages <= 1) { container.innerHTML = ''; return; } let html = '
'; // 上一页按钮 html += ``; // 页码显示(显示分类数) html += `第 ${currentPage} 页,共 ${totalPages} 页(共 ${total} 个分类)`; // 下一页按钮 html += ``; html += '
'; container.innerHTML = html; } // 加载指定页码的知识项 function loadKnowledgePage(page) { const { currentCategory, pageSize, total } = knowledgePagination; const totalPages = Math.ceil(total / pageSize); if (page < 1 || page > totalPages) { return; } loadKnowledgeItems(currentCategory, page, pageSize); } // 渲染单个知识项卡片 function renderKnowledgeItemCard(item) { // 提取内容预览(如果item没有content字段,说明是摘要,不显示预览) let previewText = ''; if (item.content) { // 去除markdown格式,取前150字符 let preview = item.content; // 移除markdown标题标记 preview = preview.replace(/^#+\s+/gm, ''); // 移除代码块 preview = preview.replace(/```[\s\S]*?```/g, ''); // 移除行内代码 preview = preview.replace(/`[^`]+`/g, ''); // 移除链接 preview = preview.replace(/\[([^\]]+)\]\([^\)]+\)/g, '$1'); // 清理多余空白 preview = preview.replace(/\n+/g, ' ').replace(/\s+/g, ' ').trim(); previewText = preview.length > 150 ? preview.substring(0, 150) + '...' : preview; } // 提取文件路径显示 const filePath = item.filePath || ''; const relativePath = filePath.split(/[/\\]/).slice(-2).join('/'); // 显示最后两级路径 // 格式化时间 const createdTime = formatTime(item.createdAt); const updatedTime = formatTime(item.updatedAt); // 优先显示更新时间,如果没有更新时间则显示创建时间 const displayTime = updatedTime || createdTime; const timeLabel = updatedTime ? '更新时间' : '创建时间'; // 判断是否为最近更新(7天内) let isRecent = false; if (item.updatedAt && updatedTime) { const updateDate = new Date(item.updatedAt); if (!isNaN(updateDate.getTime())) { isRecent = (Date.now() - updateDate.getTime()) < 7 * 24 * 60 * 60 * 1000; } } return `

${escapeHtml(item.title)}

${relativePath ? `
📁 ${escapeHtml(relativePath)}
` : ''}
${previewText ? `

${escapeHtml(previewText)}

` : ''}
`; } // 更新统计信息(支持按分类分页的数据结构) function updateKnowledgeStats(data, categoryCount) { const statsContainer = document.getElementById('knowledge-stats'); if (!statsContainer) return; // 计算当前页的知识项数 let currentPageItemCount = 0; if (Array.isArray(data) && data.length > 0) { // 判断是categoriesWithItems还是items数组 if (data[0].category !== undefined && data[0].items !== undefined) { // 是按分类分页的数据结构 currentPageItemCount = data.reduce((sum, cat) => sum + (cat.items?.length || 0), 0); } else { // 是按项分页的数据结构(向后兼容) currentPageItemCount = data.length; } } // 总分类数(来自分页信息,只有在未定义时才使用当前页分类数作为后备值) const totalCategories = (knowledgePagination.total != null) ? knowledgePagination.total : categoryCount; statsContainer.innerHTML = `
总分类数 ${totalCategories}
当前页分类 ${categoryCount} 个
当前页知识项 ${currentPageItemCount} 项
`; // 更新索引进度 updateIndexProgress(); } // 更新索引进度 let indexProgressInterval = null; async function updateIndexProgress() { try { const response = await apiFetch('/api/knowledge/index-status', { method: 'GET', headers: { 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0' } }); if (!response.ok) { return; // 静默失败,不影响主界面 } const status = await response.json(); const progressContainer = document.getElementById('knowledge-index-progress'); if (!progressContainer) return; // 检查知识库功能是否启用 if (status.enabled === false) { // 功能未启用,隐藏进度条 progressContainer.style.display = 'none'; if (indexProgressInterval) { clearInterval(indexProgressInterval); indexProgressInterval = null; } return; } const totalItems = status.total_items || 0; const indexedItems = status.indexed_items || 0; const progressPercent = status.progress_percent || 0; const isComplete = status.is_complete || false; if (totalItems === 0) { // 没有知识项,隐藏进度条 progressContainer.style.display = 'none'; if (indexProgressInterval) { clearInterval(indexProgressInterval); indexProgressInterval = null; } return; } // 显示进度条 progressContainer.style.display = 'block'; if (isComplete) { progressContainer.innerHTML = `
索引构建完成 (${indexedItems}/${totalItems})
`; // 完成后停止轮询 if (indexProgressInterval) { clearInterval(indexProgressInterval); indexProgressInterval = null; } } else { progressContainer.innerHTML = `
🔨 正在构建索引: ${indexedItems}/${totalItems} (${progressPercent.toFixed(1)}%)
索引构建完成后,语义搜索功能将可用
`; // 如果还没有开始轮询,开始轮询 if (!indexProgressInterval) { indexProgressInterval = setInterval(updateIndexProgress, 3000); // 每3秒刷新一次 } } } catch (error) { // 静默失败 console.debug('获取索引状态失败:', error); } } // 选择知识分类 function selectKnowledgeCategory(category) { const trigger = document.getElementById('knowledge-category-filter-trigger'); const wrapper = document.getElementById('knowledge-category-filter-wrapper'); const dropdown = document.getElementById('knowledge-category-filter-dropdown'); if (trigger && wrapper && dropdown) { const displayText = category || '全部'; trigger.querySelector('span').textContent = displayText; wrapper.classList.remove('open'); // 更新选中状态 dropdown.querySelectorAll('.custom-select-option').forEach(opt => { opt.classList.remove('selected'); if (opt.getAttribute('data-value') === category) { opt.classList.add('selected'); } }); } // 切换分类时重置到第一页(如果选择了分类,API会返回该分类的所有项) loadKnowledgeItems(category, 1, knowledgePagination.pageSize); } // 筛选知识项 function filterKnowledgeItems() { const wrapper = document.getElementById('knowledge-category-filter-wrapper'); if (wrapper) { const selectedOption = wrapper.querySelector('.custom-select-option.selected'); const category = selectedOption ? selectedOption.getAttribute('data-value') : ''; // 重置到第一页 loadKnowledgeItems(category, 1, knowledgePagination.pageSize); } } // 处理搜索输入(带防抖) function handleKnowledgeSearchInput() { const searchInput = document.getElementById('knowledge-search'); const searchTerm = searchInput?.value.trim() || ''; // 清除之前的定时器 if (searchTimeout) { clearTimeout(searchTimeout); } // 如果搜索框为空,立即恢复列表 if (!searchTerm) { const wrapper = document.getElementById('knowledge-category-filter-wrapper'); let category = ''; if (wrapper) { const selectedOption = wrapper.querySelector('.custom-select-option.selected'); category = selectedOption ? selectedOption.getAttribute('data-value') : ''; } loadKnowledgeItems(category, 1, knowledgePagination.pageSize); return; } // 有搜索词时,延迟500ms后执行搜索(防抖) searchTimeout = setTimeout(() => { searchKnowledgeItems(); }, 500); } // 搜索知识项(后端关键字匹配,在所有数据中搜索) async function searchKnowledgeItems() { const searchInput = document.getElementById('knowledge-search'); const searchTerm = searchInput?.value.trim() || ''; if (!searchTerm) { // 恢复原始列表(重置到第一页) const wrapper = document.getElementById('knowledge-category-filter-wrapper'); let category = ''; if (wrapper) { const selectedOption = wrapper.querySelector('.custom-select-option.selected'); category = selectedOption ? selectedOption.getAttribute('data-value') : ''; } await loadKnowledgeItems(category, 1, knowledgePagination.pageSize); return; } try { // 获取当前选择的分类 const wrapper = document.getElementById('knowledge-category-filter-wrapper'); let category = ''; if (wrapper) { const selectedOption = wrapper.querySelector('.custom-select-option.selected'); category = selectedOption ? selectedOption.getAttribute('data-value') : ''; } // 调用后端API进行全量搜索 const timestamp = Date.now(); let url = `/api/knowledge/items?search=${encodeURIComponent(searchTerm)}&_t=${timestamp}`; if (category) { url += `&category=${encodeURIComponent(category)}`; } const response = await apiFetch(url, { method: 'GET', headers: { 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0' } }); if (!response.ok) { throw new Error('搜索失败'); } const data = await response.json(); // 检查知识库功能是否启用 if (data.enabled === false) { const container = document.getElementById('knowledge-items-list'); if (container) { container.innerHTML = `
📚

知识库功能未启用

${data.message || '请前往系统设置启用知识检索功能'}

`; } return; } // 处理搜索结果 const categoriesWithItems = data.categories || []; // 渲染搜索结果 const container = document.getElementById('knowledge-items-list'); if (!container) return; if (categoriesWithItems.length === 0) { container.innerHTML = `
🔍

未找到匹配的知识项

关键词 "${escapeHtml(searchTerm)}" 在所有数据中没有匹配结果

请尝试其他关键词,或使用分类筛选功能

`; } else { // 计算总项数和分类数 const totalItems = categoriesWithItems.reduce((sum, cat) => sum + (cat.items?.length || 0), 0); const categoryCount = categoriesWithItems.length; // 更新统计信息 updateKnowledgeStats(categoriesWithItems, categoryCount); // 渲染搜索结果 renderKnowledgeItemsByCategories(categoriesWithItems); } // 搜索时隐藏分页(因为搜索结果显示所有匹配结果) const paginationContainer = document.getElementById('knowledge-pagination'); if (paginationContainer) { paginationContainer.innerHTML = ''; } } catch (error) { console.error('搜索知识项失败:', error); showNotification('搜索失败: ' + error.message, 'error'); } } // 刷新知识库 async function refreshKnowledgeBase() { try { showNotification('正在扫描知识库...', 'info'); const response = await apiFetch('/api/knowledge/scan', { method: 'POST' }); if (!response.ok) { throw new Error('扫描知识库失败'); } const data = await response.json(); // 根据返回的消息显示不同的提示 if (data.items_to_index && data.items_to_index > 0) { showNotification(`扫描完成,开始索引 ${data.items_to_index} 个新添加或更新的知识项`, 'success'); } else { showNotification(data.message || '扫描完成,没有需要索引的新项或更新项', 'success'); } // 重新加载知识项(重置到第一页) await loadKnowledgeCategories(); await loadKnowledgeItems(knowledgePagination.currentCategory, 1, knowledgePagination.pageSize); // 停止现有的轮询 if (indexProgressInterval) { clearInterval(indexProgressInterval); indexProgressInterval = null; } // 如果有需要索引的项,等待一小段时间后立即更新进度 if (data.items_to_index && data.items_to_index > 0) { await new Promise(resolve => setTimeout(resolve, 500)); updateIndexProgress(); // 开始轮询进度(每2秒刷新一次) if (!indexProgressInterval) { indexProgressInterval = setInterval(updateIndexProgress, 2000); } } else { // 没有需要索引的项,也更新一次以显示当前状态 updateIndexProgress(); } } catch (error) { console.error('刷新知识库失败:', error); showNotification('刷新知识库失败: ' + error.message, 'error'); } } // 重建索引 async function rebuildKnowledgeIndex() { try { if (!confirm('确定要重建索引吗?这可能需要一些时间。')) { return; } showNotification('正在重建索引...', 'info'); // 先停止现有的轮询 if (indexProgressInterval) { clearInterval(indexProgressInterval); indexProgressInterval = null; } // 立即显示"正在重建"状态,因为重建开始时会清空旧索引 const progressContainer = document.getElementById('knowledge-index-progress'); if (progressContainer) { progressContainer.style.display = 'block'; progressContainer.innerHTML = `
🔨 正在重建索引: 准备中...
索引构建完成后,语义搜索功能将可用
`; } const response = await apiFetch('/api/knowledge/index', { method: 'POST' }); if (!response.ok) { throw new Error('重建索引失败'); } showNotification('索引重建已开始,将在后台进行', 'success'); // 等待一小段时间,确保后端已经开始处理并清空了旧索引 await new Promise(resolve => setTimeout(resolve, 500)); // 立即更新一次进度 updateIndexProgress(); // 开始轮询进度(每2秒刷新一次,比默认的3秒更频繁) if (!indexProgressInterval) { indexProgressInterval = setInterval(updateIndexProgress, 2000); } } catch (error) { console.error('重建索引失败:', error); showNotification('重建索引失败: ' + error.message, 'error'); } } // 显示添加知识项模态框 function showAddKnowledgeItemModal() { currentEditingItemId = null; document.getElementById('knowledge-item-modal-title').textContent = '添加知识'; document.getElementById('knowledge-item-category').value = ''; document.getElementById('knowledge-item-title').value = ''; document.getElementById('knowledge-item-content').value = ''; document.getElementById('knowledge-item-modal').style.display = 'block'; } // 编辑知识项 async function editKnowledgeItem(id) { try { const response = await apiFetch(`/api/knowledge/items/${id}`); if (!response.ok) { throw new Error('获取知识项失败'); } const item = await response.json(); currentEditingItemId = id; document.getElementById('knowledge-item-modal-title').textContent = '编辑知识'; document.getElementById('knowledge-item-category').value = item.category; document.getElementById('knowledge-item-title').value = item.title; document.getElementById('knowledge-item-content').value = item.content; document.getElementById('knowledge-item-modal').style.display = 'block'; } catch (error) { console.error('编辑知识项失败:', error); showNotification('编辑知识项失败: ' + error.message, 'error'); } } // 保存知识项 async function saveKnowledgeItem() { // 防止重复提交 if (isSavingKnowledgeItem) { showNotification('正在保存中,请勿重复点击...', 'warning'); return; } const category = document.getElementById('knowledge-item-category').value.trim(); const title = document.getElementById('knowledge-item-title').value.trim(); const content = document.getElementById('knowledge-item-content').value.trim(); if (!category || !title || !content) { showNotification('请填写所有必填字段', 'error'); return; } // 设置保存中标志 isSavingKnowledgeItem = true; // 获取保存按钮和取消按钮 const saveButton = document.querySelector('#knowledge-item-modal .modal-footer .btn-primary'); const cancelButton = document.querySelector('#knowledge-item-modal .modal-footer .btn-secondary'); const modal = document.getElementById('knowledge-item-modal'); const originalButtonText = saveButton ? saveButton.textContent : '保存'; const originalButtonDisabled = saveButton ? saveButton.disabled : false; // 禁用所有输入字段和按钮 const categoryInput = document.getElementById('knowledge-item-category'); const titleInput = document.getElementById('knowledge-item-title'); const contentInput = document.getElementById('knowledge-item-content'); if (categoryInput) categoryInput.disabled = true; if (titleInput) titleInput.disabled = true; if (contentInput) contentInput.disabled = true; if (cancelButton) cancelButton.disabled = true; // 设置保存按钮加载状态 if (saveButton) { saveButton.disabled = true; saveButton.style.opacity = '0.6'; saveButton.style.cursor = 'not-allowed'; saveButton.textContent = '保存中...'; } try { const url = currentEditingItemId ? `/api/knowledge/items/${currentEditingItemId}` : '/api/knowledge/items'; const method = currentEditingItemId ? 'PUT' : 'POST'; const response = await apiFetch(url, { method: method, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ category, title, content }) }); if (!response.ok) { const errorData = await response.json().catch(() => ({})); throw new Error(errorData.error || '保存知识项失败'); } const item = await response.json(); const action = currentEditingItemId ? '更新' : '创建'; const newItemCategory = item.category || category; // 保存新添加的知识项分类 // 获取当前筛选状态,以便刷新后保持 const currentCategory = document.getElementById('knowledge-category-filter-wrapper'); let selectedCategory = ''; if (currentCategory) { const selectedOption = currentCategory.querySelector('.custom-select-option.selected'); if (selectedOption) { selectedCategory = selectedOption.getAttribute('data-value') || ''; } } // 立即关闭模态框,给用户明确的反馈 closeKnowledgeItemModal(); // 显示加载状态并刷新数据(等待完成以确保数据同步) const itemsListContainer = document.getElementById('knowledge-items-list'); const originalContent = itemsListContainer ? itemsListContainer.innerHTML : ''; if (itemsListContainer) { itemsListContainer.innerHTML = '
刷新中...
'; } try { // 先刷新分类,再刷新知识项 console.log('开始刷新知识库数据...'); await loadKnowledgeCategories(); console.log('分类刷新完成,开始刷新知识项...'); // 如果新添加的知识项不在当前筛选的分类中,切换到该分类显示 let categoryToShow = selectedCategory; if (!currentEditingItemId && selectedCategory && selectedCategory !== '' && newItemCategory !== selectedCategory) { // 新添加的知识项,如果当前筛选的不是该分类,切换到新知识项的分类 categoryToShow = newItemCategory; // 更新筛选器显示(不触发加载,因为我们下面会手动加载) const trigger = document.getElementById('knowledge-category-filter-trigger'); const wrapper = document.getElementById('knowledge-category-filter-wrapper'); const dropdown = document.getElementById('knowledge-category-filter-dropdown'); if (trigger && wrapper && dropdown) { trigger.querySelector('span').textContent = newItemCategory || '全部'; dropdown.querySelectorAll('.custom-select-option').forEach(opt => { opt.classList.remove('selected'); if (opt.getAttribute('data-value') === newItemCategory) { opt.classList.add('selected'); } }); } showNotification(`✅ ${action}成功!已切换到分类"${newItemCategory}"查看新添加的知识项。`, 'success'); } // 刷新知识项列表(重置到第一页) await loadKnowledgeItems(categoryToShow, 1, knowledgePagination.pageSize); console.log('知识项刷新完成'); } catch (err) { console.error('刷新数据失败:', err); // 如果刷新失败,恢复原内容 if (itemsListContainer && originalContent) { itemsListContainer.innerHTML = originalContent; } showNotification('⚠️ 知识项已保存,但刷新列表失败,请手动刷新页面查看', 'warning'); } } catch (error) { console.error('保存知识项失败:', error); showNotification('❌ 保存知识项失败: ' + error.message, 'error'); // 如果通知系统不可用,使用alert if (typeof window.showNotification !== 'function') { alert('❌ 保存知识项失败: ' + error.message); } // 恢复输入字段和按钮状态(错误时不关闭模态框,让用户修改后重试) if (categoryInput) categoryInput.disabled = false; if (titleInput) titleInput.disabled = false; if (contentInput) contentInput.disabled = false; if (cancelButton) cancelButton.disabled = false; if (saveButton) { saveButton.disabled = false; saveButton.style.opacity = ''; saveButton.style.cursor = ''; saveButton.textContent = originalButtonText; } } finally { // 清除保存中标志 isSavingKnowledgeItem = false; } } // 删除知识项 async function deleteKnowledgeItem(id) { if (!confirm('确定要删除这个知识项吗?')) { return; } // 找到要删除的知识项卡片和删除按钮 const itemCard = document.querySelector(`.knowledge-item-card[data-id="${id}"]`); const deleteButton = itemCard ? itemCard.querySelector('.knowledge-item-delete-btn') : null; const categorySection = itemCard ? itemCard.closest('.knowledge-category-section') : null; let originalDisplay = ''; let originalOpacity = ''; let originalButtonOpacity = ''; // 设置删除按钮的加载状态 if (deleteButton) { originalButtonOpacity = deleteButton.style.opacity; deleteButton.style.opacity = '0.5'; deleteButton.style.cursor = 'not-allowed'; deleteButton.disabled = true; // 添加加载动画 const svg = deleteButton.querySelector('svg'); if (svg) { svg.style.animation = 'spin 1s linear infinite'; } } // 立即从UI中移除该项(乐观更新) if (itemCard) { originalDisplay = itemCard.style.display; originalOpacity = itemCard.style.opacity; itemCard.style.transition = 'opacity 0.3s ease-out, transform 0.3s ease-out'; itemCard.style.opacity = '0'; itemCard.style.transform = 'translateX(-20px)'; // 等待动画完成后移除 setTimeout(() => { if (itemCard.parentElement) { itemCard.remove(); // 检查分类是否还有项目,如果没有则隐藏分类标题 if (categorySection) { const remainingItems = categorySection.querySelectorAll('.knowledge-item-card'); if (remainingItems.length === 0) { categorySection.style.transition = 'opacity 0.3s ease-out'; categorySection.style.opacity = '0'; setTimeout(() => { if (categorySection.parentElement) { categorySection.remove(); } }, 300); } else { // 更新分类计数 const categoryCount = categorySection.querySelector('.knowledge-category-count'); if (categoryCount) { const newCount = remainingItems.length; categoryCount.textContent = `${newCount} 项`; } } } // 不在这里更新统计信息,等待重新加载数据后由正确的逻辑更新 } }, 300); } try { const response = await apiFetch(`/api/knowledge/items/${id}`, { method: 'DELETE' }); if (!response.ok) { const errorData = await response.json().catch(() => ({})); throw new Error(errorData.error || '删除知识项失败'); } // 显示成功通知 showNotification('✅ 删除成功!知识项已从系统中移除。', 'success'); // 重新加载数据以确保数据同步(保持当前页码) await loadKnowledgeCategories(); await loadKnowledgeItems(knowledgePagination.currentCategory, knowledgePagination.currentPage, knowledgePagination.pageSize); } catch (error) { console.error('删除知识项失败:', error); // 如果删除失败,恢复该项显示 if (itemCard && originalDisplay !== 'none') { itemCard.style.display = originalDisplay || ''; itemCard.style.opacity = originalOpacity || '1'; itemCard.style.transform = ''; itemCard.style.transition = ''; // 如果分类被移除了,需要恢复 if (categorySection && !categorySection.parentElement) { // 需要重新加载来恢复(保持当前分页状态) await loadKnowledgeItems(knowledgePagination.currentCategory, knowledgePagination.currentPage, knowledgePagination.pageSize); } } // 恢复删除按钮状态 if (deleteButton) { deleteButton.style.opacity = originalButtonOpacity || ''; deleteButton.style.cursor = ''; deleteButton.disabled = false; const svg = deleteButton.querySelector('svg'); if (svg) { svg.style.animation = ''; } } showNotification('❌ 删除知识项失败: ' + error.message, 'error'); } } // 临时更新统计信息(删除后) function updateKnowledgeStatsAfterDelete() { const statsContainer = document.getElementById('knowledge-stats'); if (!statsContainer) return; const allItems = document.querySelectorAll('.knowledge-item-card'); const allCategories = document.querySelectorAll('.knowledge-category-section'); const totalItems = allItems.length; const categoryCount = allCategories.length; // 计算总内容大小(这里简化处理,实际应该从服务器获取) const statsItems = statsContainer.querySelectorAll('.knowledge-stat-item'); if (statsItems.length >= 2) { const totalItemsSpan = statsItems[0].querySelector('.knowledge-stat-value'); const categoryCountSpan = statsItems[1].querySelector('.knowledge-stat-value'); if (totalItemsSpan) { totalItemsSpan.textContent = totalItems; } if (categoryCountSpan) { categoryCountSpan.textContent = categoryCount; } } } // 关闭知识项模态框 function closeKnowledgeItemModal() { const modal = document.getElementById('knowledge-item-modal'); if (modal) { modal.style.display = 'none'; } // 重置编辑状态 currentEditingItemId = null; isSavingKnowledgeItem = false; // 恢复所有输入字段和按钮状态 const categoryInput = document.getElementById('knowledge-item-category'); const titleInput = document.getElementById('knowledge-item-title'); const contentInput = document.getElementById('knowledge-item-content'); const saveButton = document.querySelector('#knowledge-item-modal .modal-footer .btn-primary'); const cancelButton = document.querySelector('#knowledge-item-modal .modal-footer .btn-secondary'); if (categoryInput) { categoryInput.disabled = false; categoryInput.value = ''; } if (titleInput) { titleInput.disabled = false; titleInput.value = ''; } if (contentInput) { contentInput.disabled = false; contentInput.value = ''; } if (saveButton) { saveButton.disabled = false; saveButton.style.opacity = ''; saveButton.style.cursor = ''; saveButton.textContent = '保存'; } if (cancelButton) { cancelButton.disabled = false; } } // 加载检索日志 async function loadRetrievalLogs(conversationId = '', messageId = '') { try { let url = '/api/knowledge/retrieval-logs?limit=100'; if (conversationId) { url += `&conversationId=${encodeURIComponent(conversationId)}`; } if (messageId) { url += `&messageId=${encodeURIComponent(messageId)}`; } const response = await apiFetch(url); if (!response.ok) { throw new Error('获取检索日志失败'); } const data = await response.json(); renderRetrievalLogs(data.logs || []); } catch (error) { console.error('加载检索日志失败:', error); // 即使加载失败,也显示空状态而不是一直显示"加载中..." renderRetrievalLogs([]); // 只在非空筛选条件下才显示错误通知(避免在没有数据时显示错误) if (conversationId || messageId) { showNotification('加载检索日志失败: ' + error.message, 'error'); } } } // 渲染检索日志 function renderRetrievalLogs(logs) { const container = document.getElementById('retrieval-logs-list'); if (!container) return; // 更新统计信息(即使为空数组也要更新) updateRetrievalStats(logs); if (logs.length === 0) { container.innerHTML = '
暂无检索记录
'; retrievalLogsData = []; return; } // 保存日志数据供详情查看使用 retrievalLogsData = logs; container.innerHTML = logs.map((log, index) => { // 处理retrievedItems:可能是数组、字符串数组,或者特殊标记 let itemCount = 0; let hasResults = false; if (log.retrievedItems) { if (Array.isArray(log.retrievedItems)) { // 过滤掉特殊标记 const realItems = log.retrievedItems.filter(id => id !== '_has_results'); itemCount = realItems.length; // 如果有特殊标记,表示有结果但ID未知,显示为"有结果" if (log.retrievedItems.includes('_has_results')) { hasResults = true; // 如果有真实ID,使用真实数量;否则显示为"有结果"(不显示具体数量) if (itemCount === 0) { itemCount = -1; // -1 表示有结果但数量未知 } } else { hasResults = itemCount > 0; } } else if (typeof log.retrievedItems === 'string') { // 如果是字符串,尝试解析JSON try { const parsed = JSON.parse(log.retrievedItems); if (Array.isArray(parsed)) { const realItems = parsed.filter(id => id !== '_has_results'); itemCount = realItems.length; if (parsed.includes('_has_results')) { hasResults = true; if (itemCount === 0) { itemCount = -1; } } else { hasResults = itemCount > 0; } } } catch (e) { // 解析失败,忽略 } } } const timeAgo = getTimeAgo(log.createdAt); return `
${hasResults ? '🔍' : '⚠️'}
${escapeHtml(log.query || '无查询内容')}
🕒 ${timeAgo} ${log.riskType ? `📁 ${escapeHtml(log.riskType)}` : ''}
${hasResults ? (itemCount > 0 ? `${itemCount} 项` : '有结果') : '无结果'}
${log.conversationId ? `
对话ID ${escapeHtml(log.conversationId)}
` : ''} ${log.messageId ? `
消息ID ${escapeHtml(log.messageId)}
` : ''}
检索结果 ${hasResults ? (itemCount > 0 ? `找到 ${itemCount} 个相关知识项` : '找到相关知识项(数量未知)') : '未找到匹配的知识项'}
${hasResults && log.retrievedItems && log.retrievedItems.length > 0 ? `
检索到的知识项:
${log.retrievedItems.slice(0, 3).map((itemId, idx) => ` ${idx + 1} `).join('')} ${log.retrievedItems.length > 3 ? `+${log.retrievedItems.length - 3}` : ''}
` : ''}
`; }).join(''); } // 更新检索统计信息 function updateRetrievalStats(logs) { const statsContainer = document.getElementById('retrieval-stats'); if (!statsContainer) return; const totalLogs = logs.length; // 判断是否有结果:检查retrievedItems数组,过滤掉特殊标记后长度>0,或者包含特殊标记 const successfulLogs = logs.filter(log => { if (!log.retrievedItems) return false; if (Array.isArray(log.retrievedItems)) { const realItems = log.retrievedItems.filter(id => id !== '_has_results'); return realItems.length > 0 || log.retrievedItems.includes('_has_results'); } return false; }).length; // 计算总知识项数(只计算真实ID,不包括特殊标记) const totalItems = logs.reduce((sum, log) => { if (!log.retrievedItems) return sum; if (Array.isArray(log.retrievedItems)) { const realItems = log.retrievedItems.filter(id => id !== '_has_results'); return sum + realItems.length; } return sum; }, 0); const successRate = totalLogs > 0 ? ((successfulLogs / totalLogs) * 100).toFixed(1) : 0; statsContainer.innerHTML = `
总检索次数 ${totalLogs}
成功检索 ${successfulLogs}
成功率 ${successRate}%
检索到知识项 ${totalItems}
`; } // 获取相对时间 function getTimeAgo(timeStr) { if (!timeStr) return ''; // 处理时间字符串,支持多种格式 let date; if (typeof timeStr === 'string') { // 首先尝试直接解析(支持RFC3339/ISO8601格式) date = new Date(timeStr); // 如果解析失败,尝试其他格式 if (isNaN(date.getTime())) { // SQLite格式: "2006-01-02 15:04:05" 或带时区 const sqliteMatch = timeStr.match(/(\d{4}-\d{2}-\d{2}[\sT]\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:[+-]\d{2}:\d{2}|Z)?)/); if (sqliteMatch) { let timeStr2 = sqliteMatch[1].replace(' ', 'T'); // 如果没有时区信息,添加Z表示UTC if (!timeStr2.includes('Z') && !timeStr2.match(/[+-]\d{2}:\d{2}$/)) { timeStr2 += 'Z'; } date = new Date(timeStr2); } } // 如果还是失败,尝试更宽松的格式 if (isNaN(date.getTime())) { // 尝试匹配 "YYYY-MM-DD HH:MM:SS" 格式 const match = timeStr.match(/(\d{4})-(\d{2})-(\d{2})[\sT](\d{2}):(\d{2}):(\d{2})/); if (match) { date = new Date( parseInt(match[1]), parseInt(match[2]) - 1, parseInt(match[3]), parseInt(match[4]), parseInt(match[5]), parseInt(match[6]) ); } } } else { date = new Date(timeStr); } // 检查日期是否有效 if (isNaN(date.getTime())) { return formatTime(timeStr); } // 检查日期是否合理(不在1970年之前,不在未来太远) const year = date.getFullYear(); if (year < 1970 || year > 2100) { return formatTime(timeStr); } const now = new Date(); const diff = now - date; // 如果时间差为负数或过大(可能是解析错误),返回格式化时间 if (diff < 0 || diff > 365 * 24 * 60 * 60 * 1000 * 10) { // 超过10年认为是错误 return formatTime(timeStr); } const seconds = Math.floor(diff / 1000); const minutes = Math.floor(seconds / 60); const hours = Math.floor(minutes / 60); const days = Math.floor(hours / 24); if (days > 0) return `${days}天前`; if (hours > 0) return `${hours}小时前`; if (minutes > 0) return `${minutes}分钟前`; return '刚刚'; } // 截断ID显示 function truncateId(id) { if (!id || id.length <= 16) return id; return id.substring(0, 8) + '...' + id.substring(id.length - 8); } // 筛选检索日志 function filterRetrievalLogs() { const conversationId = document.getElementById('retrieval-logs-conversation-id').value.trim(); const messageId = document.getElementById('retrieval-logs-message-id').value.trim(); loadRetrievalLogs(conversationId, messageId); } // 刷新检索日志 function refreshRetrievalLogs() { filterRetrievalLogs(); } // 删除检索日志 async function deleteRetrievalLog(id, index) { if (!confirm('确定要删除这条检索记录吗?')) { return; } // 找到要删除的日志卡片和删除按钮 const logCard = document.querySelector(`.retrieval-log-card[data-index="${index}"]`); const deleteButton = logCard ? logCard.querySelector('.retrieval-log-delete-btn') : null; let originalButtonOpacity = ''; let originalButtonDisabled = false; // 设置删除按钮的加载状态 if (deleteButton) { originalButtonOpacity = deleteButton.style.opacity; originalButtonDisabled = deleteButton.disabled; deleteButton.style.opacity = '0.5'; deleteButton.style.cursor = 'not-allowed'; deleteButton.disabled = true; // 添加加载动画 const svg = deleteButton.querySelector('svg'); if (svg) { svg.style.animation = 'spin 1s linear infinite'; } } // 立即从UI中移除该项(乐观更新) if (logCard) { logCard.style.transition = 'opacity 0.3s ease-out, transform 0.3s ease-out'; logCard.style.opacity = '0'; logCard.style.transform = 'translateX(-20px)'; // 等待动画完成后移除 setTimeout(() => { if (logCard.parentElement) { logCard.remove(); // 更新统计信息(临时更新,稍后会重新加载) updateRetrievalStatsAfterDelete(); } }, 300); } try { const response = await apiFetch(`/api/knowledge/retrieval-logs/${id}`, { method: 'DELETE' }); if (!response.ok) { const errorData = await response.json().catch(() => ({})); throw new Error(errorData.error || '删除检索日志失败'); } // 显示成功通知 showNotification('✅ 删除成功!检索记录已从系统中移除。', 'success'); // 从内存中移除该项 if (retrievalLogsData && index >= 0 && index < retrievalLogsData.length) { retrievalLogsData.splice(index, 1); } // 重新加载数据以确保数据同步 const conversationId = document.getElementById('retrieval-logs-conversation-id')?.value.trim() || ''; const messageId = document.getElementById('retrieval-logs-message-id')?.value.trim() || ''; await loadRetrievalLogs(conversationId, messageId); } catch (error) { console.error('删除检索日志失败:', error); // 如果删除失败,恢复该项显示 if (logCard) { logCard.style.opacity = '1'; logCard.style.transform = ''; logCard.style.transition = ''; } // 恢复删除按钮状态 if (deleteButton) { deleteButton.style.opacity = originalButtonOpacity || ''; deleteButton.style.cursor = ''; deleteButton.disabled = originalButtonDisabled; const svg = deleteButton.querySelector('svg'); if (svg) { svg.style.animation = ''; } } showNotification('❌ 删除检索日志失败: ' + error.message, 'error'); } } // 临时更新统计信息(删除后) function updateRetrievalStatsAfterDelete() { const statsContainer = document.getElementById('retrieval-stats'); if (!statsContainer) return; const allLogs = document.querySelectorAll('.retrieval-log-card'); const totalLogs = allLogs.length; // 计算成功检索数 const successfulLogs = Array.from(allLogs).filter(card => { return card.classList.contains('has-results'); }).length; // 计算总知识项数(简化处理,实际应该从服务器获取) const totalItems = Array.from(allLogs).reduce((sum, card) => { const badge = card.querySelector('.retrieval-log-result-badge'); if (badge && badge.classList.contains('success')) { const text = badge.textContent.trim(); const match = text.match(/(\d+)\s*项/); if (match) { return sum + parseInt(match[1]); } else if (text === '有结果') { return sum + 1; // 简化处理,假设为1 } } return sum; }, 0); const successRate = totalLogs > 0 ? ((successfulLogs / totalLogs) * 100).toFixed(1) : 0; statsContainer.innerHTML = `
总检索次数 ${totalLogs}
成功检索 ${successfulLogs}
成功率 ${successRate}%
检索到知识项 ${totalItems}
`; } // 显示检索日志详情 async function showRetrievalLogDetails(index) { if (!retrievalLogsData || index < 0 || index >= retrievalLogsData.length) { showNotification('无法获取检索详情', 'error'); return; } const log = retrievalLogsData[index]; // 获取检索到的知识项详情 let retrievedItemsDetails = []; if (log.retrievedItems && Array.isArray(log.retrievedItems)) { const realItemIds = log.retrievedItems.filter(id => id !== '_has_results'); if (realItemIds.length > 0) { try { // 批量获取知识项详情 const itemPromises = realItemIds.map(async (itemId) => { try { const response = await apiFetch(`/api/knowledge/items/${itemId}`); if (response.ok) { return await response.json(); } return null; } catch (err) { console.error(`获取知识项 ${itemId} 失败:`, err); return null; } }); const items = await Promise.all(itemPromises); retrievedItemsDetails = items.filter(item => item !== null); } catch (err) { console.error('批量获取知识项详情失败:', err); } } } // 显示详情模态框 showRetrievalLogDetailsModal(log, retrievedItemsDetails); } // 显示检索日志详情模态框 function showRetrievalLogDetailsModal(log, retrievedItems) { // 创建或获取模态框 let modal = document.getElementById('retrieval-log-details-modal'); if (!modal) { modal = document.createElement('div'); modal.id = 'retrieval-log-details-modal'; modal.className = 'modal'; modal.innerHTML = ` `; document.body.appendChild(modal); } // 填充内容 const content = document.getElementById('retrieval-log-details-content'); const timeAgo = getTimeAgo(log.createdAt); const fullTime = formatTime(log.createdAt); let itemsHtml = ''; if (retrievedItems.length > 0) { itemsHtml = retrievedItems.map((item, idx) => { // 提取内容预览 let preview = item.content || ''; preview = preview.replace(/^#+\s+/gm, ''); preview = preview.replace(/```[\s\S]*?```/g, ''); preview = preview.replace(/`[^`]+`/g, ''); preview = preview.replace(/\[([^\]]+)\]\([^\)]+\)/g, '$1'); preview = preview.replace(/\n+/g, ' ').replace(/\s+/g, ' ').trim(); const previewText = preview.length > 200 ? preview.substring(0, 200) + '...' : preview; return `

${idx + 1}. ${escapeHtml(item.title || '未命名')}

${escapeHtml(item.category || '未分类')}
${item.filePath ? `
📁 ${escapeHtml(item.filePath)}
` : ''}
${escapeHtml(previewText || '无内容预览')}
`; }).join(''); } else { itemsHtml = '
未找到知识项详情
'; } content.innerHTML = `

查询信息

查询内容:
${escapeHtml(log.query || '无查询内容')}

检索信息

${log.riskType ? `
风险类型
${escapeHtml(log.riskType)}
` : ''}
检索时间
${timeAgo}
检索结果
${retrievedItems.length} 个知识项
${log.conversationId || log.messageId ? `

关联信息

${log.conversationId ? `
对话ID
${escapeHtml(log.conversationId)}
` : ''} ${log.messageId ? `
消息ID
${escapeHtml(log.messageId)}
` : ''}
` : ''}

检索到的知识项 (${retrievedItems.length})

${itemsHtml}
`; modal.style.display = 'block'; } // 关闭检索日志详情模态框 function closeRetrievalLogDetailsModal() { const modal = document.getElementById('retrieval-log-details-modal'); if (modal) { modal.style.display = 'none'; } } // 点击模态框外部关闭 window.addEventListener('click', function(event) { const modal = document.getElementById('retrieval-log-details-modal'); if (event.target === modal) { closeRetrievalLogDetailsModal(); } }); // 页面切换时加载数据 if (typeof switchPage === 'function') { const originalSwitchPage = switchPage; window.switchPage = function(page) { originalSwitchPage(page); if (page === 'knowledge-management') { loadKnowledgeCategories(); loadKnowledgeItems(knowledgePagination.currentCategory, 1, knowledgePagination.pageSize); updateIndexProgress(); // 更新索引进度 } else if (page === 'knowledge-retrieval-logs') { loadRetrievalLogs(); // 切换到其他页面时停止轮询 if (indexProgressInterval) { clearInterval(indexProgressInterval); indexProgressInterval = null; } } else { // 切换到其他页面时停止轮询 if (indexProgressInterval) { clearInterval(indexProgressInterval); indexProgressInterval = null; } } }; } // 页面卸载时清理定时器 window.addEventListener('beforeunload', function() { if (indexProgressInterval) { clearInterval(indexProgressInterval); indexProgressInterval = null; } }); // 工具函数 function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } function formatTime(timeStr) { if (!timeStr) return ''; // 处理时间字符串,支持多种格式 let date; if (typeof timeStr === 'string') { // 首先尝试直接解析(支持RFC3339/ISO8601格式) date = new Date(timeStr); // 如果解析失败,尝试其他格式 if (isNaN(date.getTime())) { // SQLite格式: "2006-01-02 15:04:05" 或带时区 const sqliteMatch = timeStr.match(/(\d{4}-\d{2}-\d{2}[\sT]\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:[+-]\d{2}:\d{2}|Z)?)/); if (sqliteMatch) { let timeStr2 = sqliteMatch[1].replace(' ', 'T'); // 如果没有时区信息,添加Z表示UTC if (!timeStr2.includes('Z') && !timeStr2.match(/[+-]\d{2}:\d{2}$/)) { timeStr2 += 'Z'; } date = new Date(timeStr2); } } // 如果还是失败,尝试更宽松的格式 if (isNaN(date.getTime())) { // 尝试匹配 "YYYY-MM-DD HH:MM:SS" 格式 const match = timeStr.match(/(\d{4})-(\d{2})-(\d{2})[\sT](\d{2}):(\d{2}):(\d{2})/); if (match) { date = new Date( parseInt(match[1]), parseInt(match[2]) - 1, parseInt(match[3]), parseInt(match[4]), parseInt(match[5]), parseInt(match[6]) ); } } } else { date = new Date(timeStr); } // 如果日期无效,检查是否是零值时间 if (isNaN(date.getTime())) { // 检查是否是零值时间的字符串形式 if (typeof timeStr === 'string' && (timeStr.includes('0001-01-01') || timeStr.startsWith('0001'))) { return ''; } console.warn('无法解析时间:', timeStr); return ''; } // 检查日期是否合理(不在1970年之前,不在未来太远) const year = date.getFullYear(); if (year < 1970 || year > 2100) { // 如果是零值时间(0001-01-01),返回空字符串,不显示 if (year === 1) { return ''; } console.warn('时间值不合理:', timeStr, '解析为:', date); return ''; } return date.toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false }); } // 显示通知 function showNotification(message, type = 'info') { // 如果存在全局通知系统(且不是当前函数),使用它 if (typeof window.showNotification === 'function' && window.showNotification !== showNotification) { window.showNotification(message, type); return; } // 否则使用自定义的toast通知 showToastNotification(message, type); } // 显示Toast通知 function showToastNotification(message, type = 'info') { // 创建通知容器(如果不存在) let container = document.getElementById('toast-notification-container'); if (!container) { container = document.createElement('div'); container.id = 'toast-notification-container'; container.style.cssText = ` position: fixed; top: 20px; right: 20px; z-index: 10000; display: flex; flex-direction: column; gap: 12px; pointer-events: none; `; document.body.appendChild(container); } // 创建通知元素 const toast = document.createElement('div'); toast.className = `toast-notification toast-${type}`; // 根据类型设置颜色 const typeStyles = { success: { background: '#28a745', color: '#fff', icon: '✅' }, error: { background: '#dc3545', color: '#fff', icon: '❌' }, info: { background: '#17a2b8', color: '#fff', icon: 'ℹ️' }, warning: { background: '#ffc107', color: '#000', icon: '⚠️' } }; const style = typeStyles[type] || typeStyles.info; toast.style.cssText = ` background: ${style.background}; color: ${style.color}; padding: 14px 20px; border-radius: 8px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); min-width: 300px; max-width: 500px; pointer-events: auto; animation: slideInRight 0.3s ease-out; display: flex; align-items: center; gap: 12px; font-size: 0.9375rem; line-height: 1.5; word-wrap: break-word; `; toast.innerHTML = ` ${style.icon} ${escapeHtml(message)} `; container.appendChild(toast); // 自动移除(成功消息显示5秒,错误消息显示7秒,其他显示4秒) const duration = type === 'success' ? 5000 : type === 'error' ? 7000 : 4000; setTimeout(() => { if (toast.parentElement) { toast.style.animation = 'slideOutRight 0.3s ease-out'; setTimeout(() => { if (toast.parentElement) { toast.remove(); } }, 300); } }, duration); } // 添加CSS动画(如果不存在) if (!document.getElementById('toast-notification-styles')) { const style = document.createElement('style'); style.id = 'toast-notification-styles'; style.textContent = ` @keyframes slideInRight { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } } @keyframes slideOutRight { from { transform: translateX(0); opacity: 1; } to { transform: translateX(100%); opacity: 0; } } `; document.head.appendChild(style); } // 点击模态框外部关闭 window.addEventListener('click', function(event) { const modal = document.getElementById('knowledge-item-modal'); if (event.target === modal) { closeKnowledgeItemModal(); } }); // 切换到设置页面(用于功能未启用时的提示) function switchToSettings() { if (typeof switchPage === 'function') { switchPage('settings'); // 等待设置页面加载后,切换到知识库配置部分 setTimeout(() => { if (typeof switchSettingsSection === 'function') { // 查找知识库配置部分(通常在基本设置中) const knowledgeSection = document.querySelector('[data-section="knowledge"]'); if (knowledgeSection) { switchSettingsSection('knowledge'); } else { // 如果没有独立的知识库部分,切换到基本设置 switchSettingsSection('basic'); // 滚动到知识库配置区域 setTimeout(() => { const knowledgeEnabledCheckbox = document.getElementById('knowledge-enabled'); if (knowledgeEnabledCheckbox) { knowledgeEnabledCheckbox.scrollIntoView({ behavior: 'smooth', block: 'center' }); // 高亮显示 knowledgeEnabledCheckbox.parentElement.style.transition = 'background-color 0.3s'; knowledgeEnabledCheckbox.parentElement.style.backgroundColor = '#e3f2fd'; setTimeout(() => { knowledgeEnabledCheckbox.parentElement.style.backgroundColor = ''; }, 2000); } }, 300); } } }, 100); } } // 自定义下拉组件交互 document.addEventListener('DOMContentLoaded', function() { const wrapper = document.getElementById('knowledge-category-filter-wrapper'); const trigger = document.getElementById('knowledge-category-filter-trigger'); if (wrapper && trigger) { // 点击触发器打开/关闭下拉菜单 trigger.addEventListener('click', function(e) { e.stopPropagation(); wrapper.classList.toggle('open'); }); // 点击外部关闭下拉菜单 document.addEventListener('click', function(e) { if (!wrapper.contains(e.target)) { wrapper.classList.remove('open'); } }); // 选择选项时更新选中状态 const dropdown = document.getElementById('knowledge-category-filter-dropdown'); if (dropdown) { // 默认选中"全部"选项 const defaultOption = dropdown.querySelector('.custom-select-option[data-value=""]'); if (defaultOption) { defaultOption.classList.add('selected'); } dropdown.addEventListener('click', function(e) { const option = e.target.closest('.custom-select-option'); if (option) { // 移除之前的选中状态 dropdown.querySelectorAll('.custom-select-option').forEach(opt => { opt.classList.remove('selected'); }); // 添加选中状态 option.classList.add('selected'); } }); } } });