7. 综合演练聊天面板前端实现-SpringAI实战

8 篇文章 0 订阅

前端实现

在学习了之前的SpringAI基础功能SSE流、内存历史消息基础上,整合前端界面和数据库版本的历史消息。
聊天面板

前端的聊天面板组件是以chat-view.vue为核心,聊天面板的结构如下

  • 聊天面板
    • 会话面板(左)
      • 标题(上)
      • 会话列表(中),进入页面的时候获取用户的会话列表使用session-item组件,该组件有删除按钮点击触发handleDeleteSession
      • 创建会话按钮(下), 触发点击事件调用handleCreateSession
    • 消息面板(右)
      • 会话详情(上),可编辑会话名称调用handleUpdateSession
      • 消息列表(中),for循环遍历activeSession当前会话中的消息使用message-row组件展示文本消息、图片消息、语音消息
      • 输入框使用(下),message-input组件

会话列表

会话列表

for循环遍历会话列表用会话组件显示,并监听点击事件和删除事件。点击时切换到被点击的会话,删除时从会话列表中提出被删除的会话。

<!-- 左侧的会话列表 -->
<div class="session-panel">
  <div class="title">AI助手</div>
  <div class="session-list" v-if="activeSession">
    <!-- 遍历会话列表 -->
    <session-item
      v-for="session in sessionList"
      :key="session.id"
      :active="session.id === activeSession.id"
      :session="session"
      class="session"
      @click="activeSession = session"
      @delete="handleDeleteSession"
    ></session-item>
  </div>
  <div class="button-wrapper">
    <el-button
      style="margin-right: 20px"
      :icon="ChatRound"
      size="small"
      @click="handleSessionCreate"
      >创建会话</el-button
    >
  </div>
</div>

会话的创建、删除、加载逻辑。

const activeSession = ref<AiSession>()
const sessionList = ref<AiSession[]>([])
const handleCreateSession = async (session: AiSessionInput) => {
  const res = await api.aiSessionController.save({ body: session })
  const sessionRes = await api.aiSessionController.findById({ id: res })
  sessionList.value.unshift(sessionRes)
  activeSession.value = sessionList.value[0]
}
// 从会话列表中删除会话
const handleDeleteSession = async (session: AiSession) => {
  await api.aiSessionController.delete({ body: [session.id] })
  const index = sessionList.value.findIndex((value) => {
    return value.id === session.id
  })
  sessionList.value.splice(index, 1)
  if (index == sessionList.value.length) {
    activeSession.value = sessionList.value[index - 1]
  } else {
    activeSession.value = sessionList.value[index]
  }
}
onMounted(async () => {
  // 查询自己的聊天会话
  api.aiSessionController.findByUser().then((res) => {
    // 讲会话添加到列表中
    sessionList.value = res.map((row) => {
      return { ...row, checked: false }
    })
    // 默认选中的聊天会话是第一个
    if (sessionList.value.length > 0) {
      activeSession.value = sessionList.value[0]
    } else {
      handleSessionCreate()
    }
    loading.value = false
  })
})

消息发送和展示

消息列表和消息发送

<!-- 监听发送事件 -->
<message-input @send="handleSendMessage" v-if="activeSession"></message-input>

用户发送的逻辑,使用sse.js发送消息,并监听sse消息。

const handleSendMessage = async (message: { text: string; image: string }) => {
  if (!activeSession.value) {
    ElMessage.warning('请创建会话')
    return
  }
  // 图片/语音
  const medias: AiMessage['medias'] = []
  if (message.image) {
    medias.push({ type: 'image', data: message.image })
  }
  // 用户的提问
  const chatMessage = {
    id: new Date().getTime().toString(),
    sessionId: activeSession.value.id,
    medias,
    textContent: message.text,
    type: 'USER'
  } satisfies AiMessage

  // 新建一个ChatGPT回复对象,不能重复使用同一个对象。
  responseMessage.value = {
    id: new Date().getTime().toString(),
    type: 'ASSISTANT',
    textContent: '',
    sessionId: activeSession.value.id
  }
  const evtSource = new SSE(import.meta.env.VITE_API_PREFIX + '/message/chat', {
    withCredentials: true,
    // 禁用自动启动,需要调用stream()方法才能发起请求
    start: false,
    headers: { 'Content-Type': 'application/json' },
    payload: JSON.stringify(chatMessage),
    method: 'POST'
  })
  evtSource.addEventListener('message', async (event: any) => {
    responseMessage.value.textContent = event.data
    // 非通义千问使用下面的消息拼接逻辑
    // responseMessage.value.textContent += event.data
  })
  // 调用stream,发起请求。
  evtSource.stream()
  // 将两条消息显示在页面中
  activeSession.value.messages.push(...[chatMessage, responseMessage.value])
  await nextTick(() => {
    messageListRef.value?.scrollTo(0, messageListRef.value.scrollHeight)
  })
}

遍历会话中的消息并展示

<div ref="messageListRef" class="message-list">
  <!-- 过渡效果 -->
  <transition-group name="list" v-if="activeSession">
    <message-row
      v-for="message in activeSession.messages"
      :key="message.id"
      :message="message"
    ></message-row>
  </transition-group>
</div>
  • 22
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值