// js/chatbot/chatbot-actions.js window.ChatbotActions = { /** * 删除指定索引的消息。 * @param {number} index - 要删除消息的索引。 */ deleteMessage: function(index) { if (confirm('确定要删除这条消息吗?此操作无法撤销。')) { if (window.ChatbotCore && typeof window.ChatbotCore.deleteMessageFromHistory === 'function' && typeof window.ChatbotCore.getCurrentDocId === 'function') { const docId = window.ChatbotCore.getCurrentDocId(); window.ChatbotCore.deleteMessageFromHistory(docId, index, window.ChatbotUI.updateChatbotUI); } else { console.error('ChatbotActions: ChatbotCore.deleteMessageFromHistory or ChatbotCore.getCurrentDocId function is not defined.'); alert('删除消息的功能暂未完全配置,请联系管理员。'); } } }, /** * 处理 "提供 PDF" 按钮点击:直接上传当前阅读的 PDF 到服务器 OSS, * 获取 URL 后作为消息发送给大模型。 */ handleProvidePdf: async function() { try { console.log('[handleProvidePdf] 开始执行'); // 获取当前文档数据 const currentData = window.data; if (!currentData || !currentData.name) { alert('当前没有打开任何文档。'); return; } // IndexedDB 中存储的 ID 格式是 ${fileName}_${fileSize} const dbDocId = currentData.id || `${currentData.name}_${currentData.size || 0}`; console.log('[handleProvidePdf] 文档信息:', { name: currentData.name, size: currentData.size, dbDocId: dbDocId }); // 我们需要拿到真正的 PDF 文件对象 let pdfFileContent = null; let pdfSource = ''; const currentDocName = currentData.name; if (currentDocName && Array.isArray(window.pdfFiles)) { pdfFileContent = window.pdfFiles.find(f => f.name === currentDocName); if (pdfFileContent) { pdfSource = 'window.pdfFiles'; console.log('[handleProvidePdf] 从 window.pdfFiles 获取到文件'); } } // 如果界面里有全局保存着 file 或 blob,可以直接调用 if (!pdfFileContent) { pdfFileContent = window.currentOriginalFile || window.currentPdfBlob; if (pdfFileContent) { pdfSource = window.currentOriginalFile ? 'window.currentOriginalFile' : 'window.currentPdfBlob'; console.log('[handleProvidePdf] 从', pdfSource, '获取到文件'); } } // 如果内存中没有,尝试从 IndexedDB 中获取 originalPdfBase64 if (!pdfFileContent) { console.log('[handleProvidePdf] 内存中没有,尝试从 IndexedDB 获取, dbDocId:', dbDocId); try { const record = await window.getResultFromDB(dbDocId); console.log('[handleProvidePdf] IndexedDB 记录:', record ? '找到' : '未找到'); if (record) { console.log('[handleProvidePdf] 记录 metadata 字段:', record.metadata ? Object.keys(record.metadata) : '无'); } if (record && record.metadata && record.metadata.originalPdfBase64) { // 将 base64 转换为 Blob const base64 = record.metadata.originalPdfBase64; console.log('[handleProvidePdf] originalPdfBase64 长度:', base64.length); const binaryString = atob(base64); const bytes = new Uint8Array(binaryString.length); for (let i = 0; i < binaryString.length; i++) { bytes[i] = binaryString.charCodeAt(i); } pdfFileContent = new Blob([bytes], { type: 'application/pdf' }); pdfSource = 'IndexedDB (originalPdfBase64)'; console.log('[handleProvidePdf] 从 IndexedDB 转换成功'); } } catch (e) { console.warn('[handleProvidePdf] 从 IndexedDB 获取 PDF 失败:', e); } } if (!pdfFileContent) { alert('未能获取 PDF 文件,请确保当前文档已保存原始 PDF 数据。'); return; } console.log('[handleProvidePdf] PDF 来源:', pdfSource); console.log('[handleProvidePdf] PDF 文件信息:', { type: pdfFileContent.type, size: pdfFileContent.size, sizeMB: (pdfFileContent.size / 1024 / 1024).toFixed(2) + ' MB', isBlob: pdfFileContent instanceof Blob }); // 禁用发送按钮,防止多次点击,设置UI处于加载状态 const btn = document.getElementById('chatbot-provide-pdf-btn'); const originHtml = btn.innerHTML; btn.innerHTML = ''; btn.disabled = true; // 调用上传 console.log('[handleProvidePdf] 开始上传到 OSS...'); const ossUrl = await window.uploadFileToOssViaProxy(pdfFileContent, `${currentDocName}`); console.log('[handleProvidePdf] OSS 上传成功, URL:', ossUrl); // 构建包含 file_url 的消息内容(智谱 AI 格式) const userMessageContent = [ { type: 'file_url', file_url: { url: ossUrl } }, { type: 'text', text: '请阅读这个 PDF 文档,并根据内容随时准备回答我的问题。' } ]; console.log('[handleProvidePdf] 发送给大模型的消息内容:', JSON.stringify(userMessageContent, null, 2)); // 恢复按钮状态 btn.innerHTML = originHtml; btn.disabled = false; // 让聊天框自动发送消息 if (window.ChatbotCore && typeof window.ChatbotCore.sendChatbotMessage === 'function') { // 第四个参数可以用来设置页面上的 display 文本,避免一长串 URL const displayText = `(已向 AI 提供当前 PDF 文档供阅读)`; console.log('[handleProvidePdf] 调用 sendChatbotMessage 发送消息'); window.ChatbotCore.sendChatbotMessage(userMessageContent, window.ChatbotUI.updateChatbotUI, null, displayText); } console.log('[handleProvidePdf] 执行完成'); } catch (err) { console.error('[handleProvidePdf] 提供PDF发生错误:', err); alert('上传或提供 PDF 失败:' + err.message); const btn = document.getElementById('chatbot-provide-pdf-btn'); if (btn) { btn.innerHTML = ''; btn.disabled = false; } } }, /** * 重新发送指定索引的用户消息。 * 将删除此消息之后的所有历史记录,然后重新发送此消息。 * @param {number} index - 要重发消息的索引。 */ resendUserMessage: function(index) { if (!window.ChatbotCore || !window.ChatbotCore.chatHistory || !window.ChatbotCore.chatHistory[index] || typeof window.ChatbotCore.getCurrentDocId !== 'function' || typeof window.ChatbotCore.saveChatHistory !== 'function') { console.error('ChatbotActions: Cannot resend message, core functions or history/message index is invalid.'); alert('无法重发消息,核心功能或聊天记录无效。'); return; } const docId = window.ChatbotCore.getCurrentDocId(); const messageToResend = { ...window.ChatbotCore.chatHistory[index] }; // 浅拷贝以防意外修改 if (messageToResend.role !== 'user') { alert('只能重发您发送的消息。'); return; } let textContent = ''; let hasNonTextContent = false; let originalUserMessageParts = []; // 用于重新发送 if (Array.isArray(messageToResend.content)) { originalUserMessageParts = messageToResend.content.map(part => ({ ...part })); // 深拷贝parts const textPart = originalUserMessageParts.find(part => part.type === 'text'); if (textPart) { textContent = textPart.text; } if (originalUserMessageParts.some(part => part.type === 'image_url')) { hasNonTextContent = true; } } else if (typeof messageToResend.content === 'string') { textContent = messageToResend.content; originalUserMessageParts = [{ type: 'text', text: textContent }]; } if (hasNonTextContent && textContent) { if (!confirm('此消息包含图片。目前重发功能仅支持文本部分。是否继续重发文本内容?(此操作将删除后续所有对话)')) { return; } // 如果用户确认,我们只重发文本部分 originalUserMessageParts = [{ type: 'text', text: textContent }]; } else if (hasNonTextContent && !textContent) { alert('此消息仅包含图片,目前无法直接重发。请尝试重新上传图片并输入文本。'); return; } else { // 纯文本消息,或用户已确认仅重发文本 if (!confirm('确定要重发这条消息吗?此操作将删除该消息之后的所有对话记录。')) { return; } } if (!textContent.trim() && !hasNonTextContent) { // 如果清除了图片后没有文本了 alert('没有可重发的文本内容。'); return; } // 删除此消息之后的所有历史记录 if (index < window.ChatbotCore.chatHistory.length -1) { // 确保不是最后一条消息 window.ChatbotCore.chatHistory.splice(index + 1); } // 注意:此时 messageToResend 自身还在 chatHistory 中,我们会在 sendChatbotMessage 前将其移除,或者 sendChatbotMessage 内部会处理好上下文。 // 为了简化,我们先从历史记录中移除旧的这条,sendChatbotMessage 会重新添加它。 window.ChatbotCore.chatHistory.splice(index, 1); // 更新并保存被截断的历史记录 window.ChatbotCore.saveChatHistory(docId, window.ChatbotCore.chatHistory); window.ChatbotUI.updateChatbotUI(); // 更新UI以反映历史记录的变化 // 清空当前输入框(如果用户正在输入) const inputField = document.getElementById('chatbot-input'); if (inputField) inputField.value = ''; // 清空已选图片 (因为我们只重发提取的文本或原始消息的图片) if (window.ChatbotImageUtils) { window.ChatbotImageUtils.selectedChatbotImages = []; window.ChatbotImageUtils.updateSelectedImagesPreview(); } // 准备发送内容,可以是字符串或数组 let sendVal = originalUserMessageParts.length === 1 && originalUserMessageParts[0].type === 'text' ? originalUserMessageParts[0].text : originalUserMessageParts; let displayVal = sendVal; // 简单起见,让显示值和发送值一致 if (window.PromptConstructor && typeof window.PromptConstructor.enhanceUserPrompt === 'function') { sendVal = window.PromptConstructor.enhanceUserPrompt(sendVal); } // 确保 ChatbotCore.sendChatbotMessage 的参数和行为 // 第三个参数是 onComplete (通常是null), 第四个是 displayVal window.ChatbotCore.sendChatbotMessage(sendVal, window.ChatbotUI.updateChatbotUI, null, displayVal); } };