Files
dsProject/dsLightRag/static/deepseek_huaxue_iframe.html

408 lines
14 KiB
HTML
Raw Normal View History

2025-08-14 15:45:08 +08:00
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>物体的浮沉条件是什么?</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Microsoft YaHei', Arial, sans-serif;
}
body {
background-color: #f5f5f5;
height: 100vh;
display: flex;
flex-direction: column;
}
.container {
display: flex;
flex: 1;
overflow: hidden;
}
.simulation-panel {
flex: 1;
border: 1px solid #ddd;
overflow: hidden;
background-color: white;
}
.chat-panel {
flex: 0 0 400px;
display: flex;
flex-direction: column;
border: 1px solid #ddd;
background-color: white;
box-shadow: -2px 0 5px rgba(0,0,0,0.1);
}
.chat-header {
padding: 15px;
background-color: #2196F3; /* 改为蓝色 */
color: white;
font-size: 18px;
font-weight: bold;
}
.chat-messages {
flex: 1;
padding: 15px;
overflow-y: auto;
}
.message {
margin-bottom: 15px;
max-width: 85%;
padding: 10px 15px;
border-radius: 18px;
word-break: break-word;
}
.user-message {
background-color: #E3F2FD; /* 改为浅蓝色 */
margin-left: auto;
border-top-right-radius: 4px;
}
.ai-message {
background-color: #E5E5EA;
border-top-left-radius: 4px;
}
.message-content {
line-height: 1.5;
}
.chat-input {
display: flex;
padding: 10px;
border-top: 1px solid #ddd;
}
.chat-input input {
flex: 1;
padding: 10px 15px;
border: 1px solid #ddd;
border-radius: 20px;
outline: none;
}
.chat-input button {
margin-left: 10px;
padding: 0 15px;
background-color: #2196F3; /* 改为蓝色 */
color: white;
border: none;
border-radius: 20px;
cursor: pointer;
font-weight: bold;
}
.chat-input button:hover {
background-color: #1976D2; /* 改为深蓝色 */
}
.loading-indicator {
display: flex;
align-items: center;
margin: 10px 0;
}
.loading-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background-color: #666;
margin: 0 4px;
animation: pulse 1.4s infinite ease-in-out both;
}
.loading-dot:nth-child(1) {
animation-delay: -0.32s;
}
.loading-dot:nth-child(2) {
animation-delay: -0.16s;
}
@keyframes pulse {
0%, 80%, 100% {
transform: scale(0);
}
40% {
transform: scale(1);
}
}
iframe {
width: 100%;
height: 100%;
border: none;
}
/* 响应式设计 */
@media (max-width: 900px) {
.container {
flex-direction: column;
}
.simulation-panel {
height: 50vh;
}
.chat-panel {
flex: 1;
width: 100%;
}
}
</style>
</head>
<body>
<div class="container">
<div class="simulation-panel">
<iframe src="deepseek_html_20250812_5fd0ec.html"></iframe>
</div>
<div class="chat-panel">
<div class="chat-header">AI 助手</div>
<div class="chat-messages" id="chat-messages">
<div class="message ai-message">
<div class="message-content">提示词:
开发一个高中化学讲解的课件,要求如下:
左侧控制面板:
标题:岩盐结构
描述NaCl晶体模型交互演示
离子半径设置:
钠离子半径0.3
氯离子半径0.45
视图控制:
显示/隐藏离子:一个复选框,用于控制是否显示离子。
显示/隐藏晶胞:一个复选框,用于控制是否显示晶胞。
显示八面体:一个按钮,用于显示八面体结构。
仅显示八面体:一个按钮,用于仅显示八面体结构。
切八分之一晶胞:一个按钮,用于显示晶胞的八分之一。
重置视图:一个按钮,用于重置视图到默认状态。
观察视角:
X轴、Y轴、Z轴三个按钮用于调整观察的三维视角。
右侧主展示区:
标题:氯化钠晶胞结构学演示
描述:高中化学交互式教学演示
氯化钠晶胞结构图:
展示了氯化钠的晶胞结构,其中绿色球代表氯离子,蓝色球代表钠离子。晶胞的边缘用红色线条连接,形成一个立方体结构。
交互提示:
提示用户可以使用鼠标拖拽旋转视角,滚轮缩放,平行投影等操作来观察晶胞结构。
这个网页的主要功能是通过交互式的方式,让用户能够观察和学习氯化钠的晶胞结构,并通过调整离子半径和视角来获得更深入的理解。</div>
</div>
</div>
<div class="chat-input">
<input type="text" id="user-input" placeholder="输入你的问题..." value="氯化钠晶胞结构是什么样的?">
<button id="send-btn">发送</button>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const chatMessages = document.getElementById('chat-messages');
const userInput = document.getElementById('user-input');
const sendBtn = document.getElementById('send-btn');
let sseConnection = null;
let isLoading = false;
// 发送消息
function sendMessage() {
const message = userInput.value.trim();
if (!message || isLoading) return;
// 添加用户消息
addMessage(message, 'user');
userInput.value = '';
// 显示加载中
showLoading();
// 关闭当前可能存在的SSE连接
if (sseConnection) {
sseConnection.close();
sseConnection = null;
}
// 发送POST请求到/api/llm
fetch('/api/llm', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ question: message })
})
.then(response => {
if (!response.ok) {
throw new Error('HTTP error! status: ' + response.status);
}
// 处理流式响应
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
let aiMessageElement = null;
let aiMessageContentElement = null;
function processChunk({ done, value }) {
if (done) {
// 处理最后一块数据
if (buffer) {
handleSSEMessage(buffer, aiMessageElement, aiMessageContentElement);
}
hideLoading();
return;
}
// 解码接收到的数据
const chunk = decoder.decode(value, { stream: true });
buffer += chunk;
// 按行分割数据
const lines = buffer.split('\n');
buffer = lines.pop(); // 保存不完整的行
// 如果还没有创建AI消息元素则创建
if (!aiMessageElement) {
aiMessageElement = document.createElement('div');
aiMessageElement.className = 'message ai-message';
aiMessageContentElement = document.createElement('div');
aiMessageContentElement.className = 'message-content';
aiMessageElement.appendChild(aiMessageContentElement);
chatMessages.appendChild(aiMessageElement);
scrollToBottom();
}
// 处理每一行
for (const line of lines) {
if (line.trim()) {
handleSSEMessage(line, aiMessageElement, aiMessageContentElement);
}
}
// 继续读取
return reader.read().then(processChunk);
}
// 开始读取响应体
return reader.read().then(processChunk);
})
.catch(error => {
console.error('发送请求失败:', error);
hideLoading();
addMessage('抱歉,请求失败: ' + error.message, 'ai');
});
}
// 处理SSE消息
function handleSSEMessage(line, aiMessageElement, aiMessageContentElement) {
// 检查是否是SSE格式 (以'data: '开头)
if (line.startsWith('data: ')) {
const data = line.substring(6).trim();
// 检查是否是结束信号
if (data === 'DONE') {
hideLoading();
return;
}
try {
// 解析JSON
const jsonData = JSON.parse(data);
// 提取content字段
if (jsonData.content) {
aiMessageContentElement.textContent += jsonData.content;
scrollToBottom();
}
} catch (error) {
console.error('解析JSON失败:', error, '数据:', data);
// 如果解析失败,尝试直接添加文本
aiMessageContentElement.textContent += data;
scrollToBottom();
}
} else if (line) {
// 如果不是标准SSE格式尝试直接解析
try {
const jsonData = JSON.parse(line);
if (jsonData.content) {
aiMessageContentElement.textContent += jsonData.content;
scrollToBottom();
}
} catch (error) {
console.error('解析非标准SSE数据失败:', error, '数据:', line);
// 如果解析失败,直接添加文本
aiMessageContentElement.textContent += line;
scrollToBottom();
}
}
}
// 添加消息到聊天框
function addMessage(message, sender) {
const messageElement = document.createElement('div');
messageElement.className = `message ${sender}-message`;
const contentElement = document.createElement('div');
contentElement.className = 'message-content';
contentElement.textContent = message;
messageElement.appendChild(contentElement);
chatMessages.appendChild(messageElement);
scrollToBottom();
}
// 显示加载中
function showLoading() {
if (isLoading) return;
isLoading = true;
const loadingElement = document.createElement('div');
loadingElement.className = 'loading-indicator';
loadingElement.id = 'loading-indicator';
for (let i = 0; i < 3; i++) {
const dot = document.createElement('div');
dot.className = 'loading-dot';
loadingElement.appendChild(dot);
}
chatMessages.appendChild(loadingElement);
scrollToBottom();
}
// 隐藏加载中
function hideLoading() {
if (!isLoading) return;
isLoading = false;
const loadingElement = document.getElementById('loading-indicator');
if (loadingElement) {
loadingElement.remove();
}
}
// 滚动到底部
function scrollToBottom() {
chatMessages.scrollTop = chatMessages.scrollHeight;
}
// 发送按钮点击事件
sendBtn.addEventListener('click', sendMessage);
// 输入框回车事件
userInput.addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
sendMessage();
}
});
});
</script>
</body>
</html>