vue3之写一个aichat---已聊天组件部分功能

devtools/2025/3/28 11:17:29/

渲染聊天数据

这个不必多说,直接从stores/chat中取出聊天列表数据渲染就好,因为前面添加的消息都是按照用户消息、AI助手消息这样添加的,效果如图
在这里插入图片描述

但是需要注意每条助手消息的状态,需要根据状态显示不同的图标或不显示图标,比如,正常完成的回答,不显示图标,如果是调用流式回调的接口出错的或者中途停止的,且是chatRecordList中的最后一条消息,那么就要显示重新生成的图标,点击重新生成会重新发送消息
重新发送的逻辑处理,主要是找到当前的AI助手消息将其状态变为stop停止状态,然后找到当前AI助手消息之前的最后一条用户消息获取其content,去调用stores/chat中的startChat方法从而走到聊天处理逻辑的文件中去

const handleRefresh = async messageId => {try {const message = props.messages.find(msg => msg.id === messageId)if (message && message.role === 'assistant') {// 设置为等待状态message.status = 'stop'// 找到当前助手消息之前的最后一条用户消息const messageIndex = props.messages.findIndex(msg => msg.id === messageId)let lastUserMessage = nullfor (let i = messageIndex; i >= 0; i--) {if (props.messages[i].role === 'user') {lastUserMessage = props.messages[i]break}}if (lastUserMessage) {try {// 直接使用用户消息重新请求await chatStore.startChat(lastUserMessage.content)} catch (error) {// 如果失败,恢复原状态message.status = 'error'console.error('重新生成失败:', error)}}}} catch (error) {console.error('处理重新生成失败:', error)}
}

生成AI助手消息时,滚动条自动滚动向下

在聊天时要自动滚动到最新消息的位置,使用户能看到最新的消息
因为已聊天组件和未聊天组件的切换写在Chat.vue页面中,所以scrollToBottom定义到Chat.vue中才能正常的实现这个功能
在这里插入图片描述
通过scrollTo方法将top设为container.scrollHeight即元素内容的总高度,包括由于滚动而看不见的内容,浏览器会将滚动条的位置设置为元素内容的底部,由于scrollHeight是元素内容的总高度,因此滚动条会被移动到内容的最底部,从而实现滚动到底部的效果

// 滚动到底部
const scrollToBottom = () => {setTimeout(() => {const container = document.querySelector('.chat-container')if (container) {container.scrollTo({top: container.scrollHeight,behavior: 'smooth'})}}, 100)
}

当chatRecordList变化的时候,自动触发滚动到底部的函数,isClickThought是因为在显示正式的回答前会显示思考的过程,思考完成后,开始回答问题时,会将思考的内容收起,通过点击按钮控制思考内容的显示与隐藏,但是当点击思考内容变化的时候,我们是不希望滚动条滚动的,所以就通过isClickThought来控制

watch(() => chatRecordList.value,async (newList, oldList) => {await nextTick()if (!isClickThought.value) {scrollToBottom()// 短暂延迟后检查滚动位置setTimeout(() => {isScrolledToBottom()}, 100)}isClickThought.value = false},{ deep: true }
)

当消息特别多的时候,有时候可能需要查看之前的聊天内容,查看完成后可能需要继续提问,但是由于内容特别多,手动往下翻肯定费劲,所以需要一个“置底”按钮,这个按钮只有在没有消息生成的时候才会出现,不然滚动条变化的时候页面显示不友好,点击的时候直接调用scrollToBottom函数就行,isScrolledToBottom判断是否显示该按钮

const isScrolledToBottom = () => {const container = chatContainer.valueif (!container) return true// 计算是否接近底部(允许20px的误差)const isAtBottom = container.scrollHeight - container.scrollTop - container.clientHeight < 20isShowToBottom.value = !isAtBottom && !isResponding.value
}

停止当前对话和开启新对话功能

通过计算属性isResponding判断是显示“停止”还是显示“开启新对话”,当进入已聊天界面且没有正在回答的消息的时候,显示“开启新对话”按钮,否则显示停止
在这里插入图片描述

const isResponding = computed(() => {const lastMessage = chatRecordList.value[chatRecordList.value.length - 1]return lastMessage?.role === 'assistant' && ['thinking', 'typing'].includes(lastMessage.status)
})

点击停止按钮
触发chatStore中的停止响应函数

const handleStopResponse = () => {chatStore.stopResponse()
}

stores/chat.js,其中isResponseStopped是在chat.service.js判断是否需要停止响应用到的

  const isResponseStopped = ref(false) //是否停止响应function stopResponse() {isResponseStopped.value = true// 确保调用 abortRequestabortRequest()const lastMessage = chatRecordList.value[chatRecordList.value.length - 1]//获取到最后一条聊天记录if (lastMessage && lastMessage.role === 'assistant') {if (lastMessage.status === 'thinking') {//如果当前AI助手消息的状态是思考中则将状态变为stop停止状态lastMessage.status = 'stop'} else if (lastMessage.status === 'typing') {//如果当前AI助手消息的状态是已经构建回答但尚未完成,则将状态设为已完成lastMessage.status = 'done'}}}

如果当前停止的消息是最后一条AI助手消息那么就又会有handleRefresh
点击开启新会话
开启新会话调用chatStore.newChat()方法

const handleNewChat = () => {chatStore.newChat()
}

stores/chat.js
开启新对话要把之前的对话列表、是否已聊天等状态重置

  // 清除聊天状态const clearChatState = () => {hasChatted.value = falsechatRecordList.value = []assistantMessageId.value = nullisResponseStopped.value = falsesessionId.value = ''isHistory.value = falsehistoryTime.value = ''recommendedQuestions.value = []isFromRecommend.value = false}const newChat = () => {clearChatState()setSessionId(chatService.getRandomSessionId())}cosnt setSessionId = (id) => {sessionId.value = id}

每个回答完成会根据当前的问题返回对应的三条推荐,当点击推荐的时候会在当前页面发送一个新的问题

// 点击推荐问题,发送问题
const handleRecommend = item => {chatStore.setRecommendedQuestions([])chatStore.startChat(item) // 添加 true 标识这是推荐问题
}

记住推荐问题一般都是跟在最后一条消息后面的,所以每次调用会话接口的时候要记得将setRecommendedQuestions列表置空


http://www.ppmy.cn/devtools/168884.html

相关文章

解决 WSL1 Ubuntu 24.04 更新失败

问题 wsl1 的 Ubuntu 24.04 执行: sudo apt update sudo apt upgrade -y更新过程安装失败, 提示错误: Setting up systemd (xxxx-xxx)... Failed to take /etc/passwd lock: Invalid argument dpkg: error processing package systemd (--configure):installed systemd pack…

MyBatis 学习经验分享

MyBatis 是一个广泛使用的 Java 持久层框架&#xff0c;它为开发者提供了灵活、易用的数据库操作方式。与 Hibernate 等全自动化 ORM&#xff08;对象关系映射&#xff09;框架不同&#xff0c;MyBatis 采用了更精细的控制&#xff0c;允许开发者在 SQL 语句和 Java 对象之间进…

【Linux】systemV消息队列和信号量

个人主页~ 消息队列和信号量 一、消息队列1、实现原理2、系统调用接口&#xff08;一&#xff09;创建获取一个消息队列&#xff08;二&#xff09;控制消息队列&#xff08;三&#xff09;发送消息&#xff08;四&#xff09;在消息队列中获取数据块 二、信号量1、原理2、系统…

python基础8 单元测试

通过前面的7个章节&#xff0c;作者学习了python的各项基础知识&#xff0c;也学习了python的编译和执行。但在实际环境上&#xff0c;我们需要验证我们的代码功能符合我们的设计预期&#xff0c;所以需要结合python的单元测试类&#xff0c;编写单元测试代码。 Python有一个内…

第十五届蓝桥杯C/C++组:宝石组合题目(从小学奥数到编程题详解)

这道题目真的一看就不好做&#xff0c;如果直接暴力去做百分之90必挂掉&#xff0c;那么这道题目到底应该怎么去做呢&#xff1f;这我们就得从小学奥数开始聊了。&#xff08;闲话&#xff1a;自从开始蓝桥杯备赛后&#xff0c;每天都在被小学奥数震惊&#xff0c;为什么我小的…

WebRTC中音视频服务质量QoS之RTT衡量网络往返时延的加权平均RTT计算机制‌详解

WebRTC中音视频服务质量QoS之RTT衡量网络往返时延加权平均RTT计算机制‌的详解 WebRTC中音视频服务质量QoS之RTT衡量网络往返时延加权平均RTT计算机制‌的详解 WebRTC中音视频服务质量QoS之RTT衡量网络往返时延加权平均RTT计算机制‌的详解前言一、 RTT 网络往返时延的原理‌1、…

解释 一下什么是 React 的 useRef Hook

useRef 是 React 中的一个 Hook&#xff0c;用于创建一个可以持久化存储的可变引用。它通常用于访问 DOM 元素或保存任何可变值&#xff0c;而不触发组件的重新渲染。本文将详细介绍 useRef 的定义、用法、适用场景及最佳实践。 1. 什么是 useRef&#xff1f; 1.1 定义 useR…

虚拟机 | Ubuntu操作系统:su和sudo理解及如何处理忘记root密码

系列文章目录 虚拟机 | Ubuntu 安装流程以及界面太小问题解决 虚拟机 | Ubuntu图形化系统&#xff1a; open-vm-tools安装失败以及实现文件拖放 文章目录 系列文章目录前言一、su和sudo是什么&#xff1f;1、su忘记root密码的解决方案无法进入GRUB引导页面 2、sudo推荐使用sud…