(function () { var page = document.querySelector(".knowledge-page"); if (!page) { return; } var documentForm = document.getElementById("knowledgeDocumentForm"); var documentStatus = document.getElementById("knowledgeDocumentStatus"); var documentTable = document.getElementById("knowledgeDocumentTable"); var documentSearch = document.getElementById("knowledgeDocumentSearch"); var searchForm = document.getElementById("knowledgeSearchForm"); var queryInput = document.getElementById("knowledgeSearchQuery"); var results = document.getElementById("knowledgeSearchResults"); var sourceSearch = document.getElementById("knowledgeSourceSearch"); var sourceTable = document.getElementById("knowledgeSourceTable"); var documentFileInput = document.getElementById("knowledgeDocumentFile"); var uploadDropzone = document.getElementById("knowledgeUploadDropzone"); function csrfToken() { var cookie = document.cookie.split("; ").find(function (item) { return item.indexOf("csrftoken=") === 0; }); return cookie ? decodeURIComponent(cookie.split("=")[1]) : ""; } function escapeHtml(value) { return String(value || "") .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } async function patchDocument(row, payload) { var response = await fetch(row.getAttribute("data-detail-url"), { method: "PATCH", headers: { "Content-Type": "application/json", "X-CSRFToken": csrfToken(), }, body: JSON.stringify(payload), }); if (!response.ok) { throw new Error("知识库材料更新失败。"); } return response.json(); } async function deleteDocument(row) { var response = await fetch(row.getAttribute("data-detail-url"), { method: "DELETE", headers: { "X-CSRFToken": csrfToken() }, }); if (!response.ok) { throw new Error("知识库材料删除失败。"); } } async function indexDocument(row) { var response = await fetch(row.getAttribute("data-index-url"), { method: "POST", headers: { "X-CSRFToken": csrfToken() }, }); if (!response.ok) { throw new Error("知识库材料解析入库失败。"); } return response.json(); } function renderResults(payload) { if (!results) { return; } if (payload.error_message) { results.innerHTML = '

' + escapeHtml(payload.error_message) + "

"; return; } if (!payload.results || !payload.results.length) { results.innerHTML = '

未检索到依据片段。

'; return; } results.innerHTML = payload.results .map(function (item, index) { return [ '
', "
结果 " + (index + 1) + "" + escapeHtml(item.source || "法规材料") + "
", "

" + escapeHtml(item.text || "").slice(0, 600) + "

", item.score === null || item.score === undefined ? "" : "score: " + escapeHtml(item.score) + "", "
", ].join(""); }) .join(""); } if (documentForm) { documentForm.addEventListener("submit", async function (event) { event.preventDefault(); var formData = new FormData(documentForm); if (documentStatus) { documentStatus.textContent = "上传并解析入库中..."; } try { var response = await fetch(page.getAttribute("data-document-url"), { method: "POST", headers: { "X-CSRFToken": csrfToken() }, body: formData, }); if (!response.ok) { throw new Error("新增材料失败。"); } window.location.reload(); } catch (error) { if (documentStatus) { documentStatus.textContent = error.message || "新增材料失败。"; } } }); } if (documentFileInput && documentStatus) { documentFileInput.addEventListener("change", function () { var file = documentFileInput.files && documentFileInput.files[0]; documentStatus.textContent = file ? "已选择:" + file.name : "上传后会进入当前账号的全局知识库。"; }); } if (uploadDropzone && documentFileInput) { uploadDropzone.addEventListener("click", function () { documentFileInput.click(); }); uploadDropzone.addEventListener("keydown", function (event) { if (event.key === "Enter" || event.key === " ") { event.preventDefault(); documentFileInput.click(); } }); ["dragenter", "dragover"].forEach(function (eventName) { uploadDropzone.addEventListener(eventName, function (event) { event.preventDefault(); uploadDropzone.classList.add("dragging"); }); }); ["dragleave", "drop"].forEach(function (eventName) { uploadDropzone.addEventListener(eventName, function (event) { event.preventDefault(); uploadDropzone.classList.remove("dragging"); }); }); uploadDropzone.addEventListener("drop", function (event) { var files = event.dataTransfer && event.dataTransfer.files; if (!files || !files.length) { return; } documentFileInput.files = files; documentFileInput.dispatchEvent(new Event("change", { bubbles: true })); }); } if (documentTable) { documentTable.addEventListener("click", async function (event) { var button = event.target.closest("[data-kb-action]"); if (!button) { return; } var row = button.closest("tr[data-document-id]"); if (!row) { return; } var action = button.getAttribute("data-kb-action"); try { if (action === "edit") { var nameCell = row.querySelector(".attachment-name"); var nextName = window.prompt("请输入新的材料名称", nameCell ? nameCell.textContent.trim() : ""); if (nextName) { await patchDocument(row, { display_name: nextName }); window.location.reload(); } } else if (action === "toggle") { await patchDocument(row, { is_active: button.textContent.trim() === "启用" }); window.location.reload(); } else if (action === "index") { button.disabled = true; button.textContent = "解析中"; await indexDocument(row); window.location.reload(); } else if (action === "delete" && window.confirm("确认删除该知识库材料?")) { await deleteDocument(row); window.location.reload(); } } catch (error) { window.alert(error.message || "知识库材料操作失败。"); } }); } if (searchForm && queryInput) { searchForm.addEventListener("submit", async function (event) { event.preventDefault(); var query = queryInput.value.trim(); if (!query) { renderResults({ error_message: "请输入检索问题。" }); return; } results.innerHTML = '

检索中...

'; try { var response = await fetch(page.getAttribute("data-search-url"), { method: "POST", headers: { "Content-Type": "application/json", "X-CSRFToken": csrfToken(), }, body: JSON.stringify({ query: query }), }); renderResults(await response.json()); } catch (error) { renderResults({ error_message: "检索失败,请稍后重试。" }); } }); } function bindTableSearch(input, table, selector) { if (!input || !table) { return; } input.addEventListener("input", function () { var keyword = input.value.trim().toLowerCase(); table.querySelectorAll(selector).forEach(function (row) { row.hidden = keyword && row.textContent.toLowerCase().indexOf(keyword) === -1; }); }); } bindTableSearch(documentSearch, documentTable, "tbody tr[data-document-id]"); bindTableSearch(sourceSearch, sourceTable, "tbody tr[data-source-name]"); })();