// 全局变量 let allKnowledge = []; let availableTags = []; let currentPage = 1; let pageSize = 200; let totalPages = 1; let totalCount = 0; let isSearchMode = false; let selectedIds = new Set(); let _allTools = []; let _activeCategory = 'all'; // 加载 Tags async function loadTags() { const res = await fetch('/api/knowledge/meta/tags'); const data = await res.json(); availableTags = data.tags; renderTagsFilter(); } function renderTagsFilter() { const container = document.getElementById('tagsFilterContainer'); if (availableTags.length === 0) { container.innerHTML = '

暂无 tags

'; return; } container.innerHTML = availableTags.map(tag => `` ).join(''); } // 加载知识列表 async function loadKnowledge(page = 1) { const params = new URLSearchParams(); params.append('page', page); params.append('page_size', pageSize); const selectedTypes = Array.from(document.querySelectorAll('.type-filter:checked')).map(el => el.value); if (selectedTypes.length > 0) { params.append('types', selectedTypes.join(',')); } const selectedTags = Array.from(document.querySelectorAll('.tag-filter:checked')).map(el => el.value); if (selectedTags.length > 0) { params.append('tags', selectedTags.join(',')); } const ownerFilter = document.getElementById('ownerFilter').value.trim(); if (ownerFilter) { params.append('owner', ownerFilter); } const scopesFilter = document.getElementById('scopesFilter').value.trim(); if (scopesFilter) { params.append('scopes', scopesFilter); } const selectedStatus = Array.from(document.querySelectorAll('.status-filter:checked')).map(el => el.value); if (selectedStatus.length > 0) { params.append('status', selectedStatus.join(',')); } try { const res = await fetch(`/api/knowledge?${params.toString()}`); if (!res.ok) { console.error('加载失败:', res.status, res.statusText); document.getElementById('knowledgeList').innerHTML = '

加载失败,请刷新页面重试

'; return; } const data = await res.json(); allKnowledge = data.results || []; currentPage = data.pagination.page; totalPages = data.pagination.total_pages; totalCount = data.pagination.total; renderKnowledge(allKnowledge); updatePagination(); } catch (error) { console.error('加载错误:', error); document.getElementById('knowledgeList').innerHTML = '

加载错误: ' + error.message + '

'; } } function applyFilters() { currentPage = 1; loadKnowledge(currentPage); } function goToPage(page) { if (page < 1 || page > totalPages) return; loadKnowledge(page); } function updatePagination() { const paginationDiv = document.getElementById('pagination'); const pageInfo = document.getElementById('pageInfo'); const prevBtn = document.getElementById('prevBtn'); const nextBtn = document.getElementById('nextBtn'); if (totalPages <= 1) { paginationDiv.classList.add('hidden'); } else { paginationDiv.classList.remove('hidden'); pageInfo.textContent = `第 ${currentPage} / ${totalPages} 页 (共 ${totalCount} 条)`; prevBtn.disabled = currentPage === 1; nextBtn.disabled = currentPage === totalPages; } } // 搜索功能 async function performSearch() { const query = document.getElementById('searchInput').value.trim(); if (!query) { alert('请输入搜索内容'); return; } isSearchMode = true; const statusDiv = document.getElementById('searchStatus'); statusDiv.textContent = '搜索中...'; statusDiv.classList.remove('hidden'); try { const params = new URLSearchParams(); params.append('q', query); params.append('top_k', '20'); params.append('min_score', '1'); const selectedTypes = Array.from(document.querySelectorAll('.type-filter:checked')).map(el => el.value); if (selectedTypes.length > 0) { params.append('types', selectedTypes.join(',')); } const ownerFilter = document.getElementById('ownerFilter').value.trim(); if (ownerFilter) { params.append('owner', ownerFilter); } const res = await fetch(`/api/knowledge/search?${params.toString()}`); if (!res.ok) { throw new Error(`搜索失败: ${res.status}`); } const data = await res.json(); allKnowledge = data.results || []; statusDiv.textContent = `找到 ${allKnowledge.length} 条相关知识${data.reranked ? ' (已智能排序)' : ''}`; renderKnowledge(allKnowledge); document.getElementById('pagination').classList.add('hidden'); } catch (error) { console.error('搜索错误:', error); statusDiv.textContent = '搜索失败: ' + error.message; statusDiv.classList.add('text-red-500'); } } function clearSearch() { document.getElementById('searchInput').value = ''; document.getElementById('searchStatus').classList.add('hidden'); document.getElementById('searchStatus').classList.remove('text-red-500'); isSearchMode = false; currentPage = 1; loadKnowledge(currentPage); } // 渲染知识列表 function renderKnowledge(list) { const container = document.getElementById('knowledgeList'); if (list.length === 0) { container.innerHTML = '

暂无知识

'; return; } container.innerHTML = list.map(k => { let types = []; if (Array.isArray(k.types)) { types = k.types; } else if (typeof k.types === 'string') { if (k.types.startsWith('[')) { try { types = JSON.parse(k.types); } catch (e) { console.error('解析types失败:', k.types, e); types = [k.types]; } } else { types = [k.types]; } } const eval_data = k.eval || {}; const isChecked = selectedIds.has(k.id); const statusColor = { 'approved': 'bg-green-100 text-green-800', 'checked': 'bg-blue-100 text-blue-800', 'rejected': 'bg-red-100 text-red-800', 'pending': 'bg-yellow-100 text-yellow-800', 'processing': 'bg-orange-100 text-orange-800', }; const statusClass = statusColor[k.status] || 'bg-gray-100 text-gray-800'; const statusLabel = k.status || 'approved'; const toolIds = (k.resource_ids || []).filter(id => id.startsWith('tools/')); const toolTagsHtml = toolIds.length > 0 ? `
${toolIds.map(tid => { const name = tid.split('/').pop(); return ` 🔧 ${name} `; }).join('')}
` : ''; return `
${types.map(t => `${t}`).join('')}
${statusLabel} ${eval_data.score || 3}/5

${escapeHtml(k.task)}

${escapeHtml(k.content.substring(0, 150))}${k.content.length > 150 ? '...' : ''}

Owner: ${k.owner || 'N/A'} ${new Date(k.created_at).toLocaleDateString()}
${toolTagsHtml}
`; }).join(''); } // 选择相关函数 function toggleSelect(id) { if (selectedIds.has(id)) { selectedIds.delete(id); } else { selectedIds.add(id); } updateBatchDeleteButton(); } function toggleSelectAll() { if (selectedIds.size === allKnowledge.length) { selectedIds.clear(); } else { selectedIds.clear(); allKnowledge.forEach(k => selectedIds.add(k.id)); } renderKnowledge(allKnowledge); updateBatchDeleteButton(); } function updateBatchDeleteButton() { const count = selectedIds.size; document.getElementById('selectedCount').textContent = count; document.getElementById('verifyCount').textContent = count; document.getElementById('batchDeleteBtn').disabled = count === 0; document.getElementById('batchVerifyBtn').disabled = count === 0; document.getElementById('selectAllBtn').textContent = selectedIds.size === allKnowledge.length ? '取消全选' : '全选'; } // 批量删除 async function batchDelete() { if (selectedIds.size === 0) return; if (!confirm(`确定要删除选中的 ${selectedIds.size} 条知识吗?此操作不可恢复!`)) return; try { const ids = Array.from(selectedIds); const res = await fetch('/api/knowledge/batch_delete', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(ids) }); if (!res.ok) throw new Error(`删除失败: ${res.status}`); const data = await res.json(); alert(`成功删除 ${data.deleted_count} 条知识`); selectedIds.clear(); updateBatchDeleteButton(); if (isSearchMode) { clearSearch(); } else { loadKnowledge(currentPage); } } catch (error) { console.error('批量删除错误:', error); alert('删除失败: ' + error.message); } } // 批量验证 async function batchVerify() { if (selectedIds.size === 0) return; if (!confirm(`确定要批量验证通过选中的 ${selectedIds.size} 条知识吗?`)) return; const btn = document.getElementById('batchVerifyBtn'); if (btn) { btn.disabled = true; btn.textContent = `处理中...`; } try { const ids = Array.from(selectedIds); const res = await fetch('/api/knowledge/batch_verify', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ knowledge_ids: ids, action: 'approve', verified_by: 'user' }) }); if (!res.ok) throw new Error('请求失败: ' + res.status); selectedIds.clear(); updateBatchDeleteButton(); if (isSearchMode) { clearSearch(); } else { loadKnowledge(currentPage); } } catch (error) { console.error('批量验证错误:', error); alert('验证失败: ' + error.message); if (btn) { btn.disabled = false; updateBatchDeleteButton(); } } } // 验证单个知识 async function verifyKnowledge(id, action, btn) { if (btn) { btn.disabled = true; btn._origText = btn.textContent; btn.textContent = '处理中...'; } try { const res = await fetch('/api/knowledge/' + id + '/verify', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ action }) }); if (!res.ok) throw new Error('请求失败: ' + res.status); if (isSearchMode) { clearSearch(); } else { loadKnowledge(currentPage); } } catch (error) { console.error('验证错误:', error); alert('操作失败: ' + error.message); if (btn) { btn.disabled = false; btn.textContent = btn._origText; } } } // 模态框操作 function openAddModal() { document.getElementById('modalTitle').textContent = '新增知识'; document.getElementById('knowledgeForm').reset(); document.getElementById('editId').value = ''; document.querySelectorAll('.type-checkbox').forEach(el => el.checked = false); document.getElementById('modal').classList.remove('hidden'); } async function openEditModal(id) { let k = allKnowledge.find(item => item.id === id); if (!k) { try { const res = await fetch('/api/knowledge/' + encodeURIComponent(id)); if (!res.ok) { alert('知识未找到: ' + id); return; } k = await res.json(); } catch (e) { alert('获取知识失败: ' + e.message); return; } } document.getElementById('modalTitle').textContent = '编辑知识'; document.getElementById('editId').value = k.id; document.getElementById('taskInput').value = k.task || ''; document.getElementById('contentInput').value = k.content || ''; document.getElementById('tagsInput').value = JSON.stringify(k.tags || {}); const scopes = Array.isArray(k.scopes) ? k.scopes : []; document.getElementById('scopesInput').value = scopes.join(', '); document.getElementById('ownerInput').value = k.owner || ''; const types = Array.isArray(k.types) ? k.types : []; document.querySelectorAll('.type-checkbox').forEach(el => { el.checked = types.includes(el.value); }); let rels = []; if (Array.isArray(k.relationships)) { rels = k.relationships; } else if (typeof k.relationships === 'string' && k.relationships.startsWith('[')) { try { rels = JSON.parse(k.relationships); } catch(e) {} } const section = document.getElementById('relationshipsSection'); if (rels.length > 0) { const typeColor = { superset: 'text-green-700', subset: 'text-orange-600', conflict: 'text-red-600', complement: 'text-blue-600', duplicate: 'text-gray-500' }; document.getElementById('relationshipsList').innerHTML = rels.map(r => `
[${r.type}] ${r.target}
` ).join(''); section.classList.remove('hidden'); } else { section.classList.add('hidden'); } document.getElementById('modal').classList.remove('hidden'); } function closeModal() { document.getElementById('modal').classList.add('hidden'); } // 表单提交处理 document.addEventListener('DOMContentLoaded', function() { document.getElementById('knowledgeForm').addEventListener('submit', async (e) => { e.preventDefault(); const editId = document.getElementById('editId').value; const task = document.getElementById('taskInput').value; const content = document.getElementById('contentInput').value; const types = Array.from(document.querySelectorAll('.type-checkbox:checked')).map(el => el.value); const tagsText = document.getElementById('tagsInput').value.trim(); const scopesText = document.getElementById('scopesInput').value.trim(); const owner = document.getElementById('ownerInput').value.trim(); let tags = {}; if (tagsText) { try { tags = JSON.parse(tagsText); } catch (e) { alert('Tags JSON 格式错误'); return; } } const scopes = scopesText ? scopesText.split(',').map(s => s.trim()).filter(s => s) : ['org:cybertogether']; if (editId) { const res = await fetch(`/api/knowledge/${editId}`, { method: 'PATCH', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({task, content, types, tags, scopes, owner}) }); if (!res.ok) { alert('更新失败'); return; } } else { const res = await fetch('/api/knowledge', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({task, content, types, tags, scopes, owner}) }); if (!res.ok) { alert('新增失败'); return; } } closeModal(); await loadKnowledge(); }); // 初始化加载 loadTags(); loadKnowledge(); }); // 工具表相关函数 async function openToolTableModal(targetToolId = null) { document.getElementById('toolTableModal').classList.remove('hidden'); if (_allTools.length === 0) { await loadToolList(); } else { renderCategoryTabs(); renderToolList('all'); } if (targetToolId) { const targetTool = _allTools.find(t => t.id === targetToolId); if (targetTool) { const cat = targetTool.metadata && targetTool.metadata.category ? targetTool.metadata.category : 'other'; renderToolList(cat); } loadToolDetail(targetToolId); setTimeout(() => { const el = document.querySelector(`.tool-item[data-id="${targetToolId}"]`); if (el) el.scrollIntoView({ behavior: 'smooth', block: 'center' }); }, 100); } } function closeToolTableModal() { document.getElementById('toolTableModal').classList.add('hidden'); } async function loadToolList() { try { const res = await fetch('/api/resource?limit=1000'); const data = await res.json(); _allTools = (data.results || []).filter(r => r.id.startsWith('tools/')); renderCategoryTabs(); renderToolList('all'); } catch (err) { console.error('加载工具列表失败', err); document.getElementById('toolList').innerHTML = '

加载失败

'; } } function renderCategoryTabs() { const cats = ['all', ...new Set(_allTools.map(t => t.metadata && t.metadata.category ? t.metadata.category : 'other'))]; document.getElementById('toolCategoryTabs').innerHTML = cats.map(cat => { const isActive = cat === _activeCategory; const activeClass = 'bg-indigo-600 text-white border-indigo-600'; const inactiveClass = 'bg-white text-gray-600 border-gray-300 hover:border-indigo-400'; return ``; }).join(''); } function renderToolList(category) { _activeCategory = category; document.querySelectorAll('.tool-cat-tab').forEach(btn => { const isCurrent = btn.id === `tab_${category}`; btn.className = `tool-cat-tab px-4 py-1.5 rounded-full text-sm font-medium border transition ${ isCurrent ? 'bg-indigo-600 text-white border-indigo-600' : 'bg-white text-gray-600 border-gray-300 hover:border-indigo-400' }`; }); const filtered = category === 'all' ? _allTools : _allTools.filter(t => (t.metadata && t.metadata.category ? t.metadata.category : 'other') === category); const listHtml = filtered.length === 0 ? '

该分类下暂无工具

' : filtered.map(t => `
${escapeHtml(t.title || t.id.split('/').pop())}
${escapeHtml(t.content_type || '')}
`).join(''); document.getElementById('toolList').innerHTML = listHtml; } async function loadToolDetail(id) { document.querySelectorAll('.tool-item').forEach(el => { if (el.dataset.id === id) { el.classList.add('border-indigo-500', 'ring-1', 'ring-indigo-500'); el.classList.remove('border-gray-200'); } else { el.classList.remove('border-indigo-500', 'ring-1', 'ring-indigo-500'); el.classList.add('border-gray-200'); } }); const detailEl = document.getElementById('toolDetail'); detailEl.innerHTML = '

加载详情中...

'; try { const res = await fetch('/api/resource/' + id); const tool = await res.json(); const knowledgeIds = (tool.metadata && tool.metadata.knowledge_ids) ? tool.metadata.knowledge_ids : []; const knowledgeHtml = knowledgeIds.length === 0 ? '暂无' : knowledgeIds.map(kid => ` ${kid.length > 24 ? kid.slice(0, 24) + '...' : kid} `).join(''); const toolhubItems = (tool.metadata && tool.metadata.toolhub_items) ? tool.metadata.toolhub_items : []; const toolhubHtml = toolhubItems.length === 0 ? '暂无' : toolhubItems.map(item => { const [id, desc] = Object.entries(item)[0]; return ` ${escapeHtml(id)}: ${escapeHtml(desc)} `; }).join(''); const meta = tool.metadata || {}; const scenariosMd = Array.isArray(meta.scenarios) && meta.scenarios.length > 0 ? meta.scenarios.map(s => `
  • ${escapeHtml(s)}
  • `).join('') : '
  • 暂无
  • '; detailEl.innerHTML = `

    ${escapeHtml(tool.title || id)}

    📁 分类: ${escapeHtml(meta.category || '–')} 🏷️ 状态: ${escapeHtml(meta.status || '–')} 📌 Slug: ${escapeHtml(meta.tool_slug || '–')}
    🔗 关联知识: ${knowledgeHtml}
    🔧 工具项: ${toolhubHtml}

    基础概览

    工具版本: ${escapeHtml(meta.version || '–')}
    功能介绍:
    ${escapeHtml(meta.description || '暂无')}

    使用指南

    用法:
    ${escapeHtml(meta.usage || '暂无')}
    应用场景:
      ${scenariosMd}

    技术规格

    输入:
    ${escapeHtml(meta.input || '暂无')}
    输出:
    ${escapeHtml(meta.output || '暂无')}
    ${meta.source ? `

    消息信源

    ${escapeHtml(meta.source)}
    ` : ''} ${tool.body ? `

    补充说明 (文档内容)

    ${typeof marked !== 'undefined' ? marked.parse(tool.body) : escapeHtml(tool.body)}
    ` : ''}
    `; } catch (err) { detailEl.innerHTML = '
    加载详情失败,请检查网络或日志
    '; console.error(err); } } async function openKnowledgeDetailModal(id) { document.getElementById('knowledgeDetailModal').classList.remove('hidden'); const contentEl = document.getElementById('knowledgeDetailContent'); contentEl.innerHTML = '

    加载中...

    '; try { const res = await fetch(`/api/knowledge/${encodeURIComponent(id)}`); if (!res.ok) { contentEl.innerHTML = '

    知识未找到

    '; return; } const k = await res.json(); const statusColor = { 'approved': 'bg-green-100 text-green-800', 'checked': 'bg-blue-100 text-blue-800', 'rejected': 'bg-red-100 text-red-800', 'pending': 'bg-yellow-100 text-yellow-800', }; const types = Array.isArray(k.types) ? k.types : []; const tags = k.tags || {}; const tagKeys = Object.keys(tags); contentEl.innerHTML = `
    ${types.map(t => `${escapeHtml(t)}`).join('')} ${escapeHtml(k.status || '–')}

    ${escapeHtml(k.task || '')}

    ${escapeHtml(k.content || '')}
    📌 ID:${escapeHtml(k.id || '')}
    👤 Owner:${escapeHtml(k.owner || '–')}
    🕐 创建:${k.created_at ? new Date(k.created_at * 1000).toLocaleString() : '–'}
    ${tagKeys.length > 0 ? `
    🏷️ Tags:${tagKeys.map(t => `${escapeHtml(t)}`).join(' ')}
    ` : ''}
    `; } catch (err) { contentEl.innerHTML = '

    加载失败

    '; console.error(err); } } function closeKnowledgeDetailModal() { document.getElementById('knowledgeDetailModal').classList.add('hidden'); } function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; }