// 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);
}
};