Tailwind CSS 实战:基于 Kooboo 构建 AI 对话框页面(七):消息框交互功能添加

news2025/6/7 1:00:45

Tailwind CSS 实战,基于Kooboo构建AI对话框页面(一)

Tailwind CSS 实战,基于Kooboo构建AI对话框页面(二):实现交互功能

Tailwind CSS 实战,基于 Kooboo 构建 AI 对话框页面(三):实现暗黑模式主题切换

Tailwind CSS 实战,基于 Kooboo 构建 AI 对话框页面(四):语音识别输入功能

Tailwind CSS 实战:基于 Kooboo 构建 AI 对话框页面(五):语音合成输出与交互增强

Tailwind CSS 实战:基于 Kooboo 构建 AI 对话框页面(六):图片上传交互功能


《Tailwind CSS 实战》系列 前六篇内容中,我们围绕 Kooboo 平台,逐步构建起 AI 对话框页面的基础框架、语音交互、主题切换等能力。本篇作为第七篇,聚焦纯 JavaScript 驱动的消息交互功能迭代,在不改动 HTML 结构的前提下,为消息框新增 复制、编辑、删除(用户消息)及复制、重新生成(AI 消息)功能,深度优化对话交互体验。实现效果如下:

一、功能需求分析

(一)用户消息操作

  • 复制内容:点击复制按钮,调用浏览器剪贴板 API,将消息文本复制,支持后续粘贴复用。
  • 编辑消息:允许对已发送的消息进行编辑,编辑完成并发送后,在消息上显示 “(已编辑)” 标记,清晰区分原始与修改后内容。
  • 删除消息:用户可主动移除自身发送的消息,触发删除操作时,通过 confirm 弹窗二次确认,避免误删。

(二)AI 消息操作

  • 复制内容:与用户消息复制逻辑复用,快速提取 AI 回复文本到剪贴板。
  • 重新生成:点击按钮后,展示加载动画,模拟 AI 重新处理逻辑,完成后替换原有回复内容,为用户提供多样化 AI 回答。

二、实现思路

  • 不改动 HTML 结构:依赖 Tailwind CSS 预设的 .message .copy-button 等类名,通过 JavaScript 动态绑定 事件。
  • 交互逻辑解耦:封装通用函数(如复制、加载状态),避免重复代码,适配用户与 AI 消息的差异化交互。

三、核心 Javascript 逻辑代码

1. 初始化与事件绑定

// 初始化消息操作事件
    function setupMessageActions() {
        const messages = document.querySelectorAll('.message');
        
        messages.forEach(message => {
            const messageType = message.dataset.messageType;
            const actions = message.querySelector('.absolute.top-2.right-2');
            
            // 鼠标悬停显示操作按钮
            message.addEventListener('mouseenter', () => {
                actions.classList.remove('opacity-0');
                actions.classList.add('opacity-100');
            });
            
            message.addEventListener('mouseleave', () => {
                actions.classList.remove('opacity-100');
                actions.classList.add('opacity-0');
            });
            
            // 复制按钮点击
            const copyButton = message.querySelector('.copy-button');
            if (copyButton) {
                copyButton.addEventListener('click', () => {
                    copyMessageContent(copyButton);
                });
            }
            
            // 编辑按钮点击(用户消息)
            if (messageType === 'user') {
                const editButton = message.querySelector('.edit-button');
                const deleteButton = message.querySelector('.delete-button');
                
                editButton.addEventListener('click', () => {
                    const messageId = message.dataset.messageId;
                    const messageText = message.querySelector('.message-content p').textContent;
                    
                    // 设置当前编辑的消息ID
                    currentEditingMessageId = messageId;
                    
                    // 将消息内容放入输入框
                    messageInput.value = messageText;
                    messageInput.focus();
                    
                    // 保持发送按钮文本不变
                    sendButton.textContent = '发送';
                });
                
                // 删除消息
                deleteButton.addEventListener('click', () => {
                    if (confirm('确定要删除这条消息吗?')) {
                        message.remove();
                        
                    }
                });
            }
            
            // 重新生成按钮点击(AI消息)
            if (messageType === 'ai') {
                const regenerateButton = message.querySelector('.regenerate-button');
                regenerateButton.addEventListener('click', () => {
                    const originalText = message.querySelector('.message-content code').textContent.trim();
                    regenerateAIResponse(message, originalText);
                });
            }
        });
    }

核心逻辑

  • 事件处理函数仅负责状态变更(如设置 currentEditingMessageId)和触发行为(如调用 copyMessageContent)。
  • 具体的 DOM 更新(如修改消息内容、添加标记)集中在其他函数(如 sendMessage)中处理,避免事件回调过于臃肿。

2. 复制功能实现

function copyMessageContent(element) {
    const messageContent = element.closest('.message').querySelector('.message-content');
    let textToCopy = '';
    
    // 智能识别文本类型(普通文本或代码块)
    const paragraphs = messageContent.querySelectorAll('p');
    if (paragraphs.length > 0) {
        paragraphs.forEach(p => textToCopy += p.textContent + '\n');
    } else {
        const codeBlocks = messageContent.querySelectorAll('code');
        if (codeBlocks.length > 0) {
            codeBlocks.forEach(code => textToCopy += code.textContent + '\n');
        }
    }
    
    // 使用现代剪贴板 API
    navigator.clipboard.writeText(textToCopy.trim())
        .then(() => showToast(' 内容已复制到剪贴板'))
        .catch(err => {
            console.error('无法复制内容:', err);
            showToast('复制失败,请重试');
        });
}

核心逻辑

  • 通过 closest('.message') 找到最近的消息容器,确保能正确定位内容
  • 自动识别内容类型(普通文本 p 或代码块 code
  • 使用 navigator.clipboard.writeText 实现跨平台复制
  • 成功 / 失败都有明确的视觉反馈(toast 提示)

3. 编辑功能实现

// 全局变量跟踪当前编辑状态
let currentEditingMessageId = null;

// 编辑按钮点击事件
editButton.addEventListener('click', () => {
    const messageId = message.dataset.messageId;
    const messageText = message.querySelector('.message-content p').textContent;
    
    // 设置编辑状态并填充内容
    currentEditingMessageId = messageId;
    messageInput.value = messageText;
    messageInput.focus();
});

// 发送消息时处理编辑状态
function sendMessage(text) {
    if (!text) return;
    
    if (currentEditingMessageId) {
        // 找到原消息并标记为已编辑
        const originalMessage = document.querySelector(`.message[data-message-id="${currentEditingMessageId}"]`);
        const editIndicator = document.createElement('span');
        editIndicator.className = 'text-xs text-blue-300 ml-2';
        editIndicator.textContent = '(已编辑)';
        
        // 添加编辑标记并隐藏操作按钮
        const timestamp = originalMessage.querySelector('.message-timestamp');
        timestamp.parentNode.insertBefore(editIndicator, timestamp.nextSibling);
        originalMessage.querySelector('.absolute.top-2.right-2').style.display = 'none';
        
        // 清除编辑状态
        currentEditingMessageId = null;
    }
    
    // 正常发送消息逻辑...
}

核心逻辑

  • 使用全局变量 currentEditingMessageId 跟踪编辑状态
  • 点击编辑时,将消息内容回填到输入框
  • 发送消息时,检查编辑状态并处理原消息:
    • 添加 "(已编辑)" 标记
    • 隐藏编辑 / 删除按钮(防止重复编辑)
  • 编辑完成后自动清除状态

4. 删除功能实现

deleteButton.addEventListener('click', () => {
    if (confirm('确定要删除这条消息吗?')) {
        message.remove();
    }
});

核心逻辑

  • 使用浏览器内置的 confirm 对话框进行二次确认
  • 确认后直接从 DOM 中移除消息元素
  • 简单高效,防止误操作

5. 重新生成功能实现

regenerateButton.addEventListener('click', () => {
    const originalText = message.querySelector('.message-content code').textContent.trim();
    const contentDiv = message.querySelector('.message-content');
    
    // 显示加载状态
    contentDiv.innerHTML = `
        <div class="flex items-center">
            <div class="animate-spin rounded-full h-4 w-4 border-b-2 border-blue-500 mr-2"></div>
            <span class="text-sm text-gray-500">正在重新生成...</span>
        </div>
    `;
    
    // 模拟API请求延迟
    setTimeout(() => {
        const newResponse = generateAIResponse(originalText);
        contentDiv.innerHTML = `
            <pre class="text-sm bg-[var(--bg-primary)] p-3 rounded-md text-[var(--text-primary)]"><code>${newResponse}</code></pre>
            <span class="text-xs text-[var(--text-secondary)] mt-2 block message-timestamp">${getCurrentTime()}</span>
        `;
        
        // 更新时间戳
        const timestamp = message.querySelector('.message-timestamp');
        timestamp.textContent = getCurrentTime();
        
        // 自动播放语音(如果启用)
        if (speechSettings.autoPlay && !synth.speaking) {
            speak(newResponse);
        }
    }, 1000);
});

核心逻辑

  • 点击后立即显示加载动画(旋转的蓝色圆点)
  • 使用 setTimeout 模拟 API 请求延迟(实际项目中替换为真实 API 调用)
  • 生成新内容后:
    • 更新消息内容
    • 更新时间戳
    • 自动播放语音(如果启用)
  • 整个过程保持界面响应,用户可随时取消

6. 提示信息显示

function showToast(message) {
    const toast = document.createElement('div');
    toast.className = 'fixed top-4 left-1/2 transform -translate-x-1/2 bg-gray-800 text-white px-4 py-2 rounded-lg shadow-lg z-50 transition-all duration-300 translate-y-[-20px] opacity-0';
    toast.innerHTML = `<i class="fa fa-check-circle"></i><span>${message}</span>`;
    
    document.body.appendChild(toast);
    setTimeout(() => {
        toast.classList.remove('translate-y-[-20px]', 'opacity-0');
        toast.classList.add('translate-y-0', 'opacity-100');
    }, 10);
    
    setTimeout(() => {
        toast.classList.add('translate-y-[-20px]', 'opacity-0');
        setTimeout(() => {
            document.body.removeChild(toast);
        }, 300);
    }, 3000);
}

核心逻辑

  • 创建顶部居中的提示框
  • 使用 Tailwind CSS 类控制动画:
    • 淡入:从 opacity-0 到 opacity-100
    • 上移:从 translate-y-[-20px] 到 translate-y-0
  • 3 秒后自动淡出并移除

7. 全局状态管理

// 全局变量跟踪状态
let currentEditingMessageId = null;
let lastPlayedButton = null;
let lastPlayedText = '';

// 点击非消息区域时取消编辑状态
document.addEventListener('click', (e) => {
    if (!e.target.closest('.message') && !e.target.closest('#messageInput')) {
        currentEditingMessageId = null;
    }
});

核心逻辑

  • 使用全局变量跟踪关键状态(编辑、语音播放等)
  • 点击其他区域时自动取消编辑状态
  • 确保系统状态与用户操作保持同步

四、功能验证

  1. 复制功能:点击用户 / AI 消息的 “复制” 按钮,检查剪贴板内容是否正确,提示框是否正常显示 / 消失。
  2. 编辑功能:编辑用户消息后发送,确认 “(已编辑)” 标记显示、操作按钮隐藏。
  3. 删除功能:删除用户消息时,确认二次确认弹窗,及消息节点是否从 DOM 移除。
  4. 重新生成功能:点击 AI 消息 “重新生成”,检查加载动画、新内容渲染是否正常。

五、总结

本次功能的实现采用了以下关键技术和设计模式:

  1. 状态管理模式:使用全局变量跟踪编辑状态
  2. 事件委托模式:为动态生成的元素绑定事件
  3. 异步处理:使用 Promise 和 setTimeout 处理剪贴板操作和 API 请求
  4. UI 状态机:通过修改 DOM 结构实现加载状态和编辑标记
  5. 响应式设计:使用 Tailwind 的响应式类名适配不同屏幕尺寸

这些功能通过精心设计的交互流程和视觉反馈,为用户提供了直观、高效的消息管理体验,同时保持了代码的可维护性和扩展性。

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2402309.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【计算机网络】网络层IP协议与子网划分详解:从主机通信到网络设计的底层逻辑

&#x1f525;个人主页&#x1f525;&#xff1a;孤寂大仙V &#x1f308;收录专栏&#x1f308;&#xff1a;计算机网络 &#x1f339;往期回顾&#x1f339;&#xff1a; 【计算机网络】传输层TCP协议——协议段格式、三次握手四次挥手、超时重传、滑动窗口、流量控制、 &…

基于WSL搭建Ubnutu 20.04.6 LTS(二)-部署Docker环境

Docker是一组平台即服务&#xff08;PaaS&#xff09;的产品。它基于操作系统层级的虚拟化技术&#xff0c;将软件与其依赖项打包为容器。托管容器的软件称为Docker引擎。Docker能够帮助开发者在轻量级容器中自动部署应用程序&#xff0c;并使得不同容器中的应用程序彼此隔离&a…

【图像处理入门】6. 频域图像处理:傅里叶变换与滤波的奥秘

摘要 频域图像处理通过傅里叶变换将图像从空间域转换到频率域,为图像增强、去噪、压缩等任务提供全新视角。本文将深入解析傅里叶变换原理,介绍低通、高通滤波的实现方式,结合OpenCV和Python代码展示频域滤波在去除噪声、增强边缘中的应用,帮助读者掌握图像频域处理的核心…

基于开源AI智能名片链动2+1模式S2B2C商城小程序的生态农庄留存运营策略研究

摘要&#xff1a;本文聚焦于生态农庄运营中的游客留存问题&#xff0c;以村长与乡亲们吸引游客进村为背景&#xff0c;深入探讨如何借助开源AI智能名片链动2 1模式S2B2C商城小程序实现游客的有效留存。通过分析该小程序在信息传递、服务整合、营销激励等方面的优势&#xff0c…

Jenkins实现自动化部署Springboot项目到Docker容器(Jenkinsfile)

Jenkins实现自动化部署Springboot项目到Docker容器 引言:为什么需要自动化部署? 在软件开发中,频繁的手动部署既耗时又容易出错。通过 Docker + Jenkins + Git 的组合,您可以实现: ✅ 一键部署:代码推送后自动构建和部署🐳 环境一致性:Docker 确保开发、测试、生产环…

【Linux】Git原理与使用

编程不仅是解决问题的艺术&#xff0c;更是对复杂性进行优雅管理的哲学。 前言 这是我自己学习Linux系统编程的第三篇笔记。后期我会继续把Linux系统编程笔记开源至博客上。 上一期笔记是关于Vim文本编辑器知识&#xff1a; 【Linux】Vim文本编辑器-CSDN博客https://blog.csdn…

A*算法实现原理以及实现步骤(C++)

算法原理&#xff1a; A*算法是一种启发式搜索算法&#xff0c;用于在图中寻找最短路径。它结合了Dijkstra算法的确保最短路径的优点和贪心最佳优先搜索的高效性。其核心在于使用一个评估函数&#xff1a; f(n) g(n) h(n) 其中&#xff1a; - g(n) 表示从起点到节点n的实际代…

Devops自动化运维---py基础篇一

python基础篇 1、基本数据类型 2、算术运算符 3、变量 变量&#xff1a;编程语言中能储存结果或能表示值的抽象概念 用途&#xff1a;用一段数据赋予一个简短、易于记忆的名字&#xff0c;方便重复使用3.1 格式转化变量 操作符号描述%s字符串%d整数%f浮点数 实例&#xff1…

平安养老险蚌埠中心支公司开展金融宣教活动

近日&#xff0c;平安养老保险股份有限公司&#xff08;以下简称“平安养老险”&#xff09;蚌埠中心支公司&#xff0c;走进某合作企业开展金融教育宣传活动。 活动现场&#xff0c;平安养老险蚌埠中心支公司工作人员通过发放宣传手册和小礼品等方式&#xff0c;向企业员工普…

Redisson简明教程—你家的锁芯该换了

1.简介 各位攻城狮们&#xff0c;你还在使用原生命令来上锁么&#xff1f;看来你还是不够懒&#xff0c;饺子都给你包好了&#xff0c;你非要吃大饼配炒韭菜&#xff0c;快点改善一下“伙食”吧&#xff0c;写代码也要来点幸福感。今天咱们就来聊聊Redisson提供的各种锁&#…

48V带极性反接保护-差共模浪涌防护方案

在工业自动化&#xff08;电动机驱动 / 工业机器人&#xff09;、交通基础设施&#xff08;充电桩 / 车载电子&#xff09;、安防系统&#xff08;监控摄像头 / 门禁&#xff09;、储能设备&#xff08;BMS / 离网控制器&#xff09;等领域&#xff0c;DC48V 电源因安全特低电压…

Python----目标检测(使用YOLO 模型进行线程安全推理和流媒体源)

一、线程安全推理 在多线程环境中运行YOLO 模型需要仔细考虑&#xff0c;以确保线程安全。Pythons threading 模块允许您同时运行多个线程&#xff0c;但在这些线程中使用YOLO 模型时&#xff0c;需要注意一些重要的安全问题。本页将指导您创建线程安全的YOLO 模型推理。 1.1、…

jvm学习第1day jvm简介,栈溢出、堆溢出

jvm学习第1day jvm简介&#xff0c;栈溢出、堆溢出 jvm简介栈线程安全栈溢出线程运行诊断堆堆溢出 方法区方法区内存溢出常量池和运行时常量池 jvm简介 jvm 是编译后的字节码文件运行的环境&#xff0c; 因此各个平台有了jvm可以运行java.class文件&#xff0c;这是Java跨平台…

用广告维持的免费 AI 图像生成工具(个人项目分享)

用广告维持的免费 AI 图像生成工具&#xff08;个人项目分享&#xff09; 免费 AI 图像生成工具网址&#xff1a;https://aiart.gcc.ac.cn/ 最近做了一个 AI 图像生成器&#xff0c;主要目标是“尽量简单”&#xff1a; 打开网页就能用不用注册、不用登录免费&#xff0c;不…

分析Web3下数据保护的创新模式

在这个信息爆炸的时代&#xff0c;我们正站在 Web3 的门槛上&#xff0c;迎接一个以去中心化、用户主权和数据隐私为核心的新时代。Web3 不仅仅是技术的迭代&#xff0c;它更是一场关于数据权利和责任的结构性变革。本文将探讨 Web3 下数据保护的创新模式&#xff0c;以期为用户…

​减少交通拥堵、提高效率、改善交通安全的智慧交通开源了。

智慧交通视觉监控平台是一款功能强大且简单易用的实时算法视频监控系统。它的愿景是最底层打通各大芯片厂商相互间的壁垒&#xff0c;省去繁琐重复的适配流程&#xff0c;实现芯片、算法、应用的全流程组合&#xff0c;从而大大减少企业级应用约95%的开发成本。用户只需在界面上…

协议融合驱动效能跃升:Modbus转Ethernet IP的挤出吹塑机应用

在现代工业自动化领域&#xff0c;Modbus作为一种串行通信协议&#xff0c;其稳定性和简单性被广泛应用于各种工控设备中。但随着技术的进步&#xff0c;对于更高速、更远传输距离的需求日益增长&#xff0c;这就需要将Modbus协议通过以太网进行传输&#xff0c;即实现Modbus T…

bug 记录 - 使用 el-dialog 的 before-close 的坑

需求说明 弹窗中内嵌一个 form 表单 原始代码 <script setup lang"ts"> import { reactive, ref } from "vue" import type { FormRules } from element-plus const ruleFormRef ref() interface RuleForm {name: stringregion: number | null } …

Next.js 中间件鉴权绕过漏洞 CVE-2025-29927

前言:CVE-2025-29927 是一个影响 Next.js 的严重漏洞&#xff0c;源于开发者信任了客户端请求中携带的 X-Middleware-Rewrite 头部字段。攻击者可以手动构造该头部&#xff0c;实现绕过中间件逻辑&#xff0c;访问本应受保护的资源或 API。 影响版本&#xff1a;Next.js < …

基于YOLO-NAS-Pose的无人机象群姿态估计:群体行为分析的突破

【导读】 应对气候变化对非洲象的生存威胁&#xff0c;本研究创新采用无人机航拍结合AI姿态分析技术&#xff0c;突破传统观测局限。团队在肯尼亚桑布鲁保护区对比测试DeepLabCut与YOLO-NAS-Pose两种模型&#xff0c;首次将后者引入野生动物研究。通过检测象群头部、脊柱等关键…