2766 lines
106 KiB
Plaintext
2766 lines
106 KiB
Plaintext
/* ===== 云大阁网站JavaScript - 修复版 ===== */
|
||
|
||
// 全局变量
|
||
let chatCounter = 0;
|
||
let isDeepSearchActive = false;
|
||
let currentChatId = null;
|
||
let chatHistoryData = new Map(); // 存储聊天历史数据
|
||
let isOnlineMode = false; // 是否使用在线模式(连接后端),默认false使用本地存储
|
||
|
||
// DOM加载完成后执行
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
// 添加ESC键监听器以支持中断请求
|
||
document.addEventListener('keydown', function(e) {
|
||
if (e.key === 'Escape' && window.isSubmitting) {
|
||
console.log('用户按下ESC键,中断请求');
|
||
|
||
// 中断请求
|
||
if (window.currentAbortController) {
|
||
window.currentAbortController.abort();
|
||
window.currentAbortController = null;
|
||
}
|
||
|
||
// 重置状态
|
||
window.isSubmitting = false;
|
||
if (window.updateSendUI) {
|
||
window.updateSendUI(false);
|
||
}
|
||
|
||
// 显示提示
|
||
const statusText = document.querySelector('.status-text');
|
||
if (statusText) {
|
||
statusText.textContent = '已取消请求(ESC)';
|
||
statusText.style.color = '#dc3545';
|
||
setTimeout(() => {
|
||
statusText.textContent = 'Ready to submit!';
|
||
statusText.style.color = '';
|
||
}, 3000);
|
||
}
|
||
|
||
// 在聊天窗口添加提示
|
||
const chatContent = document.getElementById('chatContent');
|
||
const chatMessages = document.getElementById('chatMessages');
|
||
if (chatContent || chatMessages) {
|
||
const targetElement = chatMessages || chatContent;
|
||
const warningDiv = document.createElement('div');
|
||
warningDiv.className = 'ai-message';
|
||
warningDiv.style.cssText = 'background-color: #fff3cd; border: 1px solid #ffc107; color: #856404; padding: 10px; border-radius: 8px; margin: 10px 0;';
|
||
warningDiv.innerHTML = '⚠️ 请求已被ESC键取消。注意:后端可能仍在处理,建议稍等片刻再发送新请求。';
|
||
targetElement.appendChild(warningDiv);
|
||
setTimeout(() => {
|
||
warningDiv.style.transition = 'opacity 0.5s';
|
||
warningDiv.style.opacity = '0';
|
||
setTimeout(() => warningDiv.remove(), 500);
|
||
}, 5000);
|
||
}
|
||
}
|
||
});
|
||
|
||
// 只初始化核心功能,避免冲突
|
||
initSidebar();
|
||
initChat();
|
||
// initUserDropdown(); // 注释掉,由auth-integration.js处理
|
||
initSearch();
|
||
initChatHistoryActions();
|
||
initTextRotator();
|
||
init3DTicker();
|
||
initDownloadModal();
|
||
initBackground(); // 初始化背景动画
|
||
initToolsDropdown(); // 初始化工具下拉菜单
|
||
|
||
// 确保初始状态下3D图片是显示的
|
||
const tickerSection = document.querySelector('.ticker-section');
|
||
const ticker3D = document.getElementById('ticker3D');
|
||
|
||
console.log('检查ticker-section:', tickerSection);
|
||
console.log('检查ticker3D:', ticker3D);
|
||
console.log('body是否有chat-mode类:', document.body.classList.contains('chat-mode'));
|
||
|
||
if (tickerSection) {
|
||
tickerSection.style.display = 'block';
|
||
tickerSection.style.visibility = 'visible';
|
||
tickerSection.style.opacity = '1';
|
||
console.log('设置ticker-section显示');
|
||
|
||
// 检查computed style
|
||
const computedStyle = window.getComputedStyle(tickerSection);
|
||
console.log('ticker-section computed display:', computedStyle.display);
|
||
console.log('ticker-section computed visibility:', computedStyle.visibility);
|
||
console.log('ticker-section computed height:', computedStyle.height);
|
||
} else {
|
||
console.error('未找到ticker-section元素');
|
||
}
|
||
|
||
// 设置侧边栏默认状态 - 默认折叠
|
||
const sidebar = document.getElementById('sidebar');
|
||
|
||
if (sidebar) {
|
||
// 所有设备默认折叠状态
|
||
sidebar.classList.add('collapsed');
|
||
}
|
||
|
||
// 页面加载完自动聚焦到输入框
|
||
const messageInput = document.getElementById('messageInput');
|
||
if (messageInput) {
|
||
setTimeout(() => {
|
||
messageInput.focus();
|
||
}, 500);
|
||
}
|
||
|
||
// 初始化聊天管理器监听器
|
||
initChatManagerListeners();
|
||
|
||
// 等待 ChatManager 初始化完成后再加载
|
||
const waitForChatManager = () => {
|
||
if (window.chatManager && window.chatManager.initialized) {
|
||
console.log('[Main] ChatManager 已初始化,开始加载聊天历史');
|
||
|
||
// 加载用户的聊天历史
|
||
loadUserChatHistory();
|
||
|
||
// 恢复当前对话
|
||
setTimeout(() => {
|
||
console.log('[Main] 检查是否需要恢复对话...');
|
||
console.log('[Main] chatManager.currentChatId:', window.chatManager.currentChatId);
|
||
|
||
if (window.chatManager.currentChatId) {
|
||
console.log('[Main] 开始恢复上次的对话:', window.chatManager.currentChatId);
|
||
loadChatHistory(window.chatManager.currentChatId);
|
||
|
||
// 高亮侧边栏中的当前对话
|
||
const chatItem = document.querySelector(`[data-chat-id="${window.chatManager.currentChatId}"]`);
|
||
if (chatItem) {
|
||
chatItem.classList.add('active');
|
||
console.log('[Main] 已高亮当前对话项');
|
||
} else {
|
||
console.log('[Main] ⚠️ 未找到对应的侧边栏对话项');
|
||
}
|
||
} else {
|
||
console.log('[Main] 没有需要恢复的对话');
|
||
}
|
||
}, 50);
|
||
} else {
|
||
console.log('[Main] 等待 ChatManager 初始化...');
|
||
setTimeout(waitForChatManager, 100);
|
||
}
|
||
};
|
||
|
||
waitForChatManager();
|
||
|
||
// 监听用户登录状态变化
|
||
initUserChangeListener();
|
||
|
||
// 初始化Markdown渲染器和工具栏
|
||
if (window.markdownRenderer) {
|
||
window.markdownRenderer.initCodeCopyButtons();
|
||
|
||
// 确保工具栏被初始化
|
||
const welcomeToolbar = document.getElementById('markdownToolbar');
|
||
const welcomeInput = document.getElementById('messageInput');
|
||
if (welcomeToolbar && welcomeInput) {
|
||
welcomeToolbar.innerHTML = window.markdownRenderer.createToolbar();
|
||
welcomeToolbar.style.display = 'flex';
|
||
window.markdownRenderer.attachToolbarEvents(welcomeToolbar, welcomeInput);
|
||
}
|
||
}
|
||
|
||
// 初始化工具下拉菜单
|
||
initToolsDropdown();
|
||
|
||
console.log('云大阁网站已加载完成');
|
||
});
|
||
|
||
/* ===== 侧边栏功能 - 复刻duijie.html简单逻辑 + yundage-keyong1的tooltip ===== */
|
||
function toggleSidebar() {
|
||
document.getElementById('sidebar').classList.toggle('collapsed');
|
||
}
|
||
|
||
function initSidebar() {
|
||
// Logo点击切换事件
|
||
const logoMenuItem = document.getElementById('logoMenuItem');
|
||
if (logoMenuItem) {
|
||
logoMenuItem.addEventListener('click', function(e) {
|
||
toggleSidebar();
|
||
e.stopPropagation();
|
||
});
|
||
}
|
||
|
||
// 新建对话按钮事件
|
||
const newChatBtn = document.getElementById('newChatBtn');
|
||
if (newChatBtn) {
|
||
newChatBtn.addEventListener('click', function() {
|
||
console.log('新建对话按钮被点击');
|
||
startNewChat();
|
||
});
|
||
}
|
||
|
||
// 下载APP按钮事件
|
||
const downloadAppBtn = document.getElementById('downloadAppBtn');
|
||
if (downloadAppBtn) {
|
||
downloadAppBtn.addEventListener('click', function() {
|
||
console.log('下载APP按钮被点击');
|
||
initDownloadModal();
|
||
});
|
||
}
|
||
|
||
// 初始化tooltip定位
|
||
initTooltipPositioning();
|
||
}
|
||
|
||
// ChatGPT风格的tooltip定位 - 将tooltip移动到body避免遮挡
|
||
function initTooltipPositioning() {
|
||
const sidebar = document.getElementById('sidebar');
|
||
if (!sidebar) return; // 如果sidebar不存在,直接返回
|
||
|
||
const tooltipWrappers = sidebar.querySelectorAll('.tooltip-wrapper');
|
||
|
||
tooltipWrappers.forEach(wrapper => {
|
||
const tooltipText = wrapper.querySelector('.tooltip-text');
|
||
if (!tooltipText) return;
|
||
|
||
let floatingTooltip = null;
|
||
|
||
wrapper.addEventListener('mouseenter', function() {
|
||
if (sidebar.classList.contains('collapsed')) {
|
||
// 创建独立的tooltip元素添加到body
|
||
floatingTooltip = document.createElement('div');
|
||
floatingTooltip.textContent = tooltipText.textContent;
|
||
floatingTooltip.className = 'floating-tooltip';
|
||
floatingTooltip.style.cssText = `
|
||
position: fixed;
|
||
background-color: #333;
|
||
color: #fff;
|
||
text-align: center;
|
||
padding: 6px 10px;
|
||
border-radius: 6px;
|
||
white-space: nowrap;
|
||
font-size: 13px;
|
||
z-index: 999999;
|
||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||
pointer-events: none;
|
||
`;
|
||
|
||
// 获取wrapper的位置
|
||
const rect = wrapper.getBoundingClientRect();
|
||
|
||
// 设置tooltip位置
|
||
floatingTooltip.style.left = (rect.right + 10) + 'px';
|
||
floatingTooltip.style.top = (rect.top + rect.height / 2) + 'px';
|
||
floatingTooltip.style.transform = 'translateY(-50%)';
|
||
|
||
// 添加到body
|
||
document.body.appendChild(floatingTooltip);
|
||
}
|
||
});
|
||
|
||
wrapper.addEventListener('mouseleave', function() {
|
||
if (floatingTooltip) {
|
||
document.body.removeChild(floatingTooltip);
|
||
floatingTooltip = null;
|
||
}
|
||
});
|
||
});
|
||
}
|
||
|
||
/* ===== 聊天功能 ===== */
|
||
function initChat() {
|
||
const sendBtn = document.getElementById('sendBtn');
|
||
const messageInput = document.getElementById('messageInput');
|
||
const chatMessages = document.getElementById('chatMessages');
|
||
const deepSearchToggle = document.getElementById('deepSearchToggle');
|
||
const newChatBtn = document.getElementById('newChatBtn');
|
||
const voiceBtn = document.getElementById('voiceBtn');
|
||
|
||
// 聊天模式元素
|
||
const chatModeSendBtn = document.getElementById('chatModeSendBtn');
|
||
const chatModeMessageInput = document.getElementById('chatModeMessageInput');
|
||
|
||
if (!sendBtn || !messageInput || !chatMessages) {
|
||
console.error('关键聊天元素未找到');
|
||
return;
|
||
}
|
||
|
||
// 初始化发送按钮状态
|
||
updateSendButtonState();
|
||
|
||
// 发送消息(支持停止功能)
|
||
sendBtn.addEventListener('click', function(e) {
|
||
e.preventDefault();
|
||
|
||
if (window.isSubmitting) {
|
||
// 停止操作
|
||
console.log('用户点击停止按钮');
|
||
if (window.currentAbortController) {
|
||
window.currentAbortController.abort();
|
||
window.currentAbortController = null;
|
||
}
|
||
window.isSubmitting = false;
|
||
updateSendUI(false);
|
||
|
||
// 显示提示信息
|
||
const statusText = document.querySelector('.status-text');
|
||
if (statusText) {
|
||
statusText.textContent = '已取消请求';
|
||
statusText.style.color = '#dc3545';
|
||
setTimeout(() => {
|
||
statusText.textContent = 'Ready to submit!';
|
||
statusText.style.color = '';
|
||
}, 3000);
|
||
}
|
||
|
||
// 在聊天窗口添加提示
|
||
const chatContent = document.getElementById('chatContent');
|
||
if (chatContent) {
|
||
const warningDiv = document.createElement('div');
|
||
warningDiv.className = 'ai-message';
|
||
warningDiv.style.cssText = 'background-color: #fff3cd; border: 1px solid #ffc107; color: #856404; padding: 10px; border-radius: 8px; margin: 10px 0;';
|
||
warningDiv.innerHTML = '⚠️ 请求已取消。注意:后端可能仍在处理,建议稍等片刻再发送新请求。';
|
||
chatContent.appendChild(warningDiv);
|
||
setTimeout(() => {
|
||
warningDiv.style.transition = 'opacity 0.5s';
|
||
warningDiv.style.opacity = '0';
|
||
setTimeout(() => warningDiv.remove(), 500);
|
||
}, 5000);
|
||
}
|
||
} else {
|
||
// 发送操作
|
||
console.log('发送按钮被点击');
|
||
sendMessage();
|
||
}
|
||
});
|
||
|
||
// 回车发送消息(Shift+Enter换行)
|
||
messageInput.addEventListener('keydown', function(e) {
|
||
if (e.key === 'Enter' && !e.shiftKey) {
|
||
e.preventDefault();
|
||
sendMessage();
|
||
}
|
||
});
|
||
|
||
// 监听输入框变化,更新发送按钮状态和自动调整高度
|
||
messageInput.addEventListener('input', function() {
|
||
updateSendButtonState();
|
||
autoResizeTextarea(this);
|
||
});
|
||
|
||
// 深度检索切换
|
||
if (deepSearchToggle) {
|
||
deepSearchToggle.addEventListener('click', function() {
|
||
console.log('深度检索按钮被点击');
|
||
toggleDeepSearch();
|
||
});
|
||
}
|
||
|
||
// 删除重复的事件监听器绑定,前面已经绑定过了
|
||
|
||
// 语音按钮(暂时无功能)
|
||
if (voiceBtn) {
|
||
voiceBtn.addEventListener('click', function() {
|
||
console.log('语音按钮被点击');
|
||
// 这里可以添加语音功能
|
||
});
|
||
}
|
||
|
||
// 聊天模式输入框事件监听器
|
||
if (chatModeSendBtn && chatModeMessageInput) {
|
||
// 发送消息(支持停止功能)
|
||
chatModeSendBtn.addEventListener('click', function(e) {
|
||
e.preventDefault();
|
||
|
||
if (window.isSubmitting) {
|
||
// 停止操作
|
||
console.log('用户点击停止按钮(聊天模式)');
|
||
if (window.currentAbortController) {
|
||
window.currentAbortController.abort();
|
||
window.currentAbortController = null;
|
||
}
|
||
window.isSubmitting = false;
|
||
updateSendUI(false);
|
||
|
||
// 在聊天窗口添加提示
|
||
addMessage('⚠️ 请求已取消。注意:后端可能仍在处理,建议稍等片刻再发送新请求。', 'system');
|
||
} else {
|
||
// 发送操作
|
||
sendChatModeMessage();
|
||
}
|
||
});
|
||
|
||
// 回车发送消息
|
||
chatModeMessageInput.addEventListener('keydown', function(e) {
|
||
if (e.key === 'Enter' && !e.shiftKey) {
|
||
e.preventDefault();
|
||
sendChatModeMessage();
|
||
}
|
||
});
|
||
|
||
// 监听输入框变化
|
||
chatModeMessageInput.addEventListener('input', function() {
|
||
updateChatModeSendButtonState();
|
||
autoResizeTextarea(this);
|
||
});
|
||
}
|
||
|
||
// 更新发送按钮状态
|
||
function updateSendButtonState() {
|
||
const messageInput = document.getElementById('messageInput');
|
||
const sendBtn = document.getElementById('sendBtn');
|
||
const sendIcon = document.getElementById('sendIcon');
|
||
|
||
if (messageInput && sendBtn && sendIcon) {
|
||
const hasText = messageInput.value.trim().length > 0;
|
||
sendIcon.style.opacity = hasText && !window.isSubmitting ? '1' : '0.3';
|
||
}
|
||
}
|
||
|
||
// 更新发送UI状态(改造为停止按钮)
|
||
function updateSendUI(isLoading) {
|
||
const sendIcon = document.getElementById('sendIcon');
|
||
const loadingSpinner = document.getElementById('loadingSpinner');
|
||
const statusText = document.querySelector('.status-text');
|
||
const messageInput = document.getElementById('messageInput');
|
||
const sendBtn = document.getElementById('sendBtn');
|
||
|
||
// 同时更新聊天模式的UI
|
||
const chatModeSendIcon = document.getElementById('chatModeSendIcon');
|
||
const chatModeLoadingSpinner = document.getElementById('chatModeLoadingSpinner');
|
||
const chatModeMessageInput = document.getElementById('chatModeMessageInput');
|
||
const chatModeSendBtn = document.getElementById('chatModeSendBtn');
|
||
|
||
if (isLoading) {
|
||
// 首页:显示红色停止图标
|
||
if (loadingSpinner) {
|
||
loadingSpinner.style.setProperty('display', 'none', 'important');
|
||
}
|
||
if (sendIcon) {
|
||
sendIcon.style.display = 'block';
|
||
sendIcon.innerHTML = `
|
||
<rect x="6" y="6" width="12" height="12" fill="#dc3545"/>
|
||
`;
|
||
sendIcon.style.opacity = '1';
|
||
}
|
||
if (sendBtn) {
|
||
sendBtn.disabled = false;
|
||
sendBtn.title = '点击停止(或按ESC键)';
|
||
}
|
||
if (statusText) statusText.textContent = 'AI is thinking...';
|
||
if (messageInput) messageInput.disabled = true;
|
||
|
||
// 聊天模式:同样显示红色停止图标
|
||
if (chatModeLoadingSpinner) {
|
||
chatModeLoadingSpinner.style.setProperty('display', 'none', 'important');
|
||
}
|
||
if (chatModeSendIcon) {
|
||
chatModeSendIcon.style.display = 'block';
|
||
chatModeSendIcon.innerHTML = `
|
||
<rect x="6" y="6" width="12" height="12" fill="#dc3545"/>
|
||
`;
|
||
chatModeSendIcon.style.opacity = '1';
|
||
}
|
||
if (chatModeSendBtn) {
|
||
chatModeSendBtn.disabled = false;
|
||
chatModeSendBtn.title = '点击停止(或按ESC键)';
|
||
}
|
||
if (chatModeMessageInput) chatModeMessageInput.disabled = true;
|
||
|
||
} else {
|
||
// 首页:恢复发送图标
|
||
if (sendIcon) {
|
||
sendIcon.style.display = 'block';
|
||
sendIcon.innerHTML = `
|
||
<path d="m3 3 3 9-3 9 19-9Z"/>
|
||
<path d="m6 12 13 0"/>
|
||
`;
|
||
}
|
||
if (loadingSpinner) {
|
||
loadingSpinner.style.display = 'none';
|
||
}
|
||
if (sendBtn) {
|
||
sendBtn.disabled = false;
|
||
sendBtn.title = '发送消息';
|
||
}
|
||
if (statusText) statusText.textContent = 'Ready to submit!';
|
||
if (messageInput) messageInput.disabled = false;
|
||
updateSendButtonState();
|
||
|
||
// 聊天模式:恢复发送图标
|
||
if (chatModeSendIcon) {
|
||
chatModeSendIcon.style.display = 'block';
|
||
chatModeSendIcon.innerHTML = `
|
||
<path d="m3 3 3 9-3 9 19-9Z"/>
|
||
<path d="m6 12 13 0"/>
|
||
`;
|
||
}
|
||
if (chatModeLoadingSpinner) {
|
||
chatModeLoadingSpinner.style.display = 'none';
|
||
}
|
||
if (chatModeSendBtn) {
|
||
chatModeSendBtn.disabled = false;
|
||
chatModeSendBtn.title = '发送消息';
|
||
}
|
||
if (chatModeMessageInput) chatModeMessageInput.disabled = false;
|
||
updateChatModeSendButtonState();
|
||
}
|
||
}
|
||
|
||
// 暴露到全局以便其他模块使用
|
||
window.updateSendUI = updateSendUI;
|
||
|
||
// 聊天模式按钮状态管理
|
||
function updateChatModeSendButtonState() {
|
||
const chatModeMessageInput = document.getElementById('chatModeMessageInput');
|
||
const chatModeSendIcon = document.getElementById('chatModeSendIcon');
|
||
|
||
if (chatModeMessageInput && chatModeSendIcon) {
|
||
const hasText = chatModeMessageInput.value.trim().length > 0;
|
||
chatModeSendIcon.style.opacity = hasText && !window.isSubmitting ? '1' : '0.3';
|
||
}
|
||
}
|
||
|
||
// 聊天模式发送消息
|
||
async function sendChatModeMessage() {
|
||
const chatModeMessageInput = document.getElementById('chatModeMessageInput');
|
||
const message = chatModeMessageInput.value.trim();
|
||
|
||
if (!message || window.isSubmitting) {
|
||
console.log('消息为空或正在发送中,不发送');
|
||
return;
|
||
}
|
||
|
||
console.log('聊天模式发送消息:', message);
|
||
|
||
// 设置发送状态
|
||
window.isSubmitting = true;
|
||
updateChatModeSendUI(true);
|
||
|
||
// 如果是第一条消息且没有当前聊天ID,创建新聊天
|
||
if (!currentChatId) {
|
||
// 立即创建新的聊天会话
|
||
if (window.chatManager) {
|
||
currentChatId = window.chatManager.createChat(message);
|
||
window.chatManager.currentChatId = currentChatId;
|
||
|
||
// 立即添加到侧边栏
|
||
const chatTitle = message.length > 20 ? message.substring(0, 20) + '...' : message;
|
||
addChatToSidebar(currentChatId, chatTitle, true);
|
||
}
|
||
}
|
||
|
||
// 保存当前聊天ID,避免切换聊天记录时被改变
|
||
const targetChatId = currentChatId;
|
||
|
||
// 添加用户消息
|
||
await addMessage(message, 'user');
|
||
|
||
// 清空输入框
|
||
chatModeMessageInput.value = '';
|
||
autoResizeTextarea(chatModeMessageInput);
|
||
updateChatModeSendButtonState();
|
||
|
||
// 调用后端API获取AI回复
|
||
try {
|
||
// 判断是否使用流式响应
|
||
const useStream = true; // 可以根据需要调整
|
||
|
||
if (useStream) {
|
||
// 流式响应
|
||
let aiMessageId = null;
|
||
let aiContent = '';
|
||
|
||
// 获取工具设置
|
||
const toolsSettings = window.getToolsSettings ? window.getToolsSettings() : { deepResearch: false, showThinking: false };
|
||
const isDeepResearch = toolsSettings.deepResearch;
|
||
const showThinking = toolsSettings.showThinking;
|
||
|
||
// 创建状态显示组件
|
||
let statusDisplay = null;
|
||
if (window.StreamStatusDisplay) {
|
||
// 在消息区域之前创建状态显示容器
|
||
const statusContainer = document.createElement('div');
|
||
statusContainer.id = `status-${targetChatId}-${Date.now()}`;
|
||
chatMessages.appendChild(statusContainer);
|
||
statusDisplay = new window.StreamStatusDisplay(statusContainer);
|
||
}
|
||
|
||
const abortController = await window.ChatAPIService.sendMessageStream(
|
||
window.currentAbortController = abortController; // 保存到全局以便中断
|
||
{
|
||
message: message,
|
||
conversationId: targetChatId, // 使用保存的targetChatId
|
||
mode: isDeepSearchActive ? 'research' : 'chat',
|
||
deepResearch: isDeepResearch,
|
||
showThinking: showThinking
|
||
},
|
||
// onMessage: 接收消息片段
|
||
(content) => {
|
||
// 只有当前聊天ID与目标ID一致时才更新UI
|
||
if (currentChatId === targetChatId) {
|
||
aiContent += content;
|
||
if (aiMessageId) {
|
||
// 更新已存在的消息
|
||
updateMessageContent(aiMessageId, aiContent);
|
||
} else {
|
||
// 创建新消息
|
||
aiMessageId = Date.now().toString();
|
||
addStreamMessage(aiContent, 'ai', aiMessageId);
|
||
}
|
||
} else {
|
||
// 如果已切换到其他聊天,仅累积内容但不更新UI
|
||
aiContent += content;
|
||
}
|
||
},
|
||
// onComplete: 完成回调
|
||
async (data) => {
|
||
console.log('流式响应完成:', data);
|
||
// 使用保存的targetChatId保存AI回复到正确的聊天记录
|
||
if (window.chatManager && targetChatId) {
|
||
// 保存到目标聊天记录
|
||
const originalChatId = window.chatManager.currentChatId;
|
||
window.chatManager.currentChatId = targetChatId;
|
||
window.chatManager.addMessage(aiContent, 'ai');
|
||
// 如果当前聊天已切换,不需要恢复
|
||
if (currentChatId !== targetChatId) {
|
||
window.chatManager.currentChatId = originalChatId;
|
||
}
|
||
}
|
||
// 恢复发送状态
|
||
window.isSubmitting = false;
|
||
updateChatModeSendUI(false);
|
||
},
|
||
// onError: 错误回调
|
||
(error) => {
|
||
console.error('流式响应错误:', error);
|
||
addMessage(`抱歉,发生了错误:${error.message}`, 'ai');
|
||
window.isSubmitting = false;
|
||
updateChatModeSendUI(false);
|
||
},
|
||
// onThinking: 思考过程回调
|
||
showThinking ? (thinking) => {
|
||
console.log('思考过程:', thinking);
|
||
// 显示思考过程
|
||
if (!document.getElementById('thinking-message')) {
|
||
const thinkingDiv = document.createElement('div');
|
||
thinkingDiv.id = 'thinking-message';
|
||
thinkingDiv.className = 'thinking-process';
|
||
thinkingDiv.innerHTML = `
|
||
<div class="thinking-label">🤔 思考过程:</div>
|
||
<div class="thinking-content"></div>
|
||
`;
|
||
chatMessages.appendChild(thinkingDiv);
|
||
chatMessages.scrollTop = chatMessages.scrollHeight;
|
||
}
|
||
const thinkingContent = document.querySelector('#thinking-message .thinking-content');
|
||
if (thinkingContent) {
|
||
thinkingContent.textContent += thinking;
|
||
}
|
||
} : null,
|
||
// onStatus: 状态更新回调(新增)
|
||
(status) => {
|
||
if (statusDisplay) {
|
||
statusDisplay.updateStatus(status);
|
||
}
|
||
}
|
||
);
|
||
} else {
|
||
// 同步响应
|
||
const response = await window.ChatAPIService.sendMessage({
|
||
message: message,
|
||
conversationId: targetChatId, // 使用保存的targetChatId
|
||
mode: isDeepSearchActive ? 'research' : 'chat'
|
||
});
|
||
|
||
if (response.success) {
|
||
// 更新会话ID(如果是新会话)
|
||
if (response.conversationId && !targetChatId) {
|
||
const newChatId = response.conversationId;
|
||
// 只有当前聊天与目标一致时才更新全局变量
|
||
if (currentChatId === targetChatId) {
|
||
currentChatId = newChatId;
|
||
}
|
||
if (window.chatManager) {
|
||
window.chatManager.currentChatId = newChatId;
|
||
}
|
||
// 添加新会话到侧边栏
|
||
const chatTitle = message.length > 20 ? message.substring(0, 20) + '...' : message;
|
||
addChatToSidebar(newChatId, chatTitle, true);
|
||
}
|
||
// 使用addMessageToChat统一处理AI回复
|
||
await addMessageToChat(response.answer, 'ai', targetChatId);
|
||
} else {
|
||
// 使用addMessageToChat统一处理错误消息
|
||
await addMessageToChat('抱歉,获取回复时出现错误。', 'ai', targetChatId);
|
||
}
|
||
|
||
// 恢复发送状态
|
||
window.isSubmitting = false;
|
||
updateChatModeSendUI(false);
|
||
}
|
||
} catch (error) {
|
||
console.error('发送消息失败:', error);
|
||
// 使用addMessageToChat统一处理错误消息
|
||
await addMessageToChat(`抱歉,发生了错误:${error.message}`, 'ai', targetChatId);
|
||
window.isSubmitting = false;
|
||
updateChatModeSendUI(false);
|
||
}
|
||
|
||
// 重新聚焦输入框
|
||
chatModeMessageInput.focus();
|
||
}
|
||
|
||
// 聊天模式UI状态更新(直接调用统一的updateSendUI)
|
||
function updateChatModeSendUI(isLoading) {
|
||
updateSendUI(isLoading);
|
||
}
|
||
|
||
// 暴露到全局
|
||
window.updateChatModeSendUI = updateChatModeSendUI;
|
||
|
||
// 自动调整输入框高度(参考ChatGPT/DeepSeek)
|
||
function autoResizeTextarea(textarea) {
|
||
// 检查是否是欢迎模式的输入框(有特殊的最小高度)
|
||
if (textarea.id === 'messageInput') {
|
||
// 欢迎模式输入框保持固定高度124px,不自动调整
|
||
return;
|
||
}
|
||
|
||
// 聊天模式输入框自动调整高度
|
||
// 重置高度以获取正确的scrollHeight
|
||
textarea.style.height = '40px';
|
||
|
||
// 计算新高度
|
||
const newHeight = Math.min(textarea.scrollHeight, 120); // 最大120px
|
||
textarea.style.height = newHeight + 'px';
|
||
|
||
// 如果内容超过最大高度,显示滚动条
|
||
if (textarea.scrollHeight > 120) {
|
||
textarea.style.overflowY = 'auto';
|
||
} else {
|
||
textarea.style.overflowY = 'hidden';
|
||
}
|
||
}
|
||
|
||
async function sendMessage() {
|
||
const message = messageInput.value.trim();
|
||
if (!message || window.isSubmitting) {
|
||
console.log('消息为空或正在发送中,不发送');
|
||
return;
|
||
}
|
||
|
||
console.log('发送消息:', message);
|
||
|
||
// 设置发送状态
|
||
window.isSubmitting = true;
|
||
updateSendUI(true);
|
||
|
||
// 显示聊天容器
|
||
showChatContainer();
|
||
|
||
// 如果是第一条消息且没有当前聊天ID,创建新聊天
|
||
if (!currentChatId) {
|
||
// 立即创建新的聊天会话
|
||
if (window.chatManager) {
|
||
currentChatId = window.chatManager.createChat(message);
|
||
window.chatManager.currentChatId = currentChatId;
|
||
|
||
// 立即添加到侧边栏
|
||
const chatTitle = message.length > 20 ? message.substring(0, 20) + '...' : message;
|
||
addChatToSidebar(currentChatId, chatTitle, true);
|
||
}
|
||
}
|
||
|
||
// 保存当前聊天ID,避免切换聊天记录时被改变
|
||
const targetChatId = currentChatId;
|
||
|
||
// 添加用户消息(使用固定的chatId)
|
||
await addMessageToChat(message, 'user', targetChatId);
|
||
|
||
// 清空输入框并重置高度
|
||
messageInput.value = '';
|
||
// 欢迎模式输入框保持124px高度
|
||
if (messageInput.id === 'messageInput') {
|
||
messageInput.style.height = '124px';
|
||
} else {
|
||
messageInput.style.height = '40px';
|
||
}
|
||
updateSendButtonState();
|
||
|
||
// 调用后端API获取AI回复
|
||
try {
|
||
// 判断是否使用流式响应
|
||
const useStream = true; // 可以根据需要调整
|
||
|
||
if (useStream) {
|
||
// 流式响应
|
||
let aiMessageId = null;
|
||
let aiContent = '';
|
||
|
||
// 获取工具设置
|
||
const toolsSettings = window.getToolsSettings ? window.getToolsSettings() : { deepResearch: false, showThinking: false };
|
||
const isDeepResearch = toolsSettings.deepResearch;
|
||
const showThinking = toolsSettings.showThinking;
|
||
|
||
// 创建状态显示组件
|
||
let statusDisplay = null;
|
||
if (window.StreamStatusDisplay) {
|
||
// 在消息区域之前创建状态显示容器
|
||
const statusContainer = document.createElement('div');
|
||
statusContainer.id = `status-${targetChatId}-${Date.now()}`;
|
||
chatMessages.appendChild(statusContainer);
|
||
statusDisplay = new window.StreamStatusDisplay(statusContainer);
|
||
}
|
||
|
||
const abortController = await window.ChatAPIService.sendMessageStream(
|
||
window.currentAbortController = abortController; // 保存到全局以便中断
|
||
{
|
||
message: message,
|
||
conversationId: targetChatId, // 使用保存的targetChatId
|
||
mode: isDeepSearchActive ? 'research' : 'chat',
|
||
deepResearch: isDeepResearch,
|
||
showThinking: showThinking
|
||
},
|
||
// onMessage: 接收消息片段
|
||
(content) => {
|
||
// 只有当前聊天ID与目标ID一致时才更新UI
|
||
if (currentChatId === targetChatId) {
|
||
aiContent += content;
|
||
if (aiMessageId) {
|
||
// 更新已存在的消息
|
||
updateMessageContent(aiMessageId, aiContent);
|
||
} else {
|
||
// 创建新消息
|
||
aiMessageId = Date.now().toString();
|
||
addStreamMessage(aiContent, 'ai', aiMessageId);
|
||
}
|
||
} else {
|
||
// 如果已切换到其他聊天,仅累积内容但不更新UI
|
||
aiContent += content;
|
||
}
|
||
},
|
||
// onComplete: 完成回调
|
||
async (data) => {
|
||
console.log('流式响应完成:', data);
|
||
// 使用保存的targetChatId保存AI回复到正确的聊天记录
|
||
if (window.chatManager && targetChatId) {
|
||
// 保存到目标聊天记录
|
||
const originalChatId = window.chatManager.currentChatId;
|
||
window.chatManager.currentChatId = targetChatId;
|
||
window.chatManager.addMessage(aiContent, 'ai');
|
||
// 如果当前聊天已切换,不需要恢复
|
||
if (currentChatId !== targetChatId) {
|
||
window.chatManager.currentChatId = originalChatId;
|
||
}
|
||
}
|
||
// 恢复发送状态
|
||
window.isSubmitting = false;
|
||
updateSendUI(false);
|
||
},
|
||
// onError: 错误回调
|
||
(error) => {
|
||
console.error('流式响应错误:', error);
|
||
addMessage(`抱歉,发生了错误:${error.message}`, 'ai');
|
||
window.isSubmitting = false;
|
||
updateSendUI(false);
|
||
},
|
||
// onThinking: 思考过程回调
|
||
showThinking ? (thinking) => {
|
||
console.log('思考过程:', thinking);
|
||
// 显示思考过程
|
||
if (!document.getElementById('thinking-message')) {
|
||
const thinkingDiv = document.createElement('div');
|
||
thinkingDiv.id = 'thinking-message';
|
||
thinkingDiv.className = 'thinking-process';
|
||
thinkingDiv.innerHTML = `
|
||
<div class="thinking-label">🤔 思考过程:</div>
|
||
<div class="thinking-content"></div>
|
||
`;
|
||
chatMessages.appendChild(thinkingDiv);
|
||
chatMessages.scrollTop = chatMessages.scrollHeight;
|
||
}
|
||
const thinkingContent = document.querySelector('#thinking-message .thinking-content');
|
||
if (thinkingContent) {
|
||
thinkingContent.textContent += thinking;
|
||
}
|
||
} : null,
|
||
// onStatus: 状态更新回调(新增)
|
||
(status) => {
|
||
if (statusDisplay) {
|
||
statusDisplay.updateStatus(status);
|
||
}
|
||
}
|
||
);
|
||
} else {
|
||
// 同步响应
|
||
const response = await window.ChatAPIService.sendMessage({
|
||
message: message,
|
||
conversationId: targetChatId, // 使用保存的targetChatId
|
||
mode: isDeepSearchActive ? 'research' : 'chat'
|
||
});
|
||
|
||
if (response.success) {
|
||
// 更新会话ID(如果是新会话)
|
||
if (response.conversationId && !targetChatId) {
|
||
const newChatId = response.conversationId;
|
||
// 只有当前聊天与目标一致时才更新全局变量
|
||
if (currentChatId === targetChatId) {
|
||
currentChatId = newChatId;
|
||
}
|
||
if (window.chatManager) {
|
||
window.chatManager.currentChatId = newChatId;
|
||
}
|
||
// 添加新会话到侧边栏
|
||
const chatTitle = message.length > 20 ? message.substring(0, 20) + '...' : message;
|
||
addChatToSidebar(newChatId, chatTitle, true);
|
||
}
|
||
// 使用addMessageToChat统一处理AI回复
|
||
await addMessageToChat(response.answer, 'ai', targetChatId);
|
||
} else {
|
||
// 使用addMessageToChat统一处理错误消息
|
||
await addMessageToChat('抱歉,获取回复时出现错误。', 'ai', targetChatId);
|
||
}
|
||
|
||
// 恢复发送状态
|
||
window.isSubmitting = false;
|
||
updateSendUI(false);
|
||
}
|
||
} catch (error) {
|
||
console.error('发送消息失败:', error);
|
||
// 使用addMessageToChat统一处理错误消息
|
||
await addMessageToChat(`抱歉,发生了错误:${error.message}`, 'ai', targetChatId);
|
||
window.isSubmitting = false;
|
||
updateSendUI(false);
|
||
}
|
||
|
||
// 重新聚焦输入框
|
||
messageInput.focus();
|
||
}
|
||
|
||
// 新增: 添加消息到指定的聊天记录
|
||
async function addMessageToChat(content, type, chatId) {
|
||
// 确保聊天容器已显示
|
||
showChatContainer();
|
||
|
||
// 使用新的聊天管理器
|
||
if (window.chatManager && chatId) {
|
||
// 只有登录用户才保存聊天记录
|
||
if (window.chatManager.getCurrentUser()) {
|
||
// 临时切换到目标聊天ID
|
||
const originalChatId = window.chatManager.currentChatId;
|
||
window.chatManager.currentChatId = chatId;
|
||
const message = window.chatManager.addMessage(content, type);
|
||
// 恢复原来的聊天ID
|
||
window.chatManager.currentChatId = originalChatId;
|
||
}
|
||
|
||
// 如果启用了在线模式,同步到服务器
|
||
if (isOnlineMode && window.ChatAPIService) {
|
||
try {
|
||
await window.ChatAPIService.addMessage(chatId, {
|
||
content,
|
||
type,
|
||
metadata: {}
|
||
});
|
||
} catch (error) {
|
||
console.error('同步消息到服务器失败:', error);
|
||
}
|
||
}
|
||
} else {
|
||
// 兼容旧的存储方式
|
||
if (chatId && chatHistoryData.has(chatId)) {
|
||
const chatHistory = chatHistoryData.get(chatId);
|
||
chatHistory.push({
|
||
content: content,
|
||
type: type,
|
||
timestamp: new Date().toISOString()
|
||
});
|
||
chatHistoryData.set(chatId, chatHistory);
|
||
}
|
||
}
|
||
|
||
// 只有当前聊天ID与目标ID一致时才更新UI(不保存数据,避免重复)
|
||
if (chatId === currentChatId) {
|
||
await addMessageToUI(content, type);
|
||
}
|
||
}
|
||
|
||
// 新增: 只更新UI的函数(不保存数据)
|
||
async function addMessageToUI(content, type) {
|
||
// 确保聊天容器已显示
|
||
showChatContainer();
|
||
|
||
// 获取聊天消息容器
|
||
const chatMessages = document.getElementById('chatMessages');
|
||
if (!chatMessages) {
|
||
console.error('聊天消息容器未找到');
|
||
return;
|
||
}
|
||
|
||
const messageItem = document.createElement('div');
|
||
messageItem.className = 'message-item mb-4';
|
||
|
||
const isUser = type === 'user';
|
||
const currentTime = new Date().toLocaleTimeString('zh-CN', {
|
||
hour: '2-digit',
|
||
minute: '2-digit'
|
||
});
|
||
|
||
// 渲染内容(支持Markdown)
|
||
let renderedContent = content;
|
||
if (window.markdownRenderer) {
|
||
renderedContent = window.markdownRenderer.render(content);
|
||
}
|
||
|
||
if (isUser) {
|
||
messageItem.innerHTML = `
|
||
<div class="flex justify-end">
|
||
<div class="message user-message px-4 py-3">
|
||
<div class="text-sm text-gray-800 markdown-content">${renderedContent}</div>
|
||
</div>
|
||
</div>
|
||
<div class="text-xs text-gray-500 text-right mt-1">${currentTime}</div>
|
||
`;
|
||
} else {
|
||
messageItem.innerHTML = `
|
||
<div class="flex justify-start">
|
||
<div class="message ai-message px-4 py-3">
|
||
<div class="text-sm text-gray-800 markdown-content">${renderedContent}</div>
|
||
</div>
|
||
</div>
|
||
<div class="text-xs text-gray-500 mt-1">${currentTime}</div>
|
||
`;
|
||
}
|
||
|
||
chatMessages.appendChild(messageItem);
|
||
chatMessages.scrollTop = chatMessages.scrollHeight;
|
||
}
|
||
|
||
async function addMessage(content, type) {
|
||
// 确保聊天容器已显示
|
||
showChatContainer();
|
||
|
||
// 使用新的聊天管理器
|
||
if (window.chatManager && currentChatId) {
|
||
// 只有登录用户才保存聊天记录
|
||
if (window.chatManager.getCurrentUser()) {
|
||
const message = window.chatManager.addMessage(content, type);
|
||
}
|
||
|
||
// 如果启用了在线模式,同步到服务器
|
||
if (isOnlineMode && window.ChatAPIService) {
|
||
try {
|
||
await window.ChatAPIService.addMessage(currentChatId, {
|
||
content,
|
||
type,
|
||
metadata: {}
|
||
});
|
||
} catch (error) {
|
||
console.error('同步消息到服务器失败:', error);
|
||
}
|
||
}
|
||
} else {
|
||
// 兼容旧的存储方式
|
||
if (currentChatId && chatHistoryData.has(currentChatId)) {
|
||
const chatHistory = chatHistoryData.get(currentChatId);
|
||
chatHistory.push({
|
||
content: content,
|
||
type: type,
|
||
timestamp: new Date().toISOString()
|
||
});
|
||
chatHistoryData.set(currentChatId, chatHistory);
|
||
}
|
||
}
|
||
|
||
// 获取聊天消息容器
|
||
const chatMessages = document.getElementById('chatMessages');
|
||
if (!chatMessages) {
|
||
console.error('聊天消息容器未找到');
|
||
return;
|
||
}
|
||
|
||
const messageItem = document.createElement('div');
|
||
messageItem.className = 'message-item mb-4';
|
||
|
||
const isUser = type === 'user';
|
||
const currentTime = new Date().toLocaleTimeString('zh-CN', {
|
||
hour: '2-digit',
|
||
minute: '2-digit'
|
||
});
|
||
|
||
// 渲染内容(支持Markdown)
|
||
let renderedContent = content;
|
||
if (window.markdownRenderer) {
|
||
renderedContent = window.markdownRenderer.render(content);
|
||
}
|
||
|
||
if (isUser) {
|
||
messageItem.innerHTML = `
|
||
<div class="flex justify-end">
|
||
<div class="message user-message px-4 py-3">
|
||
<div class="text-sm text-gray-800 markdown-content">${renderedContent}</div>
|
||
</div>
|
||
</div>
|
||
<div class="text-xs text-gray-500 text-right mt-1">${currentTime}</div>
|
||
`;
|
||
} else {
|
||
messageItem.innerHTML = `
|
||
<div class="flex justify-start">
|
||
<div class="message ai-message px-4 py-3">
|
||
<div class="text-sm text-gray-800 markdown-content">${renderedContent}</div>
|
||
</div>
|
||
</div>
|
||
<div class="text-xs text-gray-500 mt-1">${currentTime}</div>
|
||
`;
|
||
}
|
||
|
||
chatMessages.appendChild(messageItem);
|
||
chatMessages.scrollTop = chatMessages.scrollHeight;
|
||
}
|
||
}
|
||
|
||
// 添加流式消息(用于实时更新)
|
||
function addStreamMessage(content, type, messageId) {
|
||
const chatMessages = document.getElementById('chatMessages');
|
||
if (!chatMessages) {
|
||
console.error('聊天消息容器未找到');
|
||
return;
|
||
}
|
||
|
||
let messageItem = document.getElementById(`message-${messageId}`);
|
||
|
||
if (!messageItem) {
|
||
// 创建新消息元素
|
||
messageItem = document.createElement('div');
|
||
messageItem.id = `message-${messageId}`;
|
||
messageItem.className = 'message-item mb-4';
|
||
chatMessages.appendChild(messageItem);
|
||
}
|
||
|
||
const currentTime = new Date().toLocaleTimeString('zh-CN', {
|
||
hour: '2-digit',
|
||
minute: '2-digit'
|
||
});
|
||
|
||
// 渲染内容(支持Markdown)
|
||
let renderedContent = content;
|
||
if (window.markdownRenderer) {
|
||
renderedContent = window.markdownRenderer.render(content);
|
||
}
|
||
|
||
messageItem.innerHTML = `
|
||
<div class="flex justify-start">
|
||
<div class="message ai-message px-4 py-3">
|
||
<div class="text-sm text-gray-800 markdown-content">${renderedContent}</div>
|
||
</div>
|
||
</div>
|
||
<div class="text-xs text-gray-500 mt-1">${currentTime}</div>
|
||
`;
|
||
|
||
chatMessages.scrollTop = chatMessages.scrollHeight;
|
||
}
|
||
|
||
// 更新消息内容(用于流式更新)
|
||
function updateMessageContent(messageId, content) {
|
||
const messageItem = document.getElementById(`message-${messageId}`);
|
||
if (!messageItem) return;
|
||
|
||
const contentDiv = messageItem.querySelector('.markdown-content');
|
||
if (!contentDiv) return;
|
||
|
||
// 渲染内容(支持Markdown)
|
||
let renderedContent = content;
|
||
if (window.markdownRenderer) {
|
||
renderedContent = window.markdownRenderer.render(content);
|
||
}
|
||
|
||
contentDiv.innerHTML = renderedContent;
|
||
|
||
// 保持滚动在底部
|
||
const chatMessages = document.getElementById('chatMessages');
|
||
if (chatMessages) {
|
||
chatMessages.scrollTop = chatMessages.scrollHeight;
|
||
}
|
||
}
|
||
|
||
/* ===== 深度检索功能 ===== */
|
||
function toggleDeepSearch() {
|
||
isDeepSearchActive = !isDeepSearchActive;
|
||
const deepSearchToggle = document.getElementById('deepSearchToggle');
|
||
|
||
if (deepSearchToggle) {
|
||
if (isDeepSearchActive) {
|
||
deepSearchToggle.classList.add('active');
|
||
deepSearchToggle.style.backgroundColor = '#dbeafe';
|
||
deepSearchToggle.style.borderColor = '#3b82f6';
|
||
deepSearchToggle.style.color = '#1d4ed8';
|
||
} else {
|
||
deepSearchToggle.classList.remove('active');
|
||
deepSearchToggle.style.backgroundColor = '';
|
||
deepSearchToggle.style.borderColor = '';
|
||
deepSearchToggle.style.color = '';
|
||
}
|
||
}
|
||
|
||
console.log('深度检索状态:', isDeepSearchActive);
|
||
}
|
||
|
||
/* ===== 用户下拉菜单 ===== */
|
||
function initUserDropdown() {
|
||
const userMenuToggle = document.getElementById('userMenuToggle');
|
||
const userDropdown = document.getElementById('userDropdown');
|
||
|
||
if (!userMenuToggle || !userDropdown) return;
|
||
|
||
userMenuToggle.addEventListener('click', function(e) {
|
||
e.stopPropagation();
|
||
console.log('用户菜单被点击');
|
||
toggleDropdown();
|
||
});
|
||
|
||
// 点击其他地方关闭下拉菜单
|
||
document.addEventListener('click', function(e) {
|
||
if (!userDropdown.contains(e.target)) {
|
||
closeDropdown();
|
||
}
|
||
});
|
||
|
||
function toggleDropdown() {
|
||
if (userDropdown.classList.contains('hidden')) {
|
||
userDropdown.classList.remove('hidden');
|
||
console.log('打开用户下拉菜单');
|
||
} else {
|
||
userDropdown.classList.add('hidden');
|
||
console.log('关闭用户下拉菜单');
|
||
}
|
||
}
|
||
|
||
function closeDropdown() {
|
||
userDropdown.classList.add('hidden');
|
||
}
|
||
}
|
||
|
||
/* ===== 下载弹窗功能 ===== */
|
||
function initDownloadModal() {
|
||
const downloadBtn = document.getElementById('downloadBtn');
|
||
const downloadAppBtn = document.getElementById('downloadAppBtn');
|
||
const downloadModal = document.getElementById('downloadModal');
|
||
const closeDownloadModal = document.getElementById('closeDownloadModal');
|
||
|
||
if (!downloadModal || !closeDownloadModal) return;
|
||
|
||
// 折叠栏下载按钮点击事件
|
||
if (downloadBtn) {
|
||
downloadBtn.addEventListener('click', function(e) {
|
||
e.preventDefault();
|
||
downloadModal.classList.remove('hidden');
|
||
});
|
||
}
|
||
|
||
// 侧边栏下载APP按钮点击事件
|
||
if (downloadAppBtn) {
|
||
downloadAppBtn.addEventListener('click', function(e) {
|
||
e.preventDefault();
|
||
downloadModal.classList.remove('hidden');
|
||
});
|
||
}
|
||
|
||
// 关闭下载弹窗
|
||
closeDownloadModal.addEventListener('click', function(e) {
|
||
e.preventDefault();
|
||
downloadModal.classList.add('hidden');
|
||
});
|
||
|
||
// 点击遮罩层关闭弹窗
|
||
downloadModal.addEventListener('click', function(e) {
|
||
if (e.target === downloadModal) {
|
||
downloadModal.classList.add('hidden');
|
||
}
|
||
});
|
||
}
|
||
|
||
/* ===== 搜索功能 ===== */
|
||
function initSearch() {
|
||
const searchInput = document.getElementById('searchInput');
|
||
|
||
if (!searchInput) return;
|
||
|
||
searchInput.addEventListener('input', function() {
|
||
const searchTerm = this.value.toLowerCase().trim();
|
||
const chatItems = document.querySelectorAll('.chat-item');
|
||
|
||
chatItems.forEach(item => {
|
||
const text = item.textContent.toLowerCase();
|
||
if (text.includes(searchTerm) || searchTerm === '') {
|
||
item.style.display = '';
|
||
} else {
|
||
item.style.display = 'none';
|
||
}
|
||
});
|
||
|
||
console.log('搜索:', searchTerm);
|
||
});
|
||
}
|
||
|
||
/* ===== 聊天历史功能 ===== */
|
||
function initChatHistoryActions() {
|
||
const chatItems = document.querySelectorAll('.chat-item');
|
||
|
||
chatItems.forEach(item => {
|
||
// 点击聊天项
|
||
item.addEventListener('click', function(e) {
|
||
if (!e.target.classList.contains('delete-chat')) {
|
||
selectChatItem(this);
|
||
console.log('选择聊天记录:', this.textContent.trim());
|
||
}
|
||
});
|
||
|
||
// 删除按钮
|
||
const deleteBtn = item.querySelector('.delete-chat');
|
||
if (deleteBtn) {
|
||
deleteBtn.addEventListener('click', function(e) {
|
||
e.stopPropagation();
|
||
deleteChatSession(item);
|
||
console.log('删除聊天记录');
|
||
});
|
||
}
|
||
});
|
||
}
|
||
|
||
function selectChatItem(item) {
|
||
// 移除其他active状态
|
||
document.querySelectorAll('.chat-history-item').forEach(chatItem => {
|
||
chatItem.classList.remove('active');
|
||
});
|
||
|
||
// 添加active状态
|
||
item.classList.add('active');
|
||
|
||
// 获取聊天ID
|
||
const chatId = item.getAttribute('data-chat-id');
|
||
|
||
// 显示聊天容器
|
||
showChatContainer();
|
||
|
||
// 加载对话内容
|
||
if (chatId) {
|
||
loadChatHistory(chatId);
|
||
console.log('加载聊天历史:', chatId);
|
||
}
|
||
}
|
||
|
||
function deleteChatSession(item) {
|
||
if (confirm('确定要删除这个对话吗?')) {
|
||
item.remove();
|
||
}
|
||
}
|
||
|
||
/* ===== 新建对话 ===== */
|
||
function startNewChat() {
|
||
// 清除active状态
|
||
document.querySelectorAll('.chat-history-item').forEach(item => {
|
||
item.classList.remove('active');
|
||
});
|
||
|
||
// 移除聊天模式类
|
||
document.body.classList.remove('chat-mode');
|
||
|
||
// 切换回欢迎模式
|
||
const chatContainer = document.getElementById('chatContainer');
|
||
const welcomeMode = document.getElementById('welcomeMode');
|
||
const chatModeInput = document.getElementById('chatModeInput');
|
||
const tickerSection = document.querySelector('.ticker-section'); // 3D新闻卡片区域
|
||
|
||
if (chatContainer) {
|
||
chatContainer.style.display = 'none';
|
||
|
||
// 恢复动画和轮播
|
||
if (window.chatModeOptimizer) {
|
||
window.chatModeOptimizer.resume();
|
||
}
|
||
}
|
||
|
||
if (welcomeMode) {
|
||
welcomeMode.style.display = 'flex'; // 使用flex保持居中布局
|
||
}
|
||
|
||
if (chatModeInput) {
|
||
chatModeInput.classList.add('hidden');
|
||
chatModeInput.style.display = 'none'; // 确保完全隐藏
|
||
}
|
||
|
||
// 显示3D新闻卡片区域
|
||
if (tickerSection) {
|
||
tickerSection.style.display = 'block';
|
||
}
|
||
|
||
// 重置当前聊天ID
|
||
currentChatId = null;
|
||
|
||
// 使用聊天管理器开始新聊天
|
||
if (window.chatManager) {
|
||
window.chatManager.startNewChat();
|
||
}
|
||
|
||
// 聚焦输入框
|
||
const messageInput = document.getElementById('messageInput');
|
||
if (messageInput) {
|
||
messageInput.focus();
|
||
}
|
||
|
||
// 如果当前正在提交消息,保持等待状态
|
||
if (window.isSubmitting) {
|
||
updateSendUI(true);
|
||
console.log('返回首页,保持等待状态');
|
||
}
|
||
|
||
console.log('开始新对话,恢复欢迎界面和3D图片');
|
||
|
||
// 重新初始化文字轮播(延迟执行确保DOM已更新)
|
||
setTimeout(() => {
|
||
console.log('重新初始化文字轮播...');
|
||
initTextRotator();
|
||
}, 300);
|
||
}
|
||
|
||
/* ===== 辅助函数 ===== */
|
||
function showChatContainer() {
|
||
const chatContainer = document.getElementById('chatContainer');
|
||
const welcomeMode = document.getElementById('welcomeMode');
|
||
const chatModeInput = document.getElementById('chatModeInput');
|
||
const chatMessages = document.getElementById('chatMessages');
|
||
const bottomFunctionArea = document.querySelector('.flex-shrink-0'); // 底部功能区
|
||
const tickerSection = document.querySelector('.ticker-section'); // 3D新闻卡片区域
|
||
|
||
if (chatContainer && welcomeMode && chatModeInput) {
|
||
// 添加聊天模式类到body
|
||
document.body.classList.add('chat-mode');
|
||
|
||
// 隐藏欢迎模式
|
||
welcomeMode.style.display = 'none';
|
||
|
||
// 隐藏底部功能区
|
||
if (bottomFunctionArea) {
|
||
bottomFunctionArea.style.display = 'none';
|
||
}
|
||
|
||
// 隐藏3D新闻卡片区域
|
||
if (tickerSection) {
|
||
tickerSection.style.display = 'none';
|
||
console.log('showChatContainer: 隐藏3D图片');
|
||
} else {
|
||
console.log('showChatContainer: 未找到ticker-section');
|
||
}
|
||
|
||
// 显示聊天容器(全屏)
|
||
chatContainer.style.display = 'block';
|
||
|
||
// 优化聊天模式性能 - 暂停动画和轮播
|
||
if (window.chatModeOptimizer) {
|
||
window.chatModeOptimizer.pause();
|
||
}
|
||
|
||
// 确保聊天消息容器可见
|
||
if (chatMessages) {
|
||
chatMessages.classList.remove('hidden');
|
||
}
|
||
|
||
// 显示底部固定输入框
|
||
chatModeInput.classList.remove('hidden');
|
||
chatModeInput.style.display = 'block'; // 确保显示
|
||
|
||
// 如果当前正在提交消息,确保聊天模式也显示等待状态
|
||
if (window.isSubmitting) {
|
||
updateSendUI(true);
|
||
console.log('切换到聊天模式,保持等待状态');
|
||
}
|
||
|
||
console.log('切换到聊天模式:隐藏欢迎区、3D图片和底部功能区,输入框下沉到底部,聊天窗口全屏显示');
|
||
}
|
||
}
|
||
|
||
async function createNewChatSession(firstMessage) {
|
||
// 使用后端会话管理,不再创建本地会话ID
|
||
// 会话ID将在第一次发送消息后由后端返回
|
||
console.log('准备创建新会话,等待后端返回会话ID');
|
||
|
||
// 清空当前会话ID,让后端创建新会话
|
||
currentChatId = null;
|
||
|
||
// 如果使用本地聊天管理器(离线模式)
|
||
if (window.chatManager && !window.ChatAPIService) {
|
||
const chatId = window.chatManager.createChat(firstMessage);
|
||
const chat = window.chatManager.getChat(chatId);
|
||
const chatTitle = chat ? chat.title : '新对话';
|
||
|
||
// 添加到侧边栏
|
||
addChatToSidebar(chatId, chatTitle);
|
||
|
||
return chatId;
|
||
}
|
||
|
||
// 使用后端API时,返回null,会话ID将在第一次消息响应时获得
|
||
return null;
|
||
}
|
||
|
||
function addChatToSidebar(chatId, title, shouldSelect = false) {
|
||
const chatHistory = document.querySelector('.chat-history-list');
|
||
if (!chatHistory) return;
|
||
|
||
// 检查会话是否已经存在于侧边栏
|
||
const existingItem = chatHistory.querySelector(`[data-chat-id="${chatId}"]`);
|
||
if (existingItem) {
|
||
console.log(`会话 ${chatId} 已存在于侧边栏,跳过添加`);
|
||
// 如果需要选中,直接选中现有项
|
||
if (shouldSelect) {
|
||
existingItem.click();
|
||
}
|
||
return;
|
||
}
|
||
|
||
// 创建新的聊天项
|
||
const chatItem = document.createElement('div');
|
||
chatItem.className = 'chat-history-item';
|
||
chatItem.setAttribute('data-chat-id', chatId);
|
||
|
||
chatItem.innerHTML = `
|
||
<div class="chat-title">${title}</div>
|
||
<button class="delete-chat" style="display: none; position: absolute; right: 10px; top: 50%; transform: translateY(-50%); background: none; border: none; color: #999; cursor: pointer;">
|
||
<i class="fas fa-trash"></i>
|
||
</button>
|
||
`;
|
||
|
||
// 添加鼠标悬停显示删除按钮
|
||
chatItem.addEventListener('mouseenter', function() {
|
||
const deleteBtn = this.querySelector('.delete-chat');
|
||
if (deleteBtn) deleteBtn.style.display = 'block';
|
||
});
|
||
|
||
chatItem.addEventListener('mouseleave', function() {
|
||
const deleteBtn = this.querySelector('.delete-chat');
|
||
if (deleteBtn) deleteBtn.style.display = 'none';
|
||
});
|
||
|
||
// 添加事件监听器
|
||
chatItem.addEventListener('click', function(e) {
|
||
if (!e.target.classList.contains('delete-chat') && !e.target.closest('.delete-chat')) {
|
||
selectChatItem(this);
|
||
console.log('选择聊天记录:', title);
|
||
|
||
// 切换到选中的聊天
|
||
if (window.chatManager) {
|
||
const switched = window.chatManager.switchToChat(chatId);
|
||
console.log('切换聊天结果:', switched);
|
||
if (!switched) {
|
||
console.error('聊天切换失败,聊天ID不存在:', chatId);
|
||
}
|
||
} else {
|
||
// 兼容旧方式
|
||
loadChatHistory(chatId);
|
||
}
|
||
}
|
||
});
|
||
|
||
const deleteBtn = chatItem.querySelector('.delete-chat');
|
||
if (deleteBtn) {
|
||
deleteBtn.addEventListener('click', function(e) {
|
||
e.stopPropagation();
|
||
|
||
// 使用新的聊天管理器删除
|
||
if (window.chatManager) {
|
||
const confirmDelete = confirm('确定要删除这个聊天记录吗?');
|
||
if (confirmDelete) {
|
||
window.chatManager.deleteChat(chatId);
|
||
console.log('删除聊天记录:', chatId);
|
||
}
|
||
} else {
|
||
// 兼容旧方式
|
||
deleteChatSession(chatItem);
|
||
console.log('删除聊天记录');
|
||
}
|
||
});
|
||
}
|
||
|
||
// 将新聊天添加到"今天"分组的第一项
|
||
const todaySection = chatHistory;
|
||
todaySection.insertBefore(chatItem, todaySection.firstChild);
|
||
|
||
// 只在需要时选中聊天(比如新创建时)
|
||
if (shouldSelect) {
|
||
selectChatItem(chatItem);
|
||
}
|
||
}
|
||
|
||
function generateAIResponse(message) {
|
||
// 根据用户输入生成包含Markdown格式的响应
|
||
const markdownResponses = [
|
||
`感谢您的提问!我正在为您分析这个问题。
|
||
|
||
## 主要观点
|
||
|
||
1. **第一点**:这是一个重要的考虑因素
|
||
2. **第二点**:需要注意相关的细节
|
||
3. **第三点**:可以从多个角度来理解
|
||
|
||
### 补充说明
|
||
> 这是一段引用,包含了一些额外的信息。
|
||
|
||
您还有其他问题吗?`,
|
||
|
||
`这是一个很有趣的话题,让我为您详细解答。
|
||
|
||
### 关键概念
|
||
- **概念A**:基础定义和说明
|
||
- **概念B**:深入理解的要点
|
||
- **概念C**:实际应用场景
|
||
|
||
\`\`\`javascript
|
||
// 示例代码
|
||
function example() {
|
||
console.log("Hello, World!");
|
||
}
|
||
\`\`\`
|
||
|
||
希望这些信息对您有所帮助!`,
|
||
|
||
`根据您的问题,我建议从以下几个方面来考虑:
|
||
|
||
1. **技术层面**
|
||
- 考虑点1
|
||
- 考虑点2
|
||
|
||
2. **实践层面**
|
||
- 实施步骤A
|
||
- 实施步骤B
|
||
|
||
### 相关资源
|
||
- [参考链接1](https://example.com)
|
||
- [参考链接2](https://example.com)
|
||
|
||
*注意*:请根据实际情况调整方案。`,
|
||
|
||
`我理解您的需求,这里有一些相关的信息可能对您有帮助。
|
||
|
||
## 解决方案
|
||
|
||
### 方案一:快速实现
|
||
\`\`\`bash
|
||
# 命令行示例
|
||
npm install package-name
|
||
npm run start
|
||
\`\`\`
|
||
|
||
### 方案二:完整配置
|
||
1. 首先,创建配置文件
|
||
2. 然后,设置必要的参数
|
||
3. 最后,运行测试验证
|
||
|
||
**提示**:记得检查兼容性问题。`,
|
||
|
||
`让我为您提供一些相关的建议和解决方案。
|
||
|
||
## 核心要点
|
||
|
||
| 特性 | 描述 | 优先级 |
|
||
|------|------|--------|
|
||
| 性能 | 优化响应速度 | 高 |
|
||
| 安全 | 加强数据保护 | 高 |
|
||
| 易用 | 改善用户体验 | 中 |
|
||
|
||
### 实施建议
|
||
> 💡 **最佳实践**:始终遵循行业标准和规范。
|
||
|
||
---
|
||
|
||
如需更多帮助,请随时告诉我!`
|
||
];
|
||
|
||
// 检查用户消息是否包含代码相关关键词
|
||
const codeKeywords = ['代码', 'code', '函数', 'function', '编程', 'programming', 'bug', '错误', 'error'];
|
||
const hasCodeContext = codeKeywords.some(keyword => message.toLowerCase().includes(keyword));
|
||
|
||
if (hasCodeContext) {
|
||
// 如果是代码相关问题,返回包含代码块的响应
|
||
return markdownResponses[1]; // 返回包含代码示例的响应
|
||
}
|
||
|
||
// 随机返回一个Markdown格式的响应
|
||
return markdownResponses[Math.floor(Math.random() * markdownResponses.length)];
|
||
}
|
||
|
||
// 删除旧的loadChatHistory函数,使用后面定义的新版本
|
||
|
||
/* ===== 文字轮播技术 ===== */
|
||
function initTextRotator() {
|
||
// 文字轮播功能
|
||
class TextRotator {
|
||
constructor(options) {
|
||
this.element = document.querySelector(options.selector);
|
||
this.words = options.words || ['资料', '专家', '活动', '标准', '合作'];
|
||
this.interval = options.interval || 2500;
|
||
this.currentIndex = 0;
|
||
this.isAnimating = false;
|
||
|
||
if (this.element) {
|
||
this.init();
|
||
}
|
||
}
|
||
|
||
init() {
|
||
// 开始时显示第一个词
|
||
this.element.textContent = this.words[0];
|
||
console.log('文字轮播初始化:', this.words[0]);
|
||
|
||
// 等待一段时间后开始轮播
|
||
setTimeout(() => {
|
||
this.startRotation();
|
||
console.log('开始文字轮播');
|
||
}, 1000);
|
||
}
|
||
|
||
startRotation() {
|
||
setInterval(() => {
|
||
this.rotateText();
|
||
}, this.interval);
|
||
}
|
||
|
||
rotateText() {
|
||
if (this.isAnimating) return;
|
||
|
||
this.isAnimating = true;
|
||
const nextWord = this.words[(this.currentIndex + 1) % this.words.length];
|
||
console.log('轮播切换到:', nextWord);
|
||
|
||
// 同时获取倒影元素
|
||
const reflectionElement = document.querySelector('#rotating-word-reflection');
|
||
|
||
// 退出动画
|
||
this.element.style.transition = 'all 0.2s ease-out';
|
||
this.element.style.opacity = '0';
|
||
this.element.style.transform = 'translateY(-10px) scale(1.02)';
|
||
|
||
if (reflectionElement) {
|
||
reflectionElement.style.transition = 'all 0.2s ease-out';
|
||
reflectionElement.style.opacity = '0';
|
||
reflectionElement.style.transform = 'translateY(10px) scale(1.02)';
|
||
}
|
||
|
||
setTimeout(() => {
|
||
// 更换文字
|
||
this.currentIndex = (this.currentIndex + 1) % this.words.length;
|
||
this.element.textContent = this.words[this.currentIndex];
|
||
if (reflectionElement) {
|
||
reflectionElement.textContent = this.words[this.currentIndex];
|
||
}
|
||
console.log('文字已更换为:', this.words[this.currentIndex]);
|
||
|
||
// 重置位置准备进入动画
|
||
this.element.style.transition = 'none';
|
||
this.element.style.transform = 'translateY(10px) scale(0.98)';
|
||
this.element.style.opacity = '0';
|
||
|
||
if (reflectionElement) {
|
||
reflectionElement.style.transition = 'none';
|
||
reflectionElement.style.transform = 'translateY(-10px) scale(0.98)';
|
||
reflectionElement.style.opacity = '0';
|
||
}
|
||
|
||
// 强制重绘
|
||
requestAnimationFrame(() => {
|
||
// 进入动画
|
||
this.element.style.transition = 'all 0.3s ease-out';
|
||
this.element.style.opacity = '1';
|
||
this.element.style.transform = 'translateY(0) scale(1)';
|
||
|
||
if (reflectionElement) {
|
||
reflectionElement.style.transition = 'all 0.3s ease-out';
|
||
reflectionElement.style.opacity = '0.7';
|
||
reflectionElement.style.transform = 'translateY(0) scale(1)';
|
||
}
|
||
|
||
setTimeout(() => {
|
||
this.isAnimating = false;
|
||
console.log('动画完成');
|
||
}, 300);
|
||
});
|
||
}, 200);
|
||
}
|
||
}
|
||
|
||
// 初始化文字轮播
|
||
const rotatingElement = document.querySelector('#rotating-word');
|
||
console.log('轮播元素查找结果:', rotatingElement);
|
||
|
||
if (rotatingElement) {
|
||
const textRotator = new TextRotator({
|
||
selector: '#rotating-word',
|
||
words: ['资料', '专家', '活动', '标准', '合作'],
|
||
interval: 2500
|
||
});
|
||
|
||
// 添加点击跳转功能和波纹效果
|
||
rotatingElement.addEventListener('click', function(e) {
|
||
console.log('点击轮播文字:', this.textContent);
|
||
|
||
// 创建波纹效果
|
||
const ripple = document.createElement('div');
|
||
const rect = this.getBoundingClientRect();
|
||
const size = Math.max(rect.width, rect.height);
|
||
const x = e.clientX - rect.left - size / 2;
|
||
const y = e.clientY - rect.top - size / 2;
|
||
|
||
ripple.style.cssText = `
|
||
position: absolute;
|
||
width: ${size}px;
|
||
height: ${size}px;
|
||
left: ${x}px;
|
||
top: ${y}px;
|
||
background: radial-gradient(circle, rgba(30, 64, 175, 0.3) 0%, transparent 70%);
|
||
border-radius: 50%;
|
||
transform: scale(0);
|
||
animation: ripple 0.6s ease-out;
|
||
pointer-events: none;
|
||
z-index: 1;
|
||
`;
|
||
|
||
this.style.position = 'relative';
|
||
this.appendChild(ripple);
|
||
|
||
setTimeout(() => {
|
||
ripple.remove();
|
||
}, 600);
|
||
|
||
// 跳转到新窗口
|
||
window.open('https://yundage.com/home', '_blank');
|
||
});
|
||
|
||
// 3D鼠标跟随效果已移除,保持静态显示
|
||
} else {
|
||
console.error('未找到轮播元素 #rotating-word');
|
||
}
|
||
}
|
||
|
||
/* ===== 鼠标跟随翻转卡片新闻跑马灯功能 ===== */
|
||
function init3DTicker() {
|
||
const ticker3D = document.getElementById('ticker3D');
|
||
const tickerCarousel = document.getElementById('tickerCarousel');
|
||
const tickerParticles = document.getElementById('tickerParticles');
|
||
|
||
if (!ticker3D || !tickerCarousel || !tickerParticles) {
|
||
console.log('翻转卡片跑马灯元素未找到');
|
||
return;
|
||
}
|
||
|
||
console.log('初始化鼠标跟随翻转卡片新闻跑马灯');
|
||
|
||
let newsIndex = 0;
|
||
|
||
// 新闻图片数据,对应images目录下的新闻1-10.png
|
||
const newsData = [
|
||
{ image: '新闻1.png', alt: '新闻1' },
|
||
{ image: '新闻2.png', alt: '新闻2' },
|
||
{ image: '新闻3.png', alt: '新闻3' },
|
||
{ image: '新闻4.png', alt: '新闻4' },
|
||
{ image: '新闻5.png', alt: '新闻5' },
|
||
{ image: '新闻6.png', alt: '新闻6' },
|
||
{ image: '新闻7.png', alt: '新闻7' },
|
||
{ image: '新闻8.png', alt: '新闻8' },
|
||
{ image: '新闻9.png', alt: '新闻9' },
|
||
{ image: '新闻10.png', alt: '新闻10' }
|
||
];
|
||
|
||
// 创建背景粒子
|
||
function createTickerParticles() {
|
||
tickerParticles.innerHTML = '';
|
||
|
||
for (let i = 0; i < 8; i++) {
|
||
const particle = document.createElement('div');
|
||
particle.className = 'ticker-particle';
|
||
particle.style.left = Math.random() * 100 + '%';
|
||
particle.style.animationDelay = Math.random() * 8 + 's';
|
||
particle.style.animationDuration = (Math.random() * 4 + 6) + 's';
|
||
tickerParticles.appendChild(particle);
|
||
}
|
||
}
|
||
|
||
// 双向鼠标跟随翻转效果
|
||
function initMouseFollowFlip() {
|
||
const cards = tickerCarousel.querySelectorAll('.ticker-flip-card');
|
||
let lastMouseX = 0;
|
||
let lastDirection = 0; // -1左,1右,0静止
|
||
|
||
// 鼠标移动事件
|
||
ticker3D.addEventListener('mousemove', function(e) {
|
||
const rect = ticker3D.getBoundingClientRect();
|
||
const mouseX = e.clientX - rect.left;
|
||
|
||
// 计算鼠标移动方向
|
||
const direction = mouseX > lastMouseX ? 1 : mouseX < lastMouseX ? -1 : 0;
|
||
if (direction !== 0) {
|
||
lastDirection = direction;
|
||
}
|
||
lastMouseX = mouseX;
|
||
|
||
// 为每个卡片计算翻转角度
|
||
cards.forEach((card, index) => {
|
||
const cardRect = card.getBoundingClientRect();
|
||
const cardLeft = cardRect.left - rect.left;
|
||
const cardRight = cardLeft + cardRect.width;
|
||
const cardWidth = cardRect.width;
|
||
const cardCenter = cardLeft + cardWidth / 2;
|
||
|
||
// 计算鼠标到卡片中心的距离
|
||
const distanceToCenter = Math.abs(mouseX - cardCenter);
|
||
const maxDistance = cardWidth * 2; // 影响范围扩大到卡片宽度的2倍
|
||
|
||
// 基础翻转进度(基于鼠标在卡片中的位置)
|
||
let baseProgress = 0;
|
||
|
||
if (mouseX >= cardLeft && mouseX <= cardRight) {
|
||
// 鼠标在卡片范围内
|
||
baseProgress = (mouseX - cardLeft) / cardWidth;
|
||
} else if (mouseX < cardLeft) {
|
||
// 鼠标在卡片左侧
|
||
const distanceFromLeft = cardLeft - mouseX;
|
||
baseProgress = Math.max(0, 1 - (distanceFromLeft / cardWidth));
|
||
} else {
|
||
// 鼠标在卡片右侧
|
||
const distanceFromRight = mouseX - cardRight;
|
||
baseProgress = Math.max(0, 1 - (distanceFromRight / cardWidth));
|
||
}
|
||
|
||
// 邻近卡片的影响(双向)
|
||
let neighborInfluence = 0;
|
||
|
||
// 检查相邻卡片的影响
|
||
for (let i = 0; i < cards.length; i++) {
|
||
if (i === index) continue;
|
||
|
||
const neighborCard = cards[i];
|
||
const neighborRect = neighborCard.getBoundingClientRect();
|
||
const neighborLeft = neighborRect.left - rect.left;
|
||
const neighborRight = neighborLeft + neighborRect.width;
|
||
const neighborCenter = neighborLeft + neighborRect.width / 2;
|
||
|
||
// 如果鼠标在相邻卡片上,计算对当前卡片的影响
|
||
if (mouseX >= neighborLeft && mouseX <= neighborRight) {
|
||
const neighborProgress = (mouseX - neighborLeft) / neighborRect.width;
|
||
const distance = Math.abs(index - i);
|
||
const influence = neighborProgress * Math.pow(0.6, distance); // 距离越远影响越小
|
||
|
||
// 根据方向确定影响方式
|
||
if ((i < index && lastDirection === 1) || (i > index && lastDirection === -1)) {
|
||
neighborInfluence = Math.max(neighborInfluence, influence);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 计算最终翻转进度
|
||
const finalProgress = Math.min(1, Math.max(0, baseProgress + neighborInfluence));
|
||
|
||
// 根据鼠标方向和位置决定翻转角度
|
||
let rotationAngle = 0;
|
||
|
||
if (finalProgress > 0) {
|
||
// 基础翻转角度
|
||
rotationAngle = finalProgress * 180;
|
||
|
||
// 考虑方向性:从右往左时可能需要反向翻转
|
||
if (lastDirection === -1 && mouseX < cardCenter) {
|
||
// 从右往左且鼠标在卡片左侧时,使用反向翻转
|
||
rotationAngle = (1 - finalProgress) * 180;
|
||
}
|
||
}
|
||
|
||
// 应用真正的3D翻转效果
|
||
const inner = card.querySelector('.flip-card-inner');
|
||
if (inner) {
|
||
inner.style.transform = `rotateY(${rotationAngle}deg)`;
|
||
}
|
||
|
||
// 调试信息(只显示第一张卡片)
|
||
if (index === 0) {
|
||
console.log(`Card ${index}: mouseX=${mouseX.toFixed(0)}, direction=${lastDirection}, baseProgress=${baseProgress.toFixed(2)}, neighborInfluence=${neighborInfluence.toFixed(2)}, rotation=${rotationAngle.toFixed(1)}°`);
|
||
}
|
||
});
|
||
});
|
||
|
||
// 鼠标离开时重置
|
||
ticker3D.addEventListener('mouseleave', function() {
|
||
cards.forEach(card => {
|
||
const inner = card.querySelector('.flip-card-inner');
|
||
if (inner) {
|
||
inner.style.transform = 'rotateY(0deg)';
|
||
}
|
||
});
|
||
lastMouseX = 0;
|
||
lastDirection = 0;
|
||
});
|
||
|
||
// 鼠标进入时记录初始位置
|
||
ticker3D.addEventListener('mouseenter', function(e) {
|
||
const rect = ticker3D.getBoundingClientRect();
|
||
lastMouseX = e.clientX - rect.left;
|
||
lastDirection = 0;
|
||
});
|
||
}
|
||
|
||
// 新闻轮播功能 - 更新卡片内容
|
||
function rotateNews() {
|
||
const cards = tickerCarousel.querySelectorAll('.ticker-flip-card');
|
||
|
||
cards.forEach((card, index) => {
|
||
const currentNewsIndex = (newsIndex + index) % newsData.length;
|
||
const newsItem = newsData[currentNewsIndex];
|
||
|
||
// 更新正面图片
|
||
const frontImage = card.querySelector('.flip-card-front .news-image');
|
||
if (frontImage) {
|
||
frontImage.src = `images/${newsItem.image}`;
|
||
frontImage.alt = newsItem.alt;
|
||
}
|
||
|
||
// 更新背面图片(保持一致)
|
||
const backImage = card.querySelector('.flip-card-back .news-image');
|
||
if (backImage) {
|
||
backImage.src = `images/${newsItem.image}`;
|
||
backImage.alt = newsItem.alt;
|
||
}
|
||
|
||
// 更新data属性
|
||
card.setAttribute('data-news', newsItem.alt);
|
||
card.setAttribute('data-image', newsItem.image);
|
||
});
|
||
|
||
newsIndex = (newsIndex + 1) % newsData.length;
|
||
}
|
||
|
||
// 卡片点击事件
|
||
function addCardListeners() {
|
||
const cards = tickerCarousel.querySelectorAll('.ticker-flip-card');
|
||
|
||
cards.forEach(card => {
|
||
card.addEventListener('click', function() {
|
||
const newsText = this.getAttribute('data-news');
|
||
console.log('点击新闻卡片:', newsText);
|
||
// 点击后跳转到微信文章,在新窗口中打开
|
||
window.open('https://mp.weixin.qq.com/s/QfUgaORgErymzezvSISlJQ', '_blank');
|
||
});
|
||
|
||
// 悬停时暂停轮播
|
||
card.addEventListener('mouseenter', function() {
|
||
clearInterval(newsRotationInterval);
|
||
});
|
||
|
||
// 离开时恢复轮播
|
||
card.addEventListener('mouseleave', function() {
|
||
startNewsRotation();
|
||
});
|
||
});
|
||
}
|
||
|
||
function showNewsInChat(newsText) {
|
||
const messageInput = document.getElementById('messageInput');
|
||
if (messageInput) {
|
||
const cleanText = newsText.replace(/[🔥📚👥🎯🤝🚀💡🌟]/g, '').trim();
|
||
messageInput.value = `告诉我更多关于"${cleanText}"的信息`;
|
||
messageInput.focus();
|
||
}
|
||
}
|
||
|
||
// 启动新闻轮播
|
||
let newsRotationInterval;
|
||
function startNewsRotation() {
|
||
clearInterval(newsRotationInterval);
|
||
newsRotationInterval = setInterval(rotateNews, 5000); // 每5秒轮换一次
|
||
}
|
||
|
||
// 响应式处理
|
||
function handleResize() {
|
||
createTickerParticles();
|
||
}
|
||
|
||
// 初始化
|
||
createTickerParticles();
|
||
addCardListeners();
|
||
startNewsRotation();
|
||
initMouseFollowFlip(); // 初始化鼠标跟随翻转
|
||
|
||
window.addEventListener('resize', handleResize);
|
||
|
||
console.log('鼠标跟随翻转卡片新闻跑马灯初始化完成');
|
||
}
|
||
|
||
/* ===== 聊天管理器相关功能 ===== */
|
||
function initChatManagerListeners() {
|
||
if (!window.chatManager) return;
|
||
|
||
// 监听聊天切换事件
|
||
window.addEventListener('chatManager:chatSwitched', (e) => {
|
||
const { chatId, chat } = e.detail;
|
||
console.log('切换到聊天:', chatId);
|
||
currentChatId = chatId;
|
||
loadChatHistory(chatId);
|
||
});
|
||
|
||
// 监听聊天删除事件
|
||
window.addEventListener('chatManager:chatDeleted', (e) => {
|
||
const { chatId } = e.detail;
|
||
console.log('删除聊天:', chatId);
|
||
// 从侧边栏移除
|
||
const chatItem = document.querySelector(`[data-chat-id="${chatId}"]`);
|
||
if (chatItem) {
|
||
chatItem.remove();
|
||
}
|
||
});
|
||
|
||
// 监听新聊天创建事件
|
||
window.addEventListener('chatManager:newChatStarted', () => {
|
||
console.log('新聊天事件已触发');
|
||
// 不要在这里调用 startNewChat(),避免递归
|
||
});
|
||
|
||
// 监听聊天标题更新事件
|
||
window.addEventListener('chatManager:chatTitleUpdated', (e) => {
|
||
const { chatId, title } = e.detail;
|
||
console.log('更新聊天标题:', chatId, title);
|
||
// 更新侧边栏中的标题
|
||
const chatItem = document.querySelector(`[data-chat-id="${chatId}"]`);
|
||
if (chatItem) {
|
||
const chatTitle = chatItem.querySelector('.chat-title');
|
||
if (chatTitle) {
|
||
chatTitle.textContent = title;
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
async function loadUserChatHistory() {
|
||
// 如果有后端API,从后端加载会话列表
|
||
if (window.ChatAPIService) {
|
||
try {
|
||
const result = await window.ChatAPIService.getConversations();
|
||
if (result.success) {
|
||
// 清空现有的聊天历史显示
|
||
const chatHistoryList = document.querySelector('.chat-history-list');
|
||
if (chatHistoryList) {
|
||
chatHistoryList.innerHTML = '';
|
||
|
||
// 添加每个会话到侧边栏
|
||
result.conversations.forEach(conversation => {
|
||
// 后端返回的会话格式可能是 {id, userId, title, mode, createdAt, updatedAt}
|
||
addChatToSidebar(conversation.id, conversation.title || '新对话');
|
||
});
|
||
}
|
||
|
||
console.log(`从后端加载了 ${result.conversations.length} 个会话`);
|
||
}
|
||
} catch (error) {
|
||
console.error('加载会话列表失败:', error);
|
||
// 降级到本地存储
|
||
loadLocalChatHistory();
|
||
}
|
||
} else {
|
||
// 没有后端API,使用本地存储
|
||
loadLocalChatHistory();
|
||
}
|
||
}
|
||
|
||
// 从本地存储加载聊天历史
|
||
function loadLocalChatHistory() {
|
||
if (!window.chatManager) return;
|
||
|
||
// 获取所有聊天
|
||
const chats = window.chatManager.getAllChats();
|
||
|
||
// 清空现有的聊天历史显示
|
||
const chatHistoryList = document.querySelector('.chat-history-list');
|
||
if (chatHistoryList) {
|
||
chatHistoryList.innerHTML = '';
|
||
|
||
// 添加每个聊天到侧边栏
|
||
chats.forEach(chat => {
|
||
addChatToSidebar(chat.id, chat.title);
|
||
});
|
||
}
|
||
|
||
console.log(`从本地加载了 ${chats.length} 个聊天历史`);
|
||
}
|
||
|
||
// 监听用户登录状态变化
|
||
function initUserChangeListener() {
|
||
let previousUser = localStorage.getItem('yundage_current_user');
|
||
|
||
// 监听 storage 事件(其他标签页的变化)
|
||
window.addEventListener('storage', function(e) {
|
||
if (e.key === 'yundage_current_user') {
|
||
handleUserChange(e.oldValue, e.newValue);
|
||
}
|
||
});
|
||
|
||
// 定期检查当前标签页的用户变化
|
||
setInterval(() => {
|
||
const currentUser = localStorage.getItem('yundage_current_user');
|
||
if (currentUser !== previousUser) {
|
||
handleUserChange(previousUser, currentUser);
|
||
previousUser = currentUser;
|
||
}
|
||
}, 1000);
|
||
|
||
// 监听聊天管理器的用户变化事件
|
||
window.addEventListener('chatManager:userChanged', () => {
|
||
loadUserChatHistory();
|
||
});
|
||
}
|
||
|
||
// 处理用户变化
|
||
function handleUserChange(oldUserStr, newUserStr) {
|
||
console.log('检测到用户变化');
|
||
|
||
const oldUser = oldUserStr ? JSON.parse(oldUserStr) : null;
|
||
const newUser = newUserStr ? JSON.parse(newUserStr) : null;
|
||
|
||
if (oldUser?.id !== newUser?.id) {
|
||
console.log(`用户从 ${oldUser?.username || '未登录'} 切换到 ${newUser?.username || '未登录'}`);
|
||
|
||
// 重新加载聊天记录
|
||
if (window.chatManager) {
|
||
window.chatManager.reloadForUser();
|
||
}
|
||
|
||
// 清空当前显示的聊天内容
|
||
const chatMessages = document.getElementById('chatMessages');
|
||
if (chatMessages) {
|
||
chatMessages.innerHTML = '';
|
||
}
|
||
|
||
// 切回欢迎界面
|
||
startNewChat();
|
||
}
|
||
}
|
||
|
||
// 更新loadChatHistory函数以使用新的聊天管理器
|
||
async function loadChatHistory(chatId) {
|
||
// 清空当前聊天显示
|
||
const chatMessages = document.getElementById('chatMessages');
|
||
if (chatMessages) {
|
||
chatMessages.innerHTML = '';
|
||
}
|
||
|
||
// 设置当前聊天ID
|
||
currentChatId = chatId;
|
||
|
||
// 直接使用本地存储加载消息
|
||
// 因为HippoDeepService的getConversationMessages实际上也是从本地chatManager加载的
|
||
// 避免重复加载导致消息显示两次
|
||
loadLocalChatMessages(chatId);
|
||
|
||
// 显示聊天容器
|
||
showChatContainer();
|
||
|
||
// 高亮当前聊天项
|
||
highlightActiveChatItem(chatId);
|
||
|
||
// 强制触发一次布局重新计算,确保输入框宽度正确
|
||
setTimeout(() => {
|
||
const chatModeInput = document.getElementById('chatModeInput');
|
||
if (chatModeInput) {
|
||
// 触发重新渲染
|
||
chatModeInput.style.display = 'none';
|
||
void chatModeInput.offsetHeight; // 强制重排
|
||
chatModeInput.style.display = 'block';
|
||
}
|
||
|
||
// 如果有视口处理器,也触发重新计算
|
||
if (window.viewportHandler) {
|
||
window.viewportHandler.forceRecalculate();
|
||
}
|
||
}, 50);
|
||
}
|
||
|
||
// 高亮当前活动的聊天项
|
||
function highlightActiveChatItem(chatId) {
|
||
// 移除所有聊天项的active类
|
||
document.querySelectorAll('.chat-history-item').forEach(item => {
|
||
item.classList.remove('active');
|
||
});
|
||
|
||
// 添加active类到当前聊天项
|
||
const activeItem = document.querySelector(`[data-chat-id="${chatId}"]`);
|
||
if (activeItem) {
|
||
activeItem.classList.add('active');
|
||
}
|
||
}
|
||
|
||
// 从本地存储加载聊天消息
|
||
function loadLocalChatMessages(chatId) {
|
||
// 从聊天管理器加载消息
|
||
if (window.chatManager) {
|
||
console.log(`[loadChatHistory] 从本地加载聊天: ${chatId}`);
|
||
console.log(`[loadChatHistory] 当前chatManager.currentChatId: ${window.chatManager.currentChatId}`);
|
||
|
||
// 如果不是当前聊天,才需要切换
|
||
if (window.chatManager.currentChatId !== chatId) {
|
||
console.log(`[loadChatHistory] 切换到聊天: ${chatId}`);
|
||
window.chatManager.switchToChat(chatId);
|
||
}
|
||
|
||
const chat = window.chatManager.getChat(chatId);
|
||
if (chat && chat.messages) {
|
||
const chatMessages = document.getElementById('chatMessages');
|
||
chat.messages.forEach(message => {
|
||
// 直接创建消息元素,不调用addMessage以避免重复保存
|
||
const messageItem = document.createElement('div');
|
||
messageItem.className = 'message-item mb-4';
|
||
|
||
const isUser = message.type === 'user';
|
||
const messageTime = new Date(message.timestamp).toLocaleTimeString('zh-CN', {
|
||
hour: '2-digit',
|
||
minute: '2-digit'
|
||
});
|
||
|
||
// 渲染内容(支持Markdown)
|
||
let renderedContent = message.content;
|
||
if (window.markdownRenderer) {
|
||
renderedContent = window.markdownRenderer.render(message.content);
|
||
}
|
||
|
||
if (isUser) {
|
||
messageItem.innerHTML = `
|
||
<div class="flex justify-end">
|
||
<div class="message user-message px-4 py-3">
|
||
<div class="text-sm text-gray-800 markdown-content">${renderedContent}</div>
|
||
</div>
|
||
</div>
|
||
<div class="text-xs text-gray-500 text-right mt-1">${messageTime}</div>
|
||
`;
|
||
} else {
|
||
messageItem.innerHTML = `
|
||
<div class="flex justify-start">
|
||
<div class="message ai-message px-4 py-3">
|
||
<div class="text-sm text-gray-800 markdown-content">${renderedContent}</div>
|
||
</div>
|
||
</div>
|
||
<div class="text-xs text-gray-500 mt-1">${messageTime}</div>
|
||
`;
|
||
}
|
||
|
||
chatMessages.appendChild(messageItem);
|
||
});
|
||
|
||
// 滚动到底部
|
||
chatMessages.scrollTop = chatMessages.scrollHeight;
|
||
|
||
// 切换到聊天模式
|
||
showChatContainer();
|
||
|
||
// 强制重新计算布局,确保对齐
|
||
setTimeout(() => {
|
||
if (window.viewportHandler) {
|
||
window.viewportHandler.forceRecalculate();
|
||
}
|
||
}, 100);
|
||
} else {
|
||
// 即使没有消息也要切换到聊天模式
|
||
showChatContainer();
|
||
}
|
||
} else {
|
||
// 兼容旧的加载方式
|
||
loadChatHistoryLegacy(chatId);
|
||
}
|
||
}
|
||
|
||
// 保留旧的加载方式作为后备
|
||
function loadChatHistoryLegacy(chatId) {
|
||
// 原来的loadChatHistory实现
|
||
if (chatHistoryData.has(chatId)) {
|
||
const messages = chatHistoryData.get(chatId);
|
||
const chatMessages = document.getElementById('chatMessages');
|
||
|
||
messages.forEach(message => {
|
||
const messageItem = document.createElement('div');
|
||
messageItem.className = 'message-item mb-4';
|
||
|
||
const isUser = message.type === 'user';
|
||
const messageTime = new Date(message.timestamp).toLocaleTimeString('zh-CN', {
|
||
hour: '2-digit',
|
||
minute: '2-digit'
|
||
});
|
||
|
||
if (isUser) {
|
||
messageItem.innerHTML = `
|
||
<div class="flex justify-end">
|
||
<div class="message user-message px-4 py-3">
|
||
<p class="text-sm text-gray-800">${message.content}</p>
|
||
</div>
|
||
</div>
|
||
<div class="text-xs text-gray-500 text-right mt-1">${messageTime}</div>
|
||
`;
|
||
} else {
|
||
messageItem.innerHTML = `
|
||
<div class="flex justify-start">
|
||
<div class="message ai-message px-4 py-3">
|
||
<p class="text-sm text-gray-800">${message.content}</p>
|
||
</div>
|
||
</div>
|
||
<div class="text-xs text-gray-500 mt-1">${messageTime}</div>
|
||
`;
|
||
}
|
||
|
||
chatMessages.appendChild(messageItem);
|
||
});
|
||
|
||
chatMessages.scrollTop = chatMessages.scrollHeight;
|
||
showChatContainer();
|
||
|
||
// 强制重新计算布局,确保对齐
|
||
setTimeout(() => {
|
||
if (window.viewportHandler) {
|
||
window.viewportHandler.forceRecalculate();
|
||
}
|
||
}, 100);
|
||
}
|
||
}
|
||
|
||
// 全局暴露必要的函数
|
||
window.startNewChat = startNewChat;
|
||
window.toggleDeepSearch = toggleDeepSearch;
|
||
|
||
/* ===== Apple风格背景动画 ===== */
|
||
function initBackground() {
|
||
const gradientElement = document.getElementById('animatedGradient');
|
||
if (!gradientElement) return;
|
||
|
||
let time = 0;
|
||
|
||
const animateGradient = () => {
|
||
// 如果背景动画被暂停,延迟检查是否恢复
|
||
if (window.pauseBackgroundAnimation) {
|
||
setTimeout(() => {
|
||
requestAnimationFrame(animateGradient);
|
||
}, 1000); // 每秒检查一次
|
||
return;
|
||
}
|
||
|
||
time += 0.0056; // 减慢动画速度让过渡更平滑
|
||
|
||
// 柔和、低饱和度的冷色调,包含近白色
|
||
const colors = [
|
||
{ r: 180, g: 205, b: 215, a: 0.5 }, // 柔和蓝
|
||
{ r: 160, g: 195, b: 220, a: 0.4 }, // 柔和天蓝
|
||
{ r: 160, g: 220, b: 200, a: 0.35 }, // 柔和海蓝
|
||
{ r: 240, g: 248, b: 255, a: 0.6 }, // 爱丽丝蓝 (近白)
|
||
{ r: 210, g: 210, b: 230, a: 0.3 }, // 柔和薰衣草
|
||
{ r: 190, g: 140, b: 200, a: 0.25 } // 柔和紫
|
||
];
|
||
|
||
// 平滑的颜色插值函数
|
||
const lerpColor = (color1, color2, t) => ({
|
||
r: Math.round(color1.r + (color2.r - color1.r) * t),
|
||
g: Math.round(color1.g + (color2.g - color1.g) * t),
|
||
b: Math.round(color1.b + (color2.b - color1.b) * t),
|
||
a: color1.a + (color2.a - color1.a) * t
|
||
});
|
||
|
||
// 为每个渐变创建平滑的颜色变化
|
||
const getInterpolatedColor = (colorIndex, speed) => {
|
||
const totalColors = colors.length;
|
||
const colorPhase = (time * speed) % totalColors;
|
||
const currentIndex = Math.floor(colorPhase) % totalColors;
|
||
const nextIndex = (currentIndex + 1) % totalColors;
|
||
const t = colorPhase - Math.floor(colorPhase);
|
||
|
||
return lerpColor(colors[currentIndex], colors[nextIndex], t);
|
||
};
|
||
|
||
// 创建多个动画渐变,使用平滑的三角函数
|
||
const gradient1Size = 60 + Math.sin(time * 0.8) * 20;
|
||
const gradient2Size = 50 + Math.cos(time * 1.1) * 15;
|
||
const gradient3Size = 70 + Math.sin(time * 0.6) * 25;
|
||
const gradient4Size = 45 + Math.cos(time * 1.3) * 18;
|
||
|
||
// 更平滑的位置变化
|
||
const gradient1X = 30 + Math.sin(time * 0.3) * 25;
|
||
const gradient1Y = 40 + Math.cos(time * 0.4) * 20;
|
||
|
||
const gradient2X = 70 + Math.cos(time * 0.35) * 30;
|
||
const gradient2Y = 60 + Math.sin(time * 0.25) * 25;
|
||
|
||
const gradient3X = 20 + Math.sin(time * 0.45) * 35;
|
||
const gradient3Y = 80 + Math.cos(time * 0.2) * 30;
|
||
|
||
const gradient4X = 80 + Math.cos(time * 0.5) * 25;
|
||
const gradient4Y = 20 + Math.sin(time * 0.3) * 20;
|
||
|
||
// 使用插值后的颜色
|
||
const color1 = getInterpolatedColor(0, 0.3);
|
||
const color2 = getInterpolatedColor(1, 0.4);
|
||
const color3 = getInterpolatedColor(2, 0.2);
|
||
const color4 = getInterpolatedColor(3, 0.5);
|
||
|
||
// 添加额外的颜色点以增强流动感
|
||
const gradient5Size = 55 + Math.sin(time * 0.9) * 18;
|
||
const gradient6Size = 65 + Math.cos(time * 0.7) * 22;
|
||
|
||
const gradient5X = 50 + Math.cos(time * 0.4) * 20;
|
||
const gradient5Y = 30 + Math.sin(time * 0.5) * 15;
|
||
|
||
const gradient6X = 40 + Math.sin(time * 0.6) * 28;
|
||
const gradient6Y = 70 + Math.cos(time * 0.35) * 22;
|
||
|
||
const color5 = getInterpolatedColor(4, 0.35);
|
||
const color6 = getInterpolatedColor(5, 0.45);
|
||
|
||
const backgroundStyle = `
|
||
radial-gradient(${gradient1Size}% ${gradient1Size}% at ${gradient1X}% ${gradient1Y}%,
|
||
rgba(${color1.r}, ${color1.g}, ${color1.b}, ${color1.a}) 0%,
|
||
transparent 60%),
|
||
radial-gradient(${gradient2Size}% ${gradient2Size}% at ${gradient2X}% ${gradient2Y}%,
|
||
rgba(${color2.r}, ${color2.g}, ${color2.b}, ${color2.a}) 0%,
|
||
transparent 65%),
|
||
radial-gradient(${gradient3Size}% ${gradient3Size}% at ${gradient3X}% ${gradient3Y}%,
|
||
rgba(${color3.r}, ${color3.g}, ${color3.b}, ${color3.a}) 0%,
|
||
transparent 70%),
|
||
radial-gradient(${gradient4Size}% ${gradient4Size}% at ${gradient4X}% ${gradient4Y}%,
|
||
rgba(${color4.r}, ${color4.g}, ${color4.b}, ${color4.a}) 0%,
|
||
transparent 55%),
|
||
radial-gradient(${gradient5Size}% ${gradient5Size}% at ${gradient5X}% ${gradient5Y}%,
|
||
rgba(${color5.r}, ${color5.g}, ${color5.b}, ${color5.a}) 0%,
|
||
transparent 75%),
|
||
radial-gradient(${gradient6Size}% ${gradient6Size}% at ${gradient6X}% ${gradient6Y}%,
|
||
rgba(${color6.r}, ${color6.g}, ${color6.b}, ${color6.a}) 0%,
|
||
transparent 68%),
|
||
linear-gradient(135deg,
|
||
rgba(248, 250, 252, 0.9) 0%,
|
||
rgba(241, 245, 249, 0.8) 50%,
|
||
rgba(226, 232, 240, 0.9) 100%)
|
||
`;
|
||
|
||
gradientElement.style.background = backgroundStyle;
|
||
requestAnimationFrame(animateGradient);
|
||
};
|
||
|
||
requestAnimationFrame(animateGradient);
|
||
}
|
||
|
||
// ===== 初始化工具下拉菜单 =====
|
||
function initToolsDropdown() {
|
||
// 存储状态
|
||
let isDeepResearchEnabled = false;
|
||
let isShowThinkingEnabled = false;
|
||
|
||
// 欢迎模式的工具按钮和下拉菜单
|
||
const toolsMenuBtn = document.getElementById('toolsMenuBtn');
|
||
const toolsDropdown = document.getElementById('toolsDropdown');
|
||
const deepResearchItem = document.getElementById('deepResearchItem');
|
||
const showThinkingItem = document.getElementById('showThinkingItem');
|
||
const deepResearchStatus = document.getElementById('deepResearchStatus');
|
||
const showThinkingStatus = document.getElementById('showThinkingStatus');
|
||
|
||
// 聊天模式的工具按钮和下拉菜单
|
||
const chatModeToolsMenuBtn = document.getElementById('chatModeToolsMenuBtn');
|
||
const chatModeToolsDropdown = document.getElementById('chatModeToolsDropdown');
|
||
const chatModeDeepResearchItem = document.getElementById('chatModeDeepResearchItem');
|
||
const chatModeShowThinkingItem = document.getElementById('chatModeShowThinkingItem');
|
||
const chatModeDeepResearchStatus = document.getElementById('chatModeDeepResearchStatus');
|
||
const chatModeShowThinkingStatus = document.getElementById('chatModeShowThinkingStatus');
|
||
|
||
// 点击按钮显示/隐藏下拉菜单
|
||
if (toolsMenuBtn && toolsDropdown) {
|
||
toolsMenuBtn.addEventListener('click', function(e) {
|
||
e.preventDefault();
|
||
e.stopPropagation();
|
||
const isShowing = toolsDropdown.classList.contains('show');
|
||
|
||
if (isShowing) {
|
||
toolsDropdown.classList.remove('show');
|
||
toolsMenuBtn.classList.remove('active');
|
||
setTimeout(() => {
|
||
toolsDropdown.style.display = 'none';
|
||
}, 200);
|
||
} else {
|
||
toolsDropdown.style.display = 'block';
|
||
setTimeout(() => {
|
||
toolsDropdown.classList.add('show');
|
||
toolsMenuBtn.classList.add('active');
|
||
}, 10);
|
||
}
|
||
});
|
||
}
|
||
|
||
// 聊天模式工具按钮
|
||
if (chatModeToolsMenuBtn && chatModeToolsDropdown) {
|
||
chatModeToolsMenuBtn.addEventListener('click', function(e) {
|
||
e.stopPropagation();
|
||
const isShowing = chatModeToolsDropdown.classList.contains('show');
|
||
|
||
if (isShowing) {
|
||
chatModeToolsDropdown.classList.remove('show');
|
||
chatModeToolsMenuBtn.classList.remove('active');
|
||
setTimeout(() => {
|
||
chatModeToolsDropdown.style.display = 'none';
|
||
}, 200);
|
||
} else {
|
||
chatModeToolsDropdown.style.display = 'block';
|
||
setTimeout(() => {
|
||
chatModeToolsDropdown.classList.add('show');
|
||
chatModeToolsMenuBtn.classList.add('active');
|
||
}, 10);
|
||
}
|
||
});
|
||
}
|
||
|
||
// 点击概念感知模式
|
||
let isDeepResearchToggling = false; // 防止重复触发
|
||
function toggleDeepResearch(e) {
|
||
// 如果事件存在,阻止冒泡
|
||
if (e) {
|
||
e.stopPropagation();
|
||
e.stopImmediatePropagation();
|
||
}
|
||
|
||
// 防止重复触发
|
||
if (isDeepResearchToggling) return;
|
||
isDeepResearchToggling = true;
|
||
|
||
isDeepResearchEnabled = !isDeepResearchEnabled;
|
||
|
||
// 更新状态文字
|
||
if (deepResearchStatus) {
|
||
deepResearchStatus.textContent = isDeepResearchEnabled ? '开' : '关';
|
||
deepResearchStatus.style.color = isDeepResearchEnabled ? '#2563eb' : '#666';
|
||
}
|
||
if (chatModeDeepResearchStatus) {
|
||
chatModeDeepResearchStatus.textContent = isDeepResearchEnabled ? '开' : '关';
|
||
chatModeDeepResearchStatus.style.color = isDeepResearchEnabled ? '#2563eb' : '#666';
|
||
}
|
||
|
||
// 更新菜单项样式
|
||
if (isDeepResearchEnabled) {
|
||
if (deepResearchItem) deepResearchItem.classList.add('active');
|
||
if (chatModeDeepResearchItem) chatModeDeepResearchItem.classList.add('active');
|
||
} else {
|
||
if (deepResearchItem) deepResearchItem.classList.remove('active');
|
||
if (chatModeDeepResearchItem) chatModeDeepResearchItem.classList.remove('active');
|
||
}
|
||
|
||
// 同步更新两个开关的状态
|
||
const deepResearchToggle = document.getElementById('deepResearchToggle');
|
||
const chatModeDeepResearchToggle = document.getElementById('chatModeDeepResearchToggle');
|
||
|
||
if (deepResearchToggle && deepResearchToggle.checked !== isDeepResearchEnabled) {
|
||
deepResearchToggle.checked = isDeepResearchEnabled;
|
||
}
|
||
|
||
if (chatModeDeepResearchToggle && chatModeDeepResearchToggle.checked !== isDeepResearchEnabled) {
|
||
chatModeDeepResearchToggle.checked = isDeepResearchEnabled;
|
||
}
|
||
|
||
console.log('概念感知模式:', isDeepResearchEnabled ? '开启' : '关闭');
|
||
|
||
// 延迟重置标志
|
||
setTimeout(() => {
|
||
isDeepResearchToggling = false;
|
||
}, 100);
|
||
}
|
||
|
||
// 点击显示思考
|
||
let isShowThinkingToggling = false; // 防止重复触发
|
||
function toggleShowThinking(e) {
|
||
// 如果事件存在,阻止冒泡
|
||
if (e) {
|
||
e.stopPropagation();
|
||
e.stopImmediatePropagation();
|
||
}
|
||
|
||
// 防止重复触发
|
||
if (isShowThinkingToggling) return;
|
||
isShowThinkingToggling = true;
|
||
|
||
isShowThinkingEnabled = !isShowThinkingEnabled;
|
||
|
||
// 更新状态文字
|
||
if (showThinkingStatus) {
|
||
showThinkingStatus.textContent = isShowThinkingEnabled ? '开' : '关';
|
||
showThinkingStatus.style.color = isShowThinkingEnabled ? '#2563eb' : '#666';
|
||
}
|
||
if (chatModeShowThinkingStatus) {
|
||
chatModeShowThinkingStatus.textContent = isShowThinkingEnabled ? '开' : '关';
|
||
chatModeShowThinkingStatus.style.color = isShowThinkingEnabled ? '#2563eb' : '#666';
|
||
}
|
||
|
||
// 更新菜单项样式
|
||
if (isShowThinkingEnabled) {
|
||
if (showThinkingItem) showThinkingItem.classList.add('active');
|
||
if (chatModeShowThinkingItem) chatModeShowThinkingItem.classList.add('active');
|
||
} else {
|
||
if (showThinkingItem) showThinkingItem.classList.remove('active');
|
||
if (chatModeShowThinkingItem) chatModeShowThinkingItem.classList.remove('active');
|
||
}
|
||
|
||
console.log('显示思考:', isShowThinkingEnabled ? '开启' : '关闭');
|
||
|
||
// 延迟重置标志
|
||
setTimeout(() => {
|
||
isShowThinkingToggling = false;
|
||
}, 100);
|
||
}
|
||
|
||
// 绑定点击事件 - 只绑定到开关本身,避免重复触发
|
||
const deepResearchToggle = document.getElementById('deepResearchToggle');
|
||
const showThinkingToggle = document.getElementById('showThinkingToggle');
|
||
const chatModeDeepResearchToggle = document.getElementById('chatModeDeepResearchToggle');
|
||
const chatModeShowThinkingToggle = document.getElementById('chatModeShowThinkingToggle');
|
||
|
||
if (deepResearchToggle) {
|
||
deepResearchToggle.addEventListener('change', function(e) {
|
||
e.stopPropagation();
|
||
toggleDeepResearch();
|
||
});
|
||
}
|
||
|
||
if (showThinkingToggle) {
|
||
showThinkingToggle.addEventListener('change', function(e) {
|
||
e.stopPropagation();
|
||
toggleShowThinking();
|
||
});
|
||
}
|
||
|
||
if (chatModeDeepResearchToggle) {
|
||
chatModeDeepResearchToggle.addEventListener('change', function(e) {
|
||
e.stopPropagation();
|
||
toggleDeepResearch();
|
||
});
|
||
}
|
||
|
||
if (chatModeShowThinkingToggle) {
|
||
chatModeShowThinkingToggle.addEventListener('change', function(e) {
|
||
e.stopPropagation();
|
||
toggleShowThinking();
|
||
});
|
||
}
|
||
|
||
// 点击其他地方关闭下拉菜单
|
||
document.addEventListener('click', function(e) {
|
||
if (toolsDropdown && !toolsMenuBtn.contains(e.target) && !toolsDropdown.contains(e.target)) {
|
||
toolsDropdown.classList.remove('show');
|
||
toolsMenuBtn.classList.remove('active');
|
||
setTimeout(() => {
|
||
toolsDropdown.style.display = 'none';
|
||
}, 200);
|
||
}
|
||
|
||
if (chatModeToolsDropdown && !chatModeToolsMenuBtn.contains(e.target) && !chatModeToolsDropdown.contains(e.target)) {
|
||
chatModeToolsDropdown.classList.remove('show');
|
||
chatModeToolsMenuBtn.classList.remove('active');
|
||
setTimeout(() => {
|
||
chatModeToolsDropdown.style.display = 'none';
|
||
}, 200);
|
||
}
|
||
});
|
||
|
||
// 导出获取状态的方法
|
||
window.getToolsSettings = function() {
|
||
return {
|
||
deepResearch: isDeepResearchEnabled,
|
||
showThinking: isShowThinkingEnabled
|
||
};
|
||
};
|
||
} |