chenpei d0dc94e36d feat: 将弹幕功能替换为AI聊天助手
新功能:
- 创建AI聊天助手组件(AIChatWidget)
- 支持文字+图片多模态对话
- 集成Gemini AI API
- 支持图片编辑和内容生成
- 实时对话历史管理
- 优雅的UI设计,支持黑夜模式

技术实现:
- 后端API代理Gemini API(/api/ai-chat)
- 支持最多5张图片上传
- 流式响应处理
- 对话历史管理
- Base64图片处理

配置:
- 在.env中添加Gemini API配置
- GEMINI_API_KEY
- GEMINI_API_BASE_URL
- GEMINI_MODEL

替换原弹幕功能,提供更强大的AI助手体验!
2025-10-27 17:45:37 +08:00

404 lines
16 KiB
HTML
Executable File
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.

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AI思维导图 - 由壹点AIGC</title>
<link rel="icon" href="https://s1.imagehub.cc/images/2025/02/24/f2135789cfaa56e2360f027673b35f2d.png" type="image/png">
<link rel="stylesheet" href="/css/bootstrap.min.css">
<link rel="stylesheet" href="/css/style.css">
<link rel="stylesheet" href="/css/fix-black-bars.css">
<link rel="stylesheet" href="/css/sidebar.css">
<!-- 加载导航栏版本管理脚本 -->
<script src="/js/navbar-version.js"></script>
<!-- 检查登录状态 -->
<script>
// 页面加载时检查用户是否已登录
document.addEventListener('DOMContentLoaded', function() {
checkAuth();
});
// 检查用户是否已登录
async function checkAuth() {
try {
// 从localStorage获取登录状态和用户信息
const isLoggedIn = localStorage.getItem('isLoggedIn') === 'true';
const username = localStorage.getItem('username');
console.log('登录状态检查:', { isLoggedIn, username });
// 如果未登录,重定向到登录页面
if (!isLoggedIn || !username) {
console.log('未找到登录信息,重定向到登录页面');
window.location.href = '/login.html?redirect=' + encodeURIComponent(window.location.pathname);
return;
}
// 验证token有效性
try {
const response = await fetch('/api/auth/check', {
method: 'GET',
credentials: 'include' // 包含cookies
});
if (!response.ok) {
// Token无效,清除本地存储并重定向到登录页面
localStorage.removeItem('isLoggedIn');
localStorage.removeItem('username');
localStorage.removeItem('userRole');
window.location.href = '/login.html?redirect=' + encodeURIComponent(window.location.pathname);
return;
}
const data = await response.json();
if (!data.authenticated) {
// 未通过身份验证,重定向到登录页面
localStorage.removeItem('isLoggedIn');
localStorage.removeItem('username');
localStorage.removeItem('userRole');
window.location.href = '/login.html?redirect=' + encodeURIComponent(window.location.pathname);
}
} catch (error) {
console.error('验证token时出错:', error);
// 发生错误时尝试继续,依赖本地存储的状态
}
} catch (error) {
console.error('检查登录状态时出错:', error);
}
}
</script>
<style>
body, html {
margin: 0;
padding: 0;
height: 100%;
overflow: hidden;
background-color: #1e1e1e; /* 深色背景 */
}
.iframe-container {
position: absolute;
top: 64px;
left: 0;
right: 0;
bottom: 0;
overflow: hidden;
}
iframe {
width: 100%;
height: 100%;
border: none;
opacity: 0;
transition: opacity 0.5s ease;
}
iframe.loaded {
opacity: 1;
}
/* 加载动画容器 */
.loading-container {
position: absolute;
top: 64px;
left: 0;
right: 0;
bottom: 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background-color: #1e1e1e;
color: #ffffff;
z-index: 100;
transition: opacity 0.5s ease;
}
.loading-container.hidden {
opacity: 0;
pointer-events: none;
}
/* 加载标题 */
.loading-title {
font-size: 24px;
margin-bottom: 20px;
}
/* 加载进度条 */
.loading-progress {
width: 300px;
height: 4px;
background-color: #333333;
border-radius: 2px;
margin-bottom: 20px;
overflow: hidden;
}
.loading-progress-bar {
height: 100%;
width: 0%;
background-color: #8c61ff;
border-radius: 2px;
transition: width 0.3s ease;
animation: progress-animation 1.5s infinite ease-in-out;
}
/* 进度条动画 */
@keyframes progress-animation {
0% {
width: 0%;
margin-left: 0;
}
50% {
width: 70%;
margin-left: 0;
}
70% {
width: 70%;
margin-left: 30%;
}
100% {
width: 0%;
margin-left: 100%;
}
}
/* 加载提示文本 */
.loading-text {
font-size: 16px;
color: #aaaaaa;
}
</style>
</head>
<body>
<!-- 导入公共导航栏 -->
<div id="navbar-container"></div>
<!-- 添加加载动画 -->
<div class="loading-container" id="loading-container">
<div class="loading-title">思维导图加载中</div>
<div class="loading-progress">
<div class="loading-progress-bar" id="loading-progress-bar"></div>
</div>
<div class="loading-text">正在准备您的思维导图,请稍候...</div>
</div>
<div class="iframe-container">
<iframe src="/mind-map-main/index.html" id="mindmap-frame" allow="fullscreen"></iframe>
</div>
<script>
// 加载公共导航栏
document.addEventListener('DOMContentLoaded', function() {
fetch('/navbar-fragment.html')
.then(response => response.text())
.then(data => {
document.getElementById('navbar-container').innerHTML = data;
// 导航栏加载完成后,重新执行版本管理脚本
if (typeof getSystemVersion === 'function') {
setTimeout(() => {
getSystemVersion();
}, 100);
}
// 动态加载navbar-version.js脚本
const navbarScript = document.createElement('script');
navbarScript.src = '/js/navbar-version.js';
navbarScript.onload = function() {
// 脚本加载完成后,如果定义了getSystemVersion函数则调用
if (typeof getSystemVersion === 'function') {
getSystemVersion();
}
};
document.body.appendChild(navbarScript);
})
.catch(error => {
console.error('加载导航栏失败:', error);
});
// 隐藏AI按钮的函数
function hideAIButton() {
try {
const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
// 1. 创建并注入CSS样式
if (!iframeDoc.getElementById('hide-ai-button-style')) {
const styleElement = iframeDoc.createElement('style');
styleElement.id = 'hide-ai-button-style';
styleElement.textContent = `
/* 隐藏所有可能的AI按钮 */
[data-command="AI"],
.bts-item[title="AI"],
div[data-type="ai"],
.toolbar-item[title="AI"],
.toolbar div[data-command="AI"],
.btnlist-item[data-command="AI"],
.btn-item[data-command="AI"],
svg[data-icon="robot"],
svg[data-type="robot"] {
display: none !important;
visibility: hidden !important;
opacity: 0 !important;
pointer-events: none !important;
width: 0 !important;
height: 0 !important;
position: absolute !important;
left: -9999px !important;
}
`;
iframeDoc.head.appendChild(styleElement);
console.log('已注入CSS样式');
}
// 2. 直接查找和隐藏AI按钮
const aiButtons = Array.from(iframeDoc.querySelectorAll('*')).filter(el => {
// 检查各种可能的属性和内容
const hasAIText = el.innerText === 'AI' || el.textContent === 'AI';
const hasAITitle = el.title === 'AI';
const hasAIDataAttr = el.dataset && (el.dataset.command === 'AI' || el.dataset.type === 'ai');
const isIconWithAIParent = el.tagName === 'svg' && el.parentElement &&
(el.parentElement.title === 'AI' || el.parentElement.dataset && el.parentElement.dataset.command === 'AI');
return hasAIText || hasAITitle || hasAIDataAttr || isIconWithAIParent;
});
aiButtons.forEach(btn => {
btn.style.display = 'none';
btn.style.visibility = 'hidden';
btn.style.opacity = '0';
btn.style.pointerEvents = 'none';
console.log('直接隐藏了AI按钮:', btn);
});
// 3. 尝试查找右侧工具栏并处理
const toolbars = iframeDoc.querySelectorAll('.mind-map .toolbar, .bts-container, .btnlist, .bx--toolbar');
toolbars.forEach(toolbar => {
// 查找第7个子元素(基于图片所示的位置)
const allItems = Array.from(toolbar.children);
if (allItems.length >= 7) {
// 从第6个位置(索引5)开始查找,可能会有偏差
for (let i = 5; i < Math.min(allItems.length, 8); i++) {
const item = allItems[i];
if (item && (item.title === 'AI' || item.innerText === 'AI' || item.textContent === 'AI')) {
item.style.display = 'none';
console.log('已隐藏工具栏中的AI按钮:', item);
}
}
}
});
} catch (error) {
console.error('隐藏AI按钮时出错:', error);
}
}
// 立即执行一次
hideAIButton();
// 设置MutationObserver监听DOM变化
try {
const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
const observer = new MutationObserver(() => {
hideAIButton();
});
observer.observe(iframeDoc.body, {
childList: true,
subtree: true,
attributes: true,
attributeFilter: ['class', 'style', 'data-command']
});
console.log('已设置MutationObserver监听');
} catch (error) {
console.error('设置MutationObserver失败:', error);
}
// 额外定期检查几次,确保晚加载的内容也被处理
const checkIntervals = [500, 1000, 2000, 3000, 5000];
checkIntervals.forEach(interval => {
setTimeout(hideAIButton, interval);
});
// 处理iframe加载
const iframe = document.getElementById('mindmap-frame');
const loadingContainer = document.getElementById('loading-container');
// 模拟加载进度增加
let loadTime = 0;
const loadingInterval = setInterval(() => {
loadTime += 1;
// 8秒后如果还没加载完,强制显示
if (loadTime >= 8) {
clearInterval(loadingInterval);
loadingContainer.classList.add('hidden');
iframe.classList.add('loaded');
}
}, 1000);
// iframe加载成功
iframe.onload = function() {
console.log('思维导图加载成功');
clearInterval(loadingInterval);
// 添加CSS隐藏AI按钮
try {
const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
const styleElement = iframeDoc.createElement('style');
styleElement.textContent = `
/* 隐藏所有可能的AI按钮 */
[data-command="AI"],
.bts-item[title="AI"],
div[data-type="ai"],
.toolbar-item[title="AI"],
.toolbar div[data-command="AI"],
.btnlist-item[data-command="AI"],
.btn-item[data-command="AI"],
svg[data-icon="robot"],
svg[data-type="robot"] {
display: none !important;
visibility: hidden !important;
opacity: 0 !important;
pointer-events: none !important;
width: 0 !important;
height: 0 !important;
position: absolute !important;
left: -9999px !important;
}
`;
iframeDoc.head.appendChild(styleElement);
console.log('已隐藏AI按钮');
} catch (error) {
console.error('注入CSS隐藏AI按钮失败:', error);
}
// 添加短暂延迟,确保思维导图已经渲染
setTimeout(() => {
loadingContainer.classList.add('hidden');
iframe.classList.add('loaded');
}, 500);
};
// iframe加载失败
iframe.onerror = function() {
console.error('思维导图加载失败');
clearInterval(loadingInterval);
// 显示错误信息
loadingContainer.innerHTML = `
<div class="alert alert-danger m-4" role="alert">
<h4 class="alert-heading">无法加载思维导图</h4>
<p>思维导图功能暂时不可用,请稍后再试。</p>
<hr>
<p class="mb-0">
<button class="btn btn-outline-danger" onclick="location.reload()">重试</button>
<a href="/" class="btn btn-outline-secondary ms-2">返回首页</a>
</p>
</div>
`;
};
});
</script>
</body>
</html>