AI聊天窗口

发布于 2025-04-25 浏览 122 人次

创建一个复杂的、响应式的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>

功能特点:

  1. 左右分栏布局

    • 左侧为功能菜单栏,右侧为主聊天区域
    • 响应式设计,在小屏幕下侧边栏可折叠
  2. 防冲突机制

    • 消息队列系统防止快速连续发送消息导致的冲突
    • 冲突提示模态框,当用户尝试在AI响应期间发送消息时显示
  3. 动画效果

    • 消息淡入动画
    • 打字指示器动画
    • 平滑的过渡效果
    • 侧边栏滑动动画
  4. 用户体验优化

    • 自动调整高度的输入框
    • 自动滚动到底部
    • 禁用发送按钮当输入为空时
    • 加载状态指示器
  5. 自适应设计

    • 完美适配不同屏幕尺寸
    • 移动设备优化布局
  6. 视觉设计

    • 柔和的配色方案
    • 清晰的字体和可读性
    • 现代化的界面元素

这个实现包含了完整的HTML结构、CSS样式和JavaScript交互逻辑,可以直接使用或根据需要进行进一步定制。