255 lines
11 KiB
JavaScript
255 lines
11 KiB
JavaScript
// 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 = '<i class="fa-solid fa-spinner fa-spin"></i>';
|
||
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 = '<iconify-icon icon="carbon:document-pdf" width="20"></iconify-icon>';
|
||
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);
|
||
}
|
||
}; |