301 lines
11 KiB
JavaScript
301 lines
11 KiB
JavaScript
// 页面加载完成后执行
|
||
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, '&')
|
||
.replace(/</g, '<')
|
||
.replace(/>/g, '>');
|
||
|
||
// 支持加粗
|
||
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}`;
|
||
}
|