2025-06-17 20:14:11 +08:00

301 lines
11 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 页面加载完成后执行
document.addEventListener('DOMContentLoaded', function() {
// 加载机器人角色列表
loadRobotCharacters();
// 初始化图片上传表单
initUploadImageForm();
// 初始化聊天表单
initChatForm();
});
// 加载机器人角色列表
async function loadRobotCharacters() {
const robotSelect = document.getElementById('robotSelect');
try {
const response = await fetch('/imgsearcherApi/api/robots');
const data = await response.json();
if (data.robots && data.robots.length > 0) {
// 添加机器人选项
data.robots.forEach(robot => {
const option = document.createElement('option');
option.value = robot.robot_id;
option.textContent = robot.name;
robotSelect.appendChild(option);
});
}
} catch (error) {
console.error('加载机器人角色失败:', error);
}
}
// 初始化图片上传表单
function initUploadImageForm() {
const form = document.getElementById('uploadImageForm');
const fileInput = document.getElementById('chatImageFile');
const previewContainer = document.getElementById('uploadPreview');
const previewImage = document.getElementById('previewImage');
const chatContainer = document.getElementById('chatContainer');
const imageTypeElement = document.getElementById('imageType');
const imageDescriptionElement = document.getElementById('imageDescription');
// 图片选择预览
fileInput.addEventListener('change', function() {
if (this.files && this.files[0]) {
const reader = new FileReader();
reader.onload = function(e) {
previewImage.src = e.target.result;
previewContainer.classList.remove('d-none');
};
reader.readAsDataURL(this.files[0]);
}
});
// 表单提交
form.addEventListener('submit', async function(e) {
e.preventDefault();
const formData = new FormData(this);
// 显示上传中状态
const submitButton = form.querySelector('button[type="submit"]');
const originalButtonText = submitButton.innerHTML;
submitButton.disabled = true;
submitButton.innerHTML = '<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> 上传中...';
try {
const response = await fetch('/imgsearcherApi/api/upload-chat-image', {
method: 'POST',
body: formData
});
const data = await response.json();
if (data.error) {
alert(`上传失败: ${data.error}`);
return;
}
// 显示图片信息
if (data.image_type) {
imageTypeElement.textContent = data.image_type;
imageTypeElement.classList.remove('d-none');
} else {
imageTypeElement.classList.add('d-none');
}
if (data.description) {
imageDescriptionElement.textContent = data.description;
imageDescriptionElement.classList.remove('d-none');
} else {
imageDescriptionElement.classList.add('d-none');
}
// 显示聊天界面
chatContainer.classList.remove('d-none');
// 显示欢迎消息
const chatMessages = document.getElementById('chatMessages');
chatMessages.innerHTML = '';
const welcomeMessage = document.createElement('div');
welcomeMessage.className = 'message message-assistant';
let welcomeText = '';
// 如果选择了机器人角色,使用机器人的自我介绍
if (data.robot) {
welcomeText = `你好!我是${data.robot.name}`;
// 存储机器人信息到页面数据中
document.getElementById('chatContainer').dataset.robotName = data.robot.name;
// 显示机器人信息在界面上
const robotInfoElement = document.createElement('div');
robotInfoElement.className = 'alert alert-info mt-2 mb-3';
robotInfoElement.innerHTML = `<strong>当前角色:${data.robot.name}</strong><br><small>${data.robot.background}</small>`;
document.getElementById('chatContainer').prepend(robotInfoElement);
} else {
welcomeText = '你好我是AI助手';
}
welcomeText += '可以帮你分析这张图片并回答问题。';
if (data.image_type && data.description) {
welcomeText += `我看到这是一张${data.image_type}的图片,${data.description}。你有什么想问的吗?`;
} else {
welcomeText += '你有什么想问的吗?';
}
welcomeMessage.innerHTML = `
<div class="message-content">${welcomeText}</div>
<div class="message-time">${getCurrentTime()}</div>
`;
chatMessages.appendChild(welcomeMessage);
// 滚动到底部
chatMessages.scrollTop = chatMessages.scrollHeight;
} catch (error) {
alert(`上传失败: ${error.message}`);
} finally {
// 恢复按钮状态
submitButton.disabled = false;
submitButton.innerHTML = originalButtonText;
}
});
}
// 初始化聊天表单
function initChatForm() {
const form = document.getElementById('chatForm');
const messageInput = document.getElementById('messageInput');
const chatMessages = document.getElementById('chatMessages');
form.addEventListener('submit', async function(e) {
e.preventDefault();
const message = messageInput.value.trim();
if (!message) return;
// 添加用户消息到聊天界面
addMessage('user', message);
// 清空输入框
messageInput.value = '';
// 显示正在输入指示器
const typingIndicator = document.createElement('div');
typingIndicator.className = 'typing-indicator';
typingIndicator.innerHTML = '<span></span><span></span><span></span>';
chatMessages.appendChild(typingIndicator);
// 滚动到底部
chatMessages.scrollTop = chatMessages.scrollHeight;
try {
const response = await fetch('/imgsearcherApi/api/chat', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
message: message
})
});
const data = await response.json();
// 移除正在输入指示器
chatMessages.removeChild(typingIndicator);
if (data.error) {
alert(`对话失败: ${data.error}`);
return;
}
// 添加AI回复到聊天界面
addMessage('assistant', data.reply);
} catch (error) {
// 移除正在输入指示器
if (typingIndicator.parentNode === chatMessages) {
chatMessages.removeChild(typingIndicator);
}
alert(`对话失败: ${error.message}`);
}
});
}
// 添加消息到聊天界面
async function addMessage(role, content) {
const chatMessages = document.getElementById('chatMessages');
const messageElement = document.createElement('div');
messageElement.className = `message message-${role}`;
messageElement.innerHTML = `
<div class="message-content">${formatMessage(content)}</div>
<div class="message-time">${getCurrentTime()}</div>
`;
chatMessages.appendChild(messageElement);
// 如果是AI回复调用TTS并播放
if (role === 'assistant') {
try {
const ttsResp = await fetch('/imgsearcherApi/api/tts', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
text: content
})
});
if (ttsResp.ok) {
const blob = await ttsResp.blob();
const audioUrl = URL.createObjectURL(blob);
const audio = document.createElement('audio');
audio.src = audioUrl;
audio.autoplay = true;
// 可选:提供播放控制按钮
const controls = document.createElement('div');
controls.className = 'audio-controls mt-1';
const playBtn = document.createElement('button');
playBtn.type = 'button';
playBtn.className = 'btn btn-sm btn-outline-secondary';
playBtn.textContent = '🔊 播放';
playBtn.addEventListener('click', () => {
audio.play();
});
controls.appendChild(playBtn);
messageElement.appendChild(controls);
} else {
console.error('TTS 接口错误', await ttsResp.text());
}
} catch (err) {
console.error('TTS 播放失败', err);
}
}
// 滚动到底部
chatMessages.scrollTop = chatMessages.scrollHeight;
}
// 格式化消息内容支持简单的markdown
function formatMessage(content) {
// 转义HTML特殊字符
let formatted = content
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;');
// 支持加粗
formatted = formatted.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
// 支持斜体
formatted = formatted.replace(/\*(.*?)\*/g, '<em>$1</em>');
// 支持代码
formatted = formatted.replace(/`(.*?)`/g, '<code>$1</code>');
// 支持换行
formatted = formatted.replace(/\n/g, '<br>');
return formatted;
}
// 获取当前时间
function getCurrentTime() {
const now = new Date();
const hours = now.getHours().toString().padStart(2, '0');
const minutes = now.getMinutes().toString().padStart(2, '0');
return `${hours}:${minutes}`;
}