<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AI聊天助手</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', 'Microsoft YaHei', sans-serif;
}
body {
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
}
.chat-container {
width: 100%;
max-width: 800px;
height: 80vh;
background-color: white;
border-radius: 16px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: column;
overflow: hidden;
}
.chat-header {
background: linear-gradient(90deg, #4e54c8, #8f94fb);
color: white;
padding: 20px;
text-align: center;
font-size: 1.5rem;
font-weight: 600;
}
.chat-messages {
flex: 1;
padding: 20px;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 15px;
}
.message {
max-width: 80%;
padding: 12px 18px;
border-radius: 18px;
line-height: 1.5;
position: relative;
animation: fadeIn 0.3s ease;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.user-message {
align-self: flex-end;
background-color: #4e54c8;
color: white;
border-bottom-right-radius: 4px;
}
.ai-message {
align-self: flex-start;
background-color: #f0f2f5;
color: #333;
border-bottom-left-radius: 4px;
}
.typing-indicator {
display: inline-block;
padding: 12px 18px;
background-color: #f0f2f5;
border-radius: 18px;
border-bottom-left-radius: 4px;
align-self: flex-start;
}
.typing-dots {
display: flex;
gap: 4px;
}
.typing-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background-color: #999;
animation: typingAnimation 1.4s infinite ease-in-out;
}
.typing-dot:nth-child(1) { animation-delay: -0.32s; }
.typing-dot:nth-child(2) { animation-delay: -0.16s; }
@keyframes typingAnimation {
0%, 80%, 100% { transform: scale(0.8); opacity: 0.5; }
40% { transform: scale(1); opacity: 1; }
}
.chat-input-container {
padding: 20px;
border-top: 1px solid #eee;
display: flex;
gap: 10px;
}
.chat-input {
flex: 1;
padding: 12px 18px;
border: 1px solid #ddd;
border-radius: 24px;
outline: none;
font-size: 1rem;
transition: border-color 0.3s;
}
.chat-input:focus {
border-color: #4e54c8;
}
.send-button {
background: linear-gradient(90deg, #4e54c8, #8f94fb);
color: white;
border: none;
border-radius: 50%;
width: 50px;
height: 50px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
}
.send-button:hover {
transform: scale(1.05);
box-shadow: 0 4px 12px rgba(78, 84, 200, 0.3);
}
.send-button:active {
transform: scale(0.98);
}
.send-button svg {
width: 24px;
height: 24px;
}
.error-message {
background-color: #ffebee;
color: #c62828;
padding: 12px 18px;
border-radius: 8px;
margin: 10px 20px;
text-align: center;
}
/* 滚动条样式 */
.chat-messages::-webkit-scrollbar {
width: 6px;
}
.chat-messages::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 10px;
}
.chat-messages::-webkit-scrollbar-thumb {
background: #c1c1c1;
border-radius: 10px;
}
.chat-messages::-webkit-scrollbar-thumb:hover {
background: #a8a8a8;
}
@media (max-width: 600px) {
.chat-container {
height: 90vh;
}
.message {
max-width: 90%;
}
}
</style>
</head>
<body>
<div class="chat-container">
<div class="chat-header">
AI聊天助手
</div>
<div class="chat-messages" id="chatMessages">
<div class="message ai-message">
您好!我是AI助手,有什么可以帮您的吗?
</div>
</div>
<div class="chat-input-container">
<input type="text" class="chat-input" id="messageInput" placeholder="输入您的问题..." autocomplete="off">
<button class="send-button" id="sendButton">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M22 2L11 13" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M22 2L15 22L11 13L2 9L22 2Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</button>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const chatMessages = document.getElementById('chatMessages');
const messageInput = document.getElementById('messageInput');
const sendButton = document.getElementById('sendButton');
// 添加消息到聊天区域
function addMessage(content, isUser = false) {
const messageDiv = document.createElement('div');
messageDiv.className = `message ${isUser ? 'user-message' : 'ai-message'}`;
messageDiv.textContent = content;
chatMessages.appendChild(messageDiv);
scrollToBottom();
return messageDiv;
}
// 显示正在输入指示器
function showTypingIndicator() {
const indicatorDiv = document.createElement('div');
indicatorDiv.className = 'typing-indicator';
indicatorDiv.id = 'typingIndicator';
const dotsDiv = document.createElement('div');
dotsDiv.className = 'typing-dots';
for (let i = 0; i < 3; i++) {
const dot = document.createElement('div');
dot.className = 'typing-dot';
dotsDiv.appendChild(dot);
}
indicatorDiv.appendChild(dotsDiv);
chatMessages.appendChild(indicatorDiv);
scrollToBottom();
return indicatorDiv;
}
// 移除正在输入指示器
function removeTypingIndicator() {
const indicator = document.getElementById('typingIndicator');
if (indicator) {
indicator.remove();
}
}
// 显示错误消息
function showError(message) {
const errorDiv = document.createElement('div');
errorDiv.className = 'error-message';
errorDiv.textContent = message;
chatMessages.appendChild(errorDiv);
scrollToBottom();
// 5秒后自动移除错误消息
setTimeout(() => {
errorDiv.remove();
}, 5000);
}
// 滚动到底部
function scrollToBottom() {
chatMessages.scrollTop = chatMessages.scrollHeight;
}
// 发送消息到后端
async function sendMessage() {
const message = messageInput.value.trim();
if (!message) return;
// 添加用户消息
addMessage(message, true);
// 清空输入框
messageInput.value = '';
// 显示正在输入指示器
const typingIndicator = showTypingIndicator();
try {
// 发送POST请求到后端
const response = await fetch('http://10.161.107.96:8080/ai/chat', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
message: message
})
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
// 移除正在输入指示器
removeTypingIndicator();
// 处理流式响应
const reader = response.body.getReader();
const decoder = new TextDecoder();
// 创建AI消息容器
const aiMessageDiv = document.createElement('div');
aiMessageDiv.className = 'message ai-message';
chatMessages.appendChild(aiMessageDiv);
let aiMessageContent = '';
while (true) {
const { done, value } = await reader.read();
if (done) {
break;
}
// 解码并添加内容
const chunk = decoder.decode(value, { stream: true });
aiMessageContent += chunk;
aiMessageDiv.textContent = aiMessageContent;
// 滚动到底部
scrollToBottom();
}
} catch (error) {
console.error('Error:', error);
removeTypingIndicator();
showError('抱歉,发生错误:' + error.message);
}
}
// 发送按钮点击事件
sendButton.addEventListener('click', sendMessage);
// 输入框回车事件
messageInput.addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
sendMessage();
}
});
// 初始聚焦到输入框
messageInput.focus();
});
</script>
</body>
</html>
02-06
1452
1452
03-27

被折叠的 条评论
为什么被折叠?



