273 lines
8.0 KiB
JavaScript
273 lines
8.0 KiB
JavaScript
const uploadArea = document.getElementById('upload-area');
|
||
const fileInput = document.getElementById('file-input');
|
||
const documentList = document.getElementById('document-list');
|
||
const chatMessages = document.getElementById('chat-messages');
|
||
const questionInput = document.getElementById('question-input');
|
||
const docCount = document.getElementById('doc-count');
|
||
const charCount = document.getElementById('char-count');
|
||
const toast = document.getElementById('toast');
|
||
|
||
function showToast(message, duration = 3000) {
|
||
toast.textContent = message;
|
||
toast.classList.add('show');
|
||
|
||
setTimeout(() => {
|
||
toast.classList.remove('show');
|
||
}, duration);
|
||
}
|
||
|
||
uploadArea.addEventListener('click', () => fileInput.click());
|
||
|
||
uploadArea.addEventListener('dragover', (e) => {
|
||
e.preventDefault();
|
||
uploadArea.style.borderColor = '#1e3c72';
|
||
uploadArea.style.background = '#e8f0fe';
|
||
});
|
||
|
||
uploadArea.addEventListener('dragleave', () => {
|
||
uploadArea.style.borderColor = '#cbd5e0';
|
||
uploadArea.style.background = 'transparent';
|
||
});
|
||
|
||
uploadArea.addEventListener('drop', (e) => {
|
||
e.preventDefault();
|
||
uploadArea.style.borderColor = '#cbd5e0';
|
||
uploadArea.style.background = 'transparent';
|
||
|
||
const files = e.dataTransfer.files;
|
||
if (files.length > 0) {
|
||
uploadFile(files[0]);
|
||
}
|
||
});
|
||
|
||
fileInput.addEventListener('change', (e) => {
|
||
if (e.target.files.length > 0) {
|
||
uploadFile(e.target.files[0]);
|
||
}
|
||
});
|
||
|
||
async function uploadFile(file) {
|
||
const formData = new FormData();
|
||
formData.append('file', file);
|
||
|
||
try {
|
||
showToast('⏳ 正在上传文档...');
|
||
|
||
const response = await fetch('/api/upload', {
|
||
method: 'POST',
|
||
body: formData
|
||
});
|
||
|
||
const data = await response.json();
|
||
|
||
if (data.error) {
|
||
showToast(`❌ ${data.error}`);
|
||
return;
|
||
}
|
||
|
||
loadDocuments();
|
||
showToast(`✅ 文档 "${data.name}" 上传成功,正在处理中...`);
|
||
addMessage('bot', `📄 文档 "${data.name}" 已上传,系统正在智能解析文档内容...`);
|
||
|
||
setTimeout(() => {
|
||
loadDocuments();
|
||
}, 2000);
|
||
|
||
} catch (error) {
|
||
showToast('❌ 上传失败,请重试');
|
||
console.error(error);
|
||
}
|
||
}
|
||
|
||
async function loadDocuments() {
|
||
try {
|
||
const response = await fetch('/api/documents');
|
||
const documents = await response.json();
|
||
|
||
docCount.textContent = `${documents.length} 个文档`;
|
||
|
||
if (documents.length === 0) {
|
||
documentList.innerHTML = `
|
||
<div class="empty-state">
|
||
<div class="empty-icon">📭</div>
|
||
<p>暂无文档</p>
|
||
<p class="empty-hint">上传文档后即可开始问答</p>
|
||
</div>
|
||
`;
|
||
return;
|
||
}
|
||
|
||
documentList.innerHTML = documents.map(doc => `
|
||
<div class="document-item">
|
||
<div class="document-info">
|
||
<div class="document-name">📄 ${doc.name}</div>
|
||
<div class="document-status ${doc.status}">
|
||
${doc.status === 'processing' ? '⏳ 处理中...' : '✅ 已完成'}
|
||
</div>
|
||
</div>
|
||
<button class="delete-btn" onclick="deleteDocument('${doc.id}')">🗑️ 删除</button>
|
||
</div>
|
||
`).join('');
|
||
|
||
} catch (error) {
|
||
console.error(error);
|
||
showToast('❌ 加载文档列表失败');
|
||
}
|
||
}
|
||
|
||
async function deleteDocument(docId) {
|
||
if (!confirm('确定要删除这个文档吗?')) {
|
||
return;
|
||
}
|
||
|
||
try {
|
||
showToast('⏳ 正在删除文档...');
|
||
|
||
await fetch(`/api/documents/${docId}`, {
|
||
method: 'DELETE'
|
||
});
|
||
|
||
loadDocuments();
|
||
showToast('✅ 文档已删除');
|
||
addMessage('bot', '🗑️ 文档已从知识库中删除');
|
||
|
||
} catch (error) {
|
||
showToast('❌ 删除失败,请重试');
|
||
console.error(error);
|
||
}
|
||
}
|
||
|
||
async function askQuestion() {
|
||
const question = questionInput.value.trim();
|
||
|
||
if (!question) {
|
||
showToast('⚠️ 请输入问题');
|
||
questionInput.focus();
|
||
return;
|
||
}
|
||
|
||
if (question.length < 3) {
|
||
showToast('⚠️ 问题太短,请输入至少3个字符');
|
||
questionInput.focus();
|
||
return;
|
||
}
|
||
|
||
addMessage('user', question);
|
||
questionInput.value = '';
|
||
updateCharCount();
|
||
|
||
try {
|
||
showToast('🤖 正在思考中...');
|
||
|
||
const response = await fetch('/api/ask', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify({ question })
|
||
});
|
||
|
||
const data = await response.json();
|
||
|
||
if (data.error) {
|
||
showToast(`❌ ${data.error}`);
|
||
addMessage('bot', `❌ 抱歉,${data.error}`);
|
||
return;
|
||
}
|
||
|
||
let answerHtml = `<p>${data.answer}</p>`;
|
||
|
||
if (data.sources && data.sources.length > 0) {
|
||
answerHtml += `
|
||
<div class="sources">
|
||
<div class="sources-title">📚 参考来源:</div>
|
||
${data.sources.map(source => `
|
||
<div class="source-item">📄 ${source.name} (第${source.page}页)</div>
|
||
`).join('')}
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
showToast('✅ 回答完成');
|
||
addMessage('bot', answerHtml);
|
||
|
||
} catch (error) {
|
||
showToast('❌ 回答问题时出错了,请重试');
|
||
addMessage('bot', '❌ 抱歉,回答问题时出错了,请重试');
|
||
console.error(error);
|
||
}
|
||
}
|
||
|
||
function addMessage(type, content) {
|
||
const messageDiv = document.createElement('div');
|
||
messageDiv.className = `message ${type}`;
|
||
messageDiv.innerHTML = `
|
||
<div class="message-content">
|
||
${content}
|
||
</div>
|
||
`;
|
||
|
||
chatMessages.appendChild(messageDiv);
|
||
chatMessages.scrollTop = chatMessages.scrollHeight;
|
||
}
|
||
|
||
function updateCharCount() {
|
||
const length = questionInput.value.length;
|
||
charCount.textContent = `${length} 字`;
|
||
|
||
if (length > 500) {
|
||
charCount.style.color = '#e53e3e';
|
||
} else if (length > 300) {
|
||
charCount.style.color = '#d69e2e';
|
||
} else {
|
||
charCount.style.color = '#718096';
|
||
}
|
||
}
|
||
|
||
async function loadConversationHistory() {
|
||
try {
|
||
const response = await fetch('/api/conversations');
|
||
const conversations = await response.json();
|
||
|
||
if (conversations.length === 0) {
|
||
addMessage('bot', '👋 欢迎使用智能知识库问答系统!上传文档后,您可以向我提问任何与文档相关的问题。');
|
||
return;
|
||
}
|
||
|
||
conversations.forEach(conv => {
|
||
addMessage('user', conv.question);
|
||
|
||
let answerHtml = `<p>${conv.answer}</p>`;
|
||
|
||
if (conv.sources && conv.sources.length > 0) {
|
||
answerHtml += `
|
||
<div class="sources">
|
||
<div class="sources-title">📚 参考来源:</div>
|
||
${conv.sources.map(source => `
|
||
<div class="source-item">📄 ${source.name} (第${source.page}页)</div>
|
||
`).join('')}
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
addMessage('bot', answerHtml);
|
||
});
|
||
|
||
} catch (error) {
|
||
console.error(error);
|
||
addMessage('bot', '👋 欢迎使用智能知识库问答系统!上传文档后,您可以向我提问任何与文档相关的问题。');
|
||
}
|
||
}
|
||
|
||
questionInput.addEventListener('input', updateCharCount);
|
||
|
||
questionInput.addEventListener('keydown', (e) => {
|
||
if (e.key === 'Enter' && !e.shiftKey) {
|
||
e.preventDefault();
|
||
askQuestion();
|
||
}
|
||
});
|
||
|
||
loadDocuments();
|
||
loadConversationHistory();
|