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

渲染聊天数据

这个不必多说,直接从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 = null

      for (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.value
  if (!container) return true

  // 计算是否接近底部(允许20px的误差)
  const isAtBottom = container.scrollHeight - container.scrollTop - container.clientHeight < 20
  isShowToBottom.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
      // 确保调用 abortRequest
      abortRequest()
      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 = false
      chatRecordList.value = []
      assistantMessageId.value = null
      isResponseStopped.value = false
      sessionId.value = ''
      isHistory.value = false
      historyTime.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列表置空

<think>嗯,用户问的是如何在Spring Boot和Vue项目里接入深度求索(DeepSeek)的AI客服。首先,我需要理解用户的需求。他们可能想要在自己的Web应用中集成一个智能客服系统,使用DeepSeek的API来处理用户的咨询。接下来,我得考虑整个流程,包括后端和前端各自的职责。 首先,后端部分,Spring Boot需要处理与DeepSeek API的通信。可能需要使用RestTemplate或者WebClient来发送HTTP请求。用户需要先注册DeepSeek的账号,获取API密钥,然后查看API文档,确定请求的端点、参数和返回格式。然后,创建一个Controller来接收前端的请求,调用DeepSeek的服务,并将结果返回给前端。这里要注意异常处理,比如网络问题或API限制,还要考虑异步处理,避免阻塞主线程。 然后是前端部分Vue需要创建一个客服界面,可能是一个聊天窗口,用户输入消息后,通过Axios发送到Spring Boot后端。后端再将请求转发给DeepSeek,获取回复后返回给前端展示。这里需要考虑消息的实时性,可能需要WebSocket来实现长连接,或者使用轮询的方式。不过轮询可能效率不高,WebSocket会更适合实时聊天。 另外,安全性方面,API密钥不能暴露在前端,所以必须通过后端来中转请求。同时,可能需要处理用户会话的历史记录,保持对话的连贯性。还有,前端界面需要设计用户友好的聊天界面,显示消息气泡、加载状态等。 测试也是重要的环节,确保前后端通信正常,处理各种边界情况,比如超时、错误响应。此外,性能优化,比如后端缓存常用回答,减少API调用次数,或者前端防抖处理用户频繁发送消息的情况。 可能还需要考虑多轮对话的处理,DeepSeek的API是否支持上下文传递,如何在每次请求中携带之前的对话历史,以便AI理解当前的对话情境。这可能需要在后端维护用户的会话状态,或者前端在发送请求时附带历史消息。 最后,部署的时候要注意环境变量的配置,比如API密钥应该存储在服务器的环境变量中,而不是硬编码在代码里,确保安全性。同时,监控API的使用情况,避免超过配额或产生意外费用。</think>以下是在SpringBoot+Vue项目中集成DeepSeek AI客服的分步实现方案: 一、后端SpringBoot配置(Java) 1. 添加依赖(pom.xml) ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> ``` 2. 创建配置类处理API请求 ```java @RestController @RequestMapping("/api/ai") public class AIController { @Value("${deepseek.api.key}") private String apiKey; @PostMapping("/chat") public ResponseEntity<String> handleChat(@RequestBody ChatRequest request) { // 使用WebClient发送请求 WebClient client = WebClient.create("https://api.deepseek.com"); return client.post() .uri("/v1/chat/completions") .header("Authorization", "Bearer " + apiKey) .contentType(MediaType.APPLICATION_JSON) .bodyValue(buildRequestBody(request)) .retrieve() .toEntity(String.class) .block(); } private Map<String, Object> buildRequestBody(ChatRequest request) { return Map.of( "model", "deepseek-chat", "messages", List.of(Map.of( "role", "user", "content", request.getMessage() )), "temperature", 0.7 ); } } ``` 二、前端Vue配置 1. 安装必要依赖 ```bash npm install axios vue-markdown ``` 2. 创建AI客服组件(components/AIChat.vue) ```vue <template> <div class="chat-container"> <div v-for="(msg, index) in messages" :key="index"> <div :class="[&#39;message&#39;, msg.role]"> <vue-markdown :source="msg.content"/> </div> </div> <input v-model="inputMessage" @keyup.enter="sendMessage"/> </div> </template> <script> import axios from &#39;axios&#39;; export default { data() { return { inputMessage: &#39;&#39;, messages: [] } }, methods: { async sendMessage() { if (!this.inputMessage.trim()) return; const userMessage = { role: &#39;user&#39;, content: this.inputMessage }; this.messages.push(userMessage); try { const response = await axios.post(&#39;/api/ai/chat&#39;, { message: this.inputMessage }); const aiResponse = { role: &#39;assistant&#39;, content: response.data.choices[0].message.content }; this.messages.push(aiResponse); } catch (error) { console.error(&#39;API请求失败:&#39;, error); } this.inputMessage = &#39;&#39;; } } } </script> ``` 三、关键配置步骤 1. 安全配置(application.yml): ```yaml deepseek: api: key: your_api_key_here url: https://api.deepseek.com/v1 ``` 2. 跨域配置(后端): ```java @Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("http://localhost:8080") .allowedMethods("*"); } } ``` 四、优化建议 1. 实时通信:建议使用WebSocket替代HTTP轮询 2. 历史记录:添加localStorage存储对话历史 3. 流式响应:处理分块响应提升用户体验 4. 限流保护:后端添加RateLimiter防止滥用 5. 敏感词过滤:添加内容过滤中间件 五、部署注意事项 1. 使用环境变量管理API密钥 2. 配置HTTPS确保通信安全 3. 添加请求重试机制 4. 实现对话上下文管理 5. 添加加载状态指示器 建议先通过Postman测试API连通性,再逐步实现完整功能。具体参数请参考DeepSeek官方文档最新版本进行调整。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值