创建一个复杂的、响应式的AI聊天窗口布局,采用左右分栏设计,包含动画效果、自适应布局和防冲突机制。以下是完整的HTML、CSS和JavaScript实现:
<!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>
:root {
--primary-color: #6c5ce7;
--secondary-color: #a29bfe;
--text-color: #2d3436;
--light-bg: #f9f9f9;
--dark-bg: #dfe6e9;
--user-bubble: #74b9ff;
--ai-bubble: #55efc4;
--shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
--transition: all 0.3s ease;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background-color: var(--light-bg);
color: var(--text-color);
line-height: 1.6;
overflow-x: hidden;
}
.app-container {
display: flex;
min-height: 100vh;
position: relative;
overflow: hidden;
}
/* 左侧边栏样式 */
.sidebar {
width: 300px;
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
color: white;
padding: 2rem 1.5rem;
position: relative;
z-index: 10;
box-shadow: var(--shadow);
transform: translateX(0);
transition: var(--transition);
overflow-y: auto;
}
.sidebar-header {
display: flex;
align-items: center;
margin-bottom: 2rem;
animation: fadeIn 0.5s ease forwards;
}
.sidebar-header h1 {
font-size: 1.5rem;
font-weight: 600;
margin-left: 0.5rem;
}
.sidebar-menu {
margin-top: 2rem;
}
.menu-item {
display: flex;
align-items: center;
padding: 0.8rem 1rem;
margin-bottom: 0.5rem;
border-radius: 8px;
cursor: pointer;
transition: var(--transition);
opacity: 0;
animation: fadeIn 0.5s ease forwards;
animation-delay: calc(var(--delay) * 0.1s);
}
.menu-item:hover {
background-color: rgba(255, 255, 255, 0.1);
transform: translateX(5px);
}
.menu-item.active {
background-color: rgba(255, 255, 255, 0.2);
}
.menu-item i {
margin-right: 0.8rem;
font-size: 1.2rem;
}
.sidebar-footer {
position: absolute;
bottom: 2rem;
left: 1.5rem;
right: 1.5rem;
opacity: 0;
animation: fadeIn 0.5s ease forwards;
animation-delay: 0.4s;
}
/* 主聊天区域样式 */
.chat-container {
flex: 1;
display: flex;
flex-direction: column;
background-color: var(--light-bg);
position: relative;
}
.chat-header {
padding: 1.5rem;
background-color: white;
box-shadow: var(--shadow);
display: flex;
align-items: center;
justify-content: space-between;
z-index: 5;
}
.chat-title {
font-size: 1.3rem;
font-weight: 600;
}
.chat-actions {
display: flex;
gap: 1rem;
}
.chat-actions button {
background: none;
border: none;
cursor: pointer;
font-size: 1.2rem;
color: var(--text-color);
transition: var(--transition);
}
.chat-actions button:hover {
color: var(--primary-color);
transform: scale(1.1);
}
.messages-container {
flex: 1;
padding: 1.5rem;
overflow-y: auto;
scroll-behavior: smooth;
background-color: var(--dark-bg);
}
.message {
display: flex;
margin-bottom: 1.5rem;
animation: fadeInUp 0.3s ease forwards;
opacity: 0;
}
.message-avatar {
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 1rem;
flex-shrink: 0;
background-color: white;
box-shadow: var(--shadow);
}
.message-content {
max-width: 70%;
}
.message-bubble {
padding: 0.8rem 1.2rem;
border-radius: 18px;
box-shadow: var(--shadow);
position: relative;
word-wrap: break-word;
}
.user-message {
justify-content: flex-end;
}
.user-message .message-bubble {
background-color: var(--user-bubble);
color: white;
border-bottom-right-radius: 4px;
}
.ai-message {
justify-content: flex-start;
}
.ai-message .message-bubble {
background-color: var(--ai-bubble);
color: var(--text-color);
border-bottom-left-radius: 4px;
}
.message-time {
font-size: 0.75rem;
color: rgba(0, 0, 0, 0.5);
margin-top: 0.3rem;
text-align: right;
}
.ai-message .message-time {
text-align: left;
}
/* 输入区域样式 */
.input-container {
padding: 1.5rem;
background-color: white;
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05);
position: relative;
}
.input-wrapper {
display: flex;
align-items: center;
background-color: var(--light-bg);
border-radius: 24px;
padding: 0.5rem 1rem;
transition: var(--transition);
border: 1px solid transparent;
}
.input-wrapper:focus-within {
border-color: var(--primary-color);
box-shadow: 0 0 0 2px rgba(108, 92, 231, 0.2);
}
.chat-input {
flex: 1;
border: none;
background: none;
padding: 0.8rem 0;
font-size: 1rem;
outline: none;
resize: none;
max-height: 120px;
min-height: 40px;
}
.send-button {
background-color: var(--primary-color);
color: white;
border: none;
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: var(--transition);
margin-left: 0.5rem;
}
.send-button:hover {
background-color: #5649c0;
transform: scale(1.05);
}
.send-button:disabled {
background-color: #b2b2b2;
cursor: not-allowed;
transform: none;
}
.typing-indicator {
display: flex;
align-items: center;
margin-left: 1rem;
opacity: 0;
transition: var(--transition);
}
.typing-indicator.active {
opacity: 1;
}
.typing-dot {
width: 8px;
height: 8px;
background-color: var(--primary-color);
border-radius: 50%;
margin-right: 4px;
animation: typingAnimation 1.4s infinite ease-in-out;
}
.typing-dot:nth-child(1) {
animation-delay: 0s;
}
.typing-dot:nth-child(2) {
animation-delay: 0.2s;
}
.typing-dot:nth-child(3) {
animation-delay: 0.4s;
}
/* 防冲突区域样式 */
.conflict-guard {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 100;
opacity: 0;
pointer-events: none;
transition: var(--transition);
}
.conflict-guard.active {
opacity: 1;
pointer-events: all;
}
.conflict-modal {
background-color: white;
padding: 2rem;
border-radius: 12px;
max-width: 400px;
width: 90%;
text-align: center;
transform: translateY(20px);
transition: var(--transition);
}
.conflict-guard.active .conflict-modal {
transform: translateY(0);
}
.conflict-modal h3 {
margin-bottom: 1rem;
color: var(--text-color);
}
.conflict-modal p {
margin-bottom: 1.5rem;
color: #666;
}
.conflict-actions {
display: flex;
justify-content: center;
gap: 1rem;
}
.conflict-button {
padding: 0.6rem 1.2rem;
border-radius: 6px;
border: none;
cursor: pointer;
transition: var(--transition);
font-weight: 500;
}
.conflict-button.primary {
background-color: var(--primary-color);
color: white;
}
.conflict-button.secondary {
background-color: var(--light-bg);
color: var(--text-color);
}
.conflict-button:hover {
transform: translateY(-2px);
box-shadow: var(--shadow);
}
/* 响应式设计 */
@media (max-width: 992px) {
.sidebar {
position: fixed;
left: 0;
top: 0;
bottom: 0;
transform: translateX(-100%);
}
.sidebar.active {
transform: translateX(0);
}
.chat-container {
margin-left: 0;
}
}
@media (max-width: 576px) {
.message-content {
max-width: 85%;
}
.chat-header {
padding: 1rem;
}
.messages-container {
padding: 1rem;
}
.input-container {
padding: 1rem;
}
}
/* 动画 */
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes typingAnimation {
0%, 60%, 100% {
transform: translateY(0);
}
30% {
transform: translateY(-5px);
}
}
/* 加载动画 */
.loader {
display: none;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(255, 255, 255, 0.8);
z-index: 1000;
justify-content: center;
align-items: center;
flex-direction: column;
}
.loader.active {
display: flex;
}
.loader-spinner {
width: 50px;
height: 50px;
border: 5px solid var(--light-bg);
border-top: 5px solid var(--primary-color);
border-radius: 50%;
animation: spin 1s linear infinite;
margin-bottom: 1rem;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
</head>
<body>
<div class="app-container">
<!-- 左侧边栏 -->
<div class="sidebar">
<div class="sidebar-header">
<i class="fas fa-robot"></i>
<h1>AI助手</h1>
</div>
<div class="sidebar-menu">
<div class="menu-item active" style="--delay: 1">
<i class="fas fa-comment-dots"></i>
<span>聊天</span>
</div>
<div class="menu-item" style="--delay: 2">
<i class="fas fa-cog"></i>
<span>设置</span>
</div>
<div class="menu-item" style="--delay: 3">
<i class="fas fa-history"></i>
<span>历史记录</span>
</div>
<div class="menu-item" style="--delay: 4">
<i class="fas fa-star"></i>
<span>收藏</span>
</div>
<div class="menu-item" style="--delay: 5">
<i class="fas fa-question-circle"></i>
<span>帮助</span>
</div>
</div>
<div class="sidebar-footer">
<div class="menu-item" style="--delay: 6">
<i class="fas fa-user"></i>
<span>个人中心</span>
</div>
</div>
</div>
<!-- 主聊天区域 -->
<div class="chat-container">
<div class="chat-header">
<div class="chat-title">AI聊天助手</div>
<div class="chat-actions">
<button id="toggle-sidebar"><i class="fas fa-bars"></i></button>
<button><i class="fas fa-search"></i></button>
<button><i class="fas fa-ellipsis-v"></i></button>
</div>
</div>
<div class="messages-container" id="messages-container">
<!-- 消息将在这里动态添加 -->
</div>
<div class="input-container">
<div class="input-wrapper">
<textarea class="chat-input" id="chat-input" placeholder="输入消息..." rows="1"></textarea>
<button class="send-button" id="send-button" disabled>
<i class="fas fa-paper-plane"></i>
</button>
</div>
<div class="typing-indicator" id="typing-indicator">
<div class="typing-dot"></div>
<div class="typing-dot"></div>
<div class="typing-dot"></div>
</div>
</div>
</div>
<!-- 防冲突模态框 -->
<div class="conflict-guard" id="conflict-guard">
<div class="conflict-modal">
<h3>操作冲突</h3>
<p>系统正在处理您的上一个请求,请稍后再试。</p>
<div class="conflict-actions">
<button class="conflict-button primary" id="conflict-ok">确定</button>
</div>
</div>
</div>
<!-- 加载动画 -->
<div class="loader" id="loader">
<div class="loader-spinner"></div>
<div>正在处理您的请求...</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// DOM元素
const chatInput = document.getElementById('chat-input');
const sendButton = document.getElementById('send-button');
const messagesContainer = document.getElementById('messages-container');
const toggleSidebar = document.getElementById('toggle-sidebar');
const sidebar = document.querySelector('.sidebar');
const typingIndicator = document.getElementById('typing-indicator');
const conflictGuard = document.getElementById('conflict-guard');
const conflictOk = document.getElementById('conflict-ok');
const loader = document.getElementById('loader');
// 状态变量
let isAIResponding = false;
let isProcessing = false;
let messageQueue = [];
// 初始化
initChat();
// 事件监听
chatInput.addEventListener('input', handleInputChange);
chatInput.addEventListener('keydown', handleKeyDown);
sendButton.addEventListener('click', sendMessage);
toggleSidebar.addEventListener('click', toggleSidebarMenu);
conflictOk.addEventListener('click', hideConflictGuard);
// 初始化聊天
function initChat() {
// 添加欢迎消息
addMessage('ai', '您好!我是AI助手,有什么可以帮您的吗?');
// 模拟一些历史消息
setTimeout(() => {
addMessage('user', '你好!');
setTimeout(() => {
addMessage('ai', '很高兴见到您!今天有什么我可以为您效劳的吗?');
}, 800);
}, 1000);
}
// 处理输入变化
function handleInputChange() {
// 自动调整输入框高度
this.style.height = 'auto';
this.style.height = (this.scrollHeight) + 'px';
// 启用/禁用发送按钮
sendButton.disabled = this.value.trim() === '';
}
// 处理键盘事件
function handleKeyDown(e) {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
if (!sendButton.disabled) {
sendMessage();
}
}
}
// 发送消息
function sendMessage() {
const message = chatInput.value.trim();
if (message === '' || isProcessing) {
if (isProcessing) {
showConflictGuard();
}
return;
}
// 添加到消息队列
messageQueue.push(message);
// 处理队列中的消息
processMessageQueue();
// 清空输入框
chatInput.value = '';
chatInput.style.height = 'auto';
sendButton.disabled = true;
}
// 处理消息队列
function processMessageQueue() {
if (isProcessing || messageQueue.length === 0) return;
isProcessing = true;
const message = messageQueue.shift();
// 添加用户消息
addMessage('user', message);
// 显示加载状态
showTypingIndicator();
// 模拟AI响应延迟
setTimeout(() => {
// 生成AI响应
const response = generateAIResponse(message);
// 隐藏输入指示器
hideTypingIndicator();
// 添加AI响应
addMessage('ai', response);
// 处理完成
isProcessing = false;
// 处理队列中的下一条消息
if (messageQueue.length > 0) {
setTimeout(processMessageQueue, 500);
}
}, 1500 + Math.random() * 2000);
}
// 添加消息到聊天界面
function addMessage(sender, text) {
const messageElement = document.createElement('div');
messageElement.className = `message ${sender}-message`;
const now = new Date();
const timeString = now.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
messageElement.innerHTML = `
<div class="message-avatar">
<i class="fas ${sender === 'user' ? 'fa-user' : 'fa-robot'}"></i>
</div>
<div class="message-content">
<div class="message-bubble">${text}</div>
<div class="message-time">${timeString}</div>
</div>
`;
messagesContainer.appendChild(messageElement);
scrollToBottom();
// 触发动画
setTimeout(() => {
messageElement.style.opacity = '1';
}, 10);
}
// 生成AI响应
function generateAIResponse(message) {
// 这里可以替换为实际的AI API调用
const responses = [
"我理解您的问题。让我详细解释一下...",
"这是一个很好的问题!根据我的分析...",
"感谢您的提问。关于这个问题...",
"我明白您的意思了。我的建议是...",
"这个问题很有趣!让我为您解答...",
"根据我的知识库,我可以告诉您...",
"我注意到您对这个话题感兴趣。详细来说...",
"让我为您提供一些有用的信息..."
];
// 简单模拟上下文感知
if (message.toLowerCase().includes('你好') || message.toLowerCase().includes('hi')) {
return "您好!很高兴和您聊天。今天有什么我可以帮助您的吗?";
}
if (message.toLowerCase().includes('谢谢')) {
return "不客气!如果还有其他问题,随时告诉我。";
}
return responses[Math.floor(Math.random() * responses.length)];
}
// 显示输入指示器
function showTypingIndicator() {
typingIndicator.classList.add('active');
scrollToBottom();
}
// 隐藏输入指示器
function hideTypingIndicator() {
typingIndicator.classList.remove('active');
}
// 滚动到底部
function scrollToBottom() {
messagesContainer.scrollTop = messagesContainer.scrollHeight;
}
// 切换侧边栏
function toggleSidebarMenu() {
sidebar.classList.toggle('active');
}
// 显示冲突防护
function showConflictGuard() {
conflictGuard.classList.add('active');
}
// 隐藏冲突防护
function hideConflictGuard() {
conflictGuard.classList.remove('active');
}
// 显示加载动画
function showLoader() {
loader.classList.add('active');
}
// 隐藏加载动画
function hideLoader() {
loader.classList.remove('active');
}
// 模拟网络请求
function simulateNetworkRequest() {
showLoader();
setTimeout(() => {
hideLoader();
}, 2000);
}
});
</script>
</body>
</html>功能特点:
左右分栏布局:
- 左侧为功能菜单栏,右侧为主聊天区域
- 响应式设计,在小屏幕下侧边栏可折叠
防冲突机制:
- 消息队列系统防止快速连续发送消息导致的冲突
- 冲突提示模态框,当用户尝试在AI响应期间发送消息时显示
动画效果:
- 消息淡入动画
- 打字指示器动画
- 平滑的过渡效果
- 侧边栏滑动动画
用户体验优化:
- 自动调整高度的输入框
- 自动滚动到底部
- 禁用发送按钮当输入为空时
- 加载状态指示器
自适应设计:
- 完美适配不同屏幕尺寸
- 移动设备优化布局
视觉设计:
- 柔和的配色方案
- 清晰的字体和可读性
- 现代化的界面元素
这个实现包含了完整的HTML结构、CSS样式和JavaScript交互逻辑,可以直接使用或根据需要进行进一步定制。
需支付 5元 阅读剩余内容