弹来弹去跑马灯!

AI聊天对话界面的HTML代码。

AI聊天对话界面的HTML代码。它实现了一个功能完整的聊天机器人,您可以直接在浏览器中运行,无需任何外部文件。

 

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
    <title>AI 智能助手 | 对话聊天界面</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            background: linear-gradient(145deg, #e0eafc 0%, #cfdef3 100%);
            font-family: 'Segoe UI', 'Inter', system-ui, -apple-system, 'Roboto', 'Helvetica Neue', sans-serif;
            height: 100vh;
            display: flex;
            align-items: center;
            justify-content: center;
            padding: 16px;
        }

        /* 聊天容器卡片 */
        .chat-container {
            width: 100%;
            max-width: 800px;
            height: 90vh;
            max-height: 85vh;
            background: rgba(255, 255, 255, 0.96);
            border-radius: 32px;
            box-shadow: 0 25px 45px -12px rgba(0, 0, 0, 0.35), 0 2px 8px rgba(0, 0, 0, 0.05);
            backdrop-filter: blur(0px);
            display: flex;
            flex-direction: column;
            overflow: hidden;
            transition: all 0.2s ease;
        }

        /* 头部 */
        .chat-header {
            background: #1e2b3c;
            color: white;
            padding: 20px 24px;
            display: flex;
            align-items: center;
            gap: 14px;
            border-bottom: 1px solid rgba(255, 255, 255, 0.15);
        }

        .ai-avatar {
            background: #2c7da0;
            width: 46px;
            height: 46px;
            border-radius: 28px;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 26px;
            font-weight: 500;
            box-shadow: 0 4px 10px rgba(0,0,0,0.2);
        }

        .header-text {
            flex: 1;
        }

        .header-text h1 {
            font-size: 1.45rem;
            font-weight: 600;
            letter-spacing: -0.3px;
        }

        .header-text p {
            font-size: 0.8rem;
            opacity: 0.8;
            margin-top: 4px;
        }

        .status-badge {
            background: #2c7da0;
            padding: 6px 12px;
            border-radius: 40px;
            font-size: 0.7rem;
            font-weight: 500;
            background: #1f5068;
        }

        /* 消息区域 */
        .messages-area {
            flex: 1;
            overflow-y: auto;
            padding: 20px 18px;
            display: flex;
            flex-direction: column;
            gap: 18px;
            background: #f9fafc;
            scroll-behavior: smooth;
        }

        /* 自定义滚动条 */
        .messages-area::-webkit-scrollbar {
            width: 5px;
        }
        .messages-area::-webkit-scrollbar-track {
            background: #e9ecef;
            border-radius: 4px;
        }
        .messages-area::-webkit-scrollbar-thumb {
            background: #b9c4d0;
            border-radius: 4px;
        }

        /* 消息气泡 */
        .message {
            display: flex;
            gap: 12px;
            max-width: 85%;
            animation: fadeSlideUp 0.25s ease-out;
        }

        .message.user {
            align-self: flex-end;
            flex-direction: row-reverse;
        }

        .message.ai {
            align-self: flex-start;
        }

        .avatar-icon {
            width: 36px;
            height: 36px;
            background: #eef2f7;
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 20px;
            flex-shrink: 0;
            box-shadow: 0 1px 3px rgba(0,0,0,0.1);
        }

        .user .avatar-icon {
            background: #1e2b3c;
            color: white;
        }

        .ai .avatar-icon {
            background: #2c7da0;
            color: white;
        }

        .bubble {
            background: white;
            padding: 12px 18px;
            border-radius: 24px;
            box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
            font-size: 0.95rem;
            line-height: 1.45;
            color: #1e2b3c;
            word-break: break-word;
            white-space: pre-wrap;
        }

        .user .bubble {
            background: #1e2b3c;
            color: white;
            border-bottom-right-radius: 6px;
        }

        .ai .bubble {
            background: white;
            border-bottom-left-radius: 6px;
            border: 1px solid #e2e8f0;
        }

        /* 打字指示器 */
        .typing-indicator {
            display: flex;
            align-items: center;
            gap: 6px;
            padding: 10px 16px;
            background: white;
            border-radius: 24px;
            width: fit-content;
            border: 1px solid #e2e8f0;
            box-shadow: 0 1px 3px rgba(0,0,0,0.05);
        }
        .typing-indicator span {
            width: 8px;
            height: 8px;
            background: #90a4bc;
            border-radius: 50%;
            display: inline-block;
            animation: bounce 1.4s infinite ease-in-out both;
        }
        .typing-indicator span:nth-child(1) { animation-delay: -0.32s; }
        .typing-indicator span:nth-child(2) { animation-delay: -0.16s; }

        @keyframes bounce {
            0%, 80%, 100% { transform: scale(0.6); opacity: 0.5; }
            40% { transform: scale(1); opacity: 1; }
        }

        /* 输入区域 */
        .input-area {
            background: white;
            padding: 14px 20px 20px;
            border-top: 1px solid #eef2f8;
            display: flex;
            gap: 12px;
            align-items: flex-end;
        }

        .input-wrapper {
            flex: 1;
            background: #f1f5f9;
            border-radius: 32px;
            padding: 8px 18px;
            display: flex;
            align-items: center;
            gap: 10px;
            border: 1px solid #e2edf2;
            transition: all 0.2s;
        }
        .input-wrapper:focus-within {
            border-color: #2c7da0;
            background: white;
            box-shadow: 0 0 0 2px rgba(44,125,160,0.2);
        }

        #messageInput {
            flex: 1;
            border: none;
            background: transparent;
            padding: 10px 0;
            font-size: 0.95rem;
            outline: none;
            resize: none;
            font-family: inherit;
            max-height: 120px;
            color: #1e2b3c;
        }

        #messageInput::placeholder {
            color: #8ca3b9;
            font-weight: 400;
        }

        .send-btn {
            background: #2c7da0;
            border: none;
            width: 42px;
            height: 42px;
            border-radius: 42px;
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: pointer;
            transition: 0.2s;
            color: white;
            font-size: 20px;
            box-shadow: 0 2px 6px rgba(0,0,0,0.1);
        }

        .send-btn:hover {
            background: #1f5e7a;
            transform: scale(0.96);
        }

        .send-btn:active {
            transform: scale(0.92);
        }

        /* 清空按钮 优雅小按钮 */
        .action-bar {
            display: flex;
            justify-content: flex-end;
            padding: 6px 20px 0;
            background: #f9fafc;
        }
        .clear-btn {
            background: none;
            border: none;
            font-size: 0.7rem;
            color: #7f8fa4;
            cursor: pointer;
            padding: 6px 12px;
            border-radius: 40px;
            transition: 0.2s;
            display: flex;
            align-items: center;
            gap: 6px;
        }
        .clear-btn:hover {
            background: #eef2fa;
            color: #1e2b3c;
        }

        /* 入场动画 */
        @keyframes fadeSlideUp {
            from {
                opacity: 0;
                transform: translateY(12px);
            }
            to {
                opacity: 1;
                transform: translateY(0);
            }
        }

        /* 响应式微调 */
        @media (max-width: 550px) {
            .message {
                max-width: 92%;
            }
            .chat-container {
                height: 92vh;
                border-radius: 26px;
            }
            .bubble {
                font-size: 0.88rem;
                padding: 10px 14px;
            }
        }
    </style>
</head>
<body>

<div class="chat-container">
    <div class="chat-header">
        <div class="ai-avatar">✨</div>
        <div class="header-text">
            <h1>AI 智慧引擎</h1>
            <p>随时为你解答 · 灵感伙伴</p>
        </div>
        <div class="status-badge" id="statusBadge">在线</div>
    </div>

    <div class="action-bar">
        <button class="clear-btn" id="clearChatBtn" aria-label="清空对话">🗑️ 清除记录</button>
    </div>

    <div class="messages-area" id="messagesArea">
        <!-- 消息动态渲染位置 -->
        <div class="message ai">
            <div class="avatar-icon">🤖</div>
            <div class="bubble">
                嗨!我是你的AI助手 🌟<br> 可以问我任何问题,编程、写作、生活建议,或是随便聊聊天~
            </div>
        </div>
    </div>

    <div class="input-area">
        <div class="input-wrapper">
            <textarea id="messageInput" placeholder="输入消息..." rows="1" style="overflow: hidden;"></textarea>
        </div>
        <button class="send-btn" id="sendBtn">➤</button>
    </div>
</div>

<script>
    // ---------- DOM 元素 ----------
    const messagesArea = document.getElementById('messagesArea');
    const messageInput = document.getElementById('messageInput');
    const sendBtn = document.getElementById('sendBtn');
    const clearBtn = document.getElementById('clearChatBtn');
    const statusBadge = document.getElementById('statusBadge');

    // ---------- 状态管理 ----------
    let isWaitingForAI = false;      // 是否正在等待AI回复(防止重复发送)
    let currentTypingElement = null; // 临时占位指示器元素

    // 本地存储Key (用于保留对话历史,提升体验)
    const STORAGE_KEY = 'ai_chat_messages';

    // 初始加载消息历史 (最多保留最近50条,并且需要数据结构符合渲染格式)
    let conversationHistory = [];     // 存储 { role: 'user' 或 'assistant', content: string }

    // 辅助:从 localstorage 读取并初始化
    function loadMessagesFromStorage() {
        const stored = localStorage.getItem(STORAGE_KEY);
        if (stored) {
            try {
                const arr = JSON.parse(stored);
                if (Array.isArray(arr) && arr.length) {
                    conversationHistory = arr.slice(-50); // 限制长度,避免过大
                    // 重新渲染界面
                    renderMessagesFromHistory();
                    return;
                }
            } catch(e) { console.warn(e); }
        }
        // 默认欢迎消息 (如果没有储存)
        if (conversationHistory.length === 0) {
            conversationHistory = [
                { role: 'assistant', content: '嗨!我是你的AI助手 🌟\n可以问我任何问题,编程、写作、生活建议,或是随便聊聊天~' }
            ];
            saveToLocalStorage();
            renderMessagesFromHistory();
        } else {
            renderMessagesFromHistory();
        }
    }

    // 保存到 localStorage
    function saveToLocalStorage() {
        // 只保留最后100条,避免溢出
        const toStore = conversationHistory.slice(-100);
        localStorage.setItem(STORAGE_KEY, JSON.stringify(toStore));
    }

    // 完全根据 conversationHistory 重新绘制消息区域 (保留滚动到底部)
    function renderMessagesFromHistory() {
        if (!messagesArea) return;
        // 清空区域,但是保留消息容器结构
        messagesArea.innerHTML = '';
        for (let msg of conversationHistory) {
            const messageDiv = createMessageElement(msg.role, msg.content);
            messagesArea.appendChild(messageDiv);
        }
        scrollToBottom();
    }

    // 创建单条消息DOM元素 (纯展示)
    function createMessageElement(role, content) {
        const messageDiv = document.createElement('div');
        messageDiv.className = `message ${role === 'user' ? 'user' : 'ai'}`;
        
        const avatarDiv = document.createElement('div');
        avatarDiv.className = 'avatar-icon';
        avatarDiv.innerText = role === 'user' ? '👤' : '🤖';
        
        const bubbleDiv = document.createElement('div');
        bubbleDiv.className = 'bubble';
        // 支持换行文本保留格式
        bubbleDiv.innerText = content;
        // 简单支持链接识别? 暂不过多高级渲染但保留文本可读性
        
        messageDiv.appendChild(avatarDiv);
        messageDiv.appendChild(bubbleDiv);
        return messageDiv;
    }

    // 滚动到底部 (平滑)
    function scrollToBottom() {
        if (messagesArea) {
            messagesArea.scrollTo({
                top: messagesArea.scrollHeight,
                behavior: 'smooth'
            });
        }
    }

    // 显示 "AI正在输入..." 的指示器 (在消息区最后)
    function showTypingIndicator() {
        if (currentTypingElement) return; // 已存在则不重复添加
        const typingWrapper = document.createElement('div');
        typingWrapper.className = 'message ai';
        typingWrapper.id = 'typingIndicatorWrapper';
        const avatarIcon = document.createElement('div');
        avatarIcon.className = 'avatar-icon';
        avatarIcon.innerText = '🤖';
        const indicatorDiv = document.createElement('div');
        indicatorDiv.className = 'typing-indicator';
        indicatorDiv.innerHTML = '<span></span><span></span><span></span>';
        typingWrapper.appendChild(avatarIcon);
        typingWrapper.appendChild(indicatorDiv);
        messagesArea.appendChild(typingWrapper);
        currentTypingElement = typingWrapper;
        scrollToBottom();
    }

    function removeTypingIndicator() {
        if (currentTypingElement && currentTypingElement.parentNode) {
            currentTypingElement.remove();
            currentTypingElement = null;
        }
    }

    // 核心:添加新消息到界面和历史,并持久化
    function addMessageToChat(role, content, skipStorage = false) {
        // 添加至历史数组
        if (!skipStorage) {
            conversationHistory.push({ role, content });
            saveToLocalStorage();
        }
        // 渲染该条消息(直接追加,避免重绘全量,提升性能)
        const messageElem = createMessageElement(role, content);
        messagesArea.appendChild(messageElem);
        scrollToBottom();
    }

    // 模拟AI 回复 (聪明响应 + 简单上下文参考)
    // 这里为了展现真实对话感,AI会依据对话历史和当前用户消息智能生成不同风格的回复
    async function generateAIResponse(userMessage) {
        // 模拟网络延迟 400~1500ms,让体验更自然
        const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
        await delay(600 + Math.random() * 700);
        
        // 小技巧: 让AI具备基本的意图识别和有趣回答,增强实用性。
        const lowerMsg = userMessage.toLowerCase().trim();
        
        // 1. 问候类
        if (lowerMsg.match(/^(你好|嗨|hi|hello|在吗|您好)/)) {
            return "你好呀!😊 有什么我可以帮到你的吗?随时分享你的想法~";
        }
        
        // 2. 提问时间/日期
        if (lowerMsg.includes('时间') || lowerMsg.includes('几点了') || lowerMsg.includes('日期') || lowerMsg.includes('今天')) {
            const now = new Date();
            const dateStr = `${now.getFullYear()}年${now.getMonth()+1}月${now.getDate()}日`;
            const timeStr = `${now.getHours().toString().padStart(2,'0')}:${now.getMinutes().toString().padStart(2,'0')}`;
            return `📅 现在是 ${dateStr} ${timeStr},记得劳逸结合哦!`;
        }
        
        // 3. 笑话 / 轻松时刻
        if (lowerMsg.includes('笑话') || lowerMsg.includes('幽默') || lowerMsg.includes('讲个笑话')) {
            const jokes = [
                "为什么程序员总是分不清万圣节和圣诞节?因为 Oct 31 = Dec 25!😂",
                "AI 和人类有什么区别?AI 不会忘记保存文件,但我猜你会~",
                "“为什么电脑总是很冷?” “因为它有Windows(窗户)开着呢!”",
                "数学老师说:『统计表明,80%的人会因为笑话冷而沉默』。我:『那剩下的20%呢?』老师:『他们在想别的笑话。』🤣"
            ];
            return jokes[Math.floor(Math.random() * jokes.length)];
        }
        
        // 4. 编程/技术帮助
        if (lowerMsg.includes('代码') || lowerMsg.includes('python') || lowerMsg.includes('javascript') || lowerMsg.includes('html') || lowerMsg.includes('怎么实现')) {
            return "💻 关于代码问题,可以具体描述你的需求~ 我支持给出示例、解释逻辑或调试思路。比如你想实现一个排序算法?或是前端的布局小技巧?随时细化,我为你解答。";
        }
        
        // 5. 关于AI自身
        if (lowerMsg.includes('你是谁') || lowerMsg.includes('你叫什么') || lowerMsg.includes('你的功能')) {
            return "我是智能对话助手,内置轻量知识库,可以陪你聊天、解决问题、构思创意。纯前端模拟但反应够快,并且记忆当前对话上下文 🌟";
        }
        
        // 6. 情感/鼓励类
        if (lowerMsg.includes('压力') || lowerMsg.includes('好累') || lowerMsg.includes('焦虑') || lowerMsg.includes('加油')) {
            return "你已经很棒了!每一个努力的瞬间都值得肯定 ✨ 适当放松,做些喜欢的事,我会一直在这儿支持你。";
        }
        
        // 7. 如果用户消息较长或者问意见
        if (userMessage.length > 20 && (lowerMsg.includes('怎么') || lowerMsg.includes('建议') || lowerMsg.includes('推荐'))) {
            return "收到你的详细描述!根据你的需求,我的建议是:先理清楚核心目标,拆解成小任务逐步完成。如果需要更具体的方案,可以再告诉我更多背景信息,一起头脑风暴~";
        }
        
        // 8. 默认智能回话: 引用上下文情感
        const contextReferences = [
            "有趣的话题!🤔 让我想想…… 其实我觉得可以换个角度思考,你觉得呢?",
            "收到!✨ 你说的很有启发性,我正在学习。还有更多细节想讨论的吗?",
            "这个问题很有意思!我相信持续探索会让你发现更多可能。需要我给出例子吗?",
            "明白啦。根据对话历史来看,我们继续深入的话,或许可以试试看不同的方法。"
        ];
        
        // 简单使用随机 + 带点礼貌
        return contextReferences[Math.floor(Math.random() * contextReferences.length)] + " 你的消息我已记住:「" + (userMessage.length > 32 ? userMessage.slice(0,32)+"…" : userMessage) + "」";
    }
    
    // 发送消息主流程
    async function sendUserMessage() {
        if (isWaitingForAI) return;
        const rawMessage = messageInput.value.trim();
        if (rawMessage === "") return;
        
        // 清空输入框 & 重置高度
        messageInput.value = "";
        messageInput.style.height = "auto";
        
        // 1. 添加用户消息到界面及历史
        addMessageToChat('user', rawMessage);
        
        // 2. 设置等待标志,避免并发
        isWaitingForAI = true;
        statusBadge.innerText = "思考中...";
        statusBadge.style.background = "#8b5f2e";
        
        // 3. 显示正在输入指示器 (打字效果)
        showTypingIndicator();
        
        try {
            // 调用模拟AI响应(可以基于整个conversationHistory,但这里传给generate函数当前用户消息+智能判断)
            const aiReply = await generateAIResponse(rawMessage);
            
            // 移除打字指示器
            removeTypingIndicator();
            
            // 添加AI回复到界面和历史
            addMessageToChat('assistant', aiReply);
            
        } catch (error) {
            console.error("AI回复出错", error);
            removeTypingIndicator();
            addMessageToChat('assistant', "抱歉,我遇到了一点技术小故障,请稍后再试~🧐");
        } finally {
            isWaitingForAI = false;
            statusBadge.innerText = "在线";
            statusBadge.style.background = "#1f5068";
            // 确保输入框焦点
            messageInput.focus();
        }
    }
    
    // 清空对话(同时清除存储和历史,重置初始欢迎语)
    function clearConversation() {
        if (isWaitingForAI) return;
        // 重置历史数组
        conversationHistory = [
            { role: 'assistant', content: '对话已重置✨ 我是你的AI助手,有什么新问题都可以问我~' }
        ];
        saveToLocalStorage();
        // 重绘界面
        renderMessagesFromHistory();
        // 移除任何残留打字指示器
        removeTypingIndicator();
        isWaitingForAI = false;
        statusBadge.innerText = "在线";
        statusBadge.style.background = "#1f5068";
        // 让输入框可交互
        messageInput.focus();
    }
    
    // 事件绑定 & 辅助:textarea 自适应高度
    function autoResizeTextarea() {
        messageInput.style.height = 'auto';
        messageInput.style.height = Math.min(messageInput.scrollHeight, 120) + 'px';
    }
    
    // 键盘监听 (回车发送,但是Shift+Enter换行)
    function onInputKeydown(e) {
        if (e.key === 'Enter' && !e.shiftKey) {
            e.preventDefault();
            sendUserMessage();
        }
    }
    
    // 初始化事件和加载存储
    function init() {
        loadMessagesFromStorage();      // 加载历史并渲染
        
        sendBtn.addEventListener('click', sendUserMessage);
        clearBtn.addEventListener('click', clearConversation);
        messageInput.addEventListener('input', autoResizeTextarea);
        messageInput.addEventListener('keydown', onInputKeydown);
        messageInput.focus();
        
        // 初次加载滚动到底部
        scrollToBottom();
        
        // 防止多次快速点击清除导致状态异常
    }
    
    // 页面启动
    init();
</script>
</body>
</html>

  

posted @ 2026-05-10 09:33  wgscd  阅读(37)  评论(0)    收藏  举报