Skip to content

ConversationList 对话列表 ​

对话历史列表容器组件,支持新消息平滑自动滚动到底部、到顶触发加载更早历史消息,以及前插历史消息时的视口位置锚定补偿。

基础用法 ​

你好,我想学习 Vue 3
09:24
你好!很高兴你想学习 Vue 3。Vue 3 是一个渐进式 JavaScript 框架,它的核心特性包括组合式 API、更好的 TypeScript 支持和性能优化。
09:25
能给我一个简单的例子吗?
09:26
当然可以!下面是一个最简单的 Vue 3 组件示例。
09:26
<script setup>
import ConversationList from '@/vue/components/ConversationList.vue'
import ChatMessage from '@/vue/components/ChatMessage.vue'
</script>
<template>
  <div style="border: 1px solid var(--vk-border-color); border-radius: 4px;">
    <ConversationList height="300px">
      <ChatMessage role="user" :timestamp="new Date(Date.now() - 120000)">
        你好,我想学习 Vue 3
      </ChatMessage>
      <ChatMessage role="assistant" :timestamp="new Date(Date.now() - 60000)">
        你好!很高兴你想学习 Vue 3。Vue 3 是一个渐进式 JavaScript 框架,它的核心特性包括组合式 API、更好的 TypeScript 支持和性能优化。
      </ChatMessage>
      <ChatMessage role="user" :timestamp="new Date()">
        能给我一个简单的例子吗?
      </ChatMessage>
      <ChatMessage role="assistant" :timestamp="new Date()">
        当然可以!下面是一个最简单的 Vue 3 组件示例。
      </ChatMessage>
    </ConversationList>
  </div>
</template>

加载更多 ​

滚动到顶部时触发 load-more 事件。内置 preserveScrollOnPrepend 机制,能在历史消息前插入列表后自动补偿滚动高度,防止页面视野突跳。

历史消息 1:这是较早的一轮回复。
09:20
历史消息 2:继续追问一个细节。
09:21
历史消息 3:补充上下文和说明。
09:22
当前问题:滚动到顶部加载更多历史消息。
09:23
向上滚动到顶部会触发 load-more。
09:24
加载中状态会阻止重复触发。
09:25
<script setup>
import { ref } from 'vue'
import ConversationList from '@/vue/components/ConversationList.vue'
import ChatMessage from '@/vue/components/ChatMessage.vue'

let nextId = 1
const loading = ref(false)
const messages = ref([
  { id: nextId++, role: 'assistant', content: '历史消息 1:这是较早的一轮回复。', timestamp: new Date(Date.now() - 360000) },
  { id: nextId++, role: 'user', content: '历史消息 2:继续追问一个细节。', timestamp: new Date(Date.now() - 300000) },
  { id: nextId++, role: 'assistant', content: '历史消息 3:补充上下文和说明。', timestamp: new Date(Date.now() - 240000) },
  { id: nextId++, role: 'user', content: '当前问题:滚动到顶部加载更多历史消息。', timestamp: new Date(Date.now() - 180000) },
  { id: nextId++, role: 'assistant', content: '向上滚动到顶部会触发 load-more。', timestamp: new Date(Date.now() - 120000) },
  { id: nextId++, role: 'assistant', content: '加载中状态会阻止重复触发。', timestamp: new Date(Date.now() - 60000) }
])

function handleLoadMore() {
  if (loading.value) return
  loading.value = true
  setTimeout(() => {
    const older = [
      { id: nextId++, role: 'assistant', content: '更早的历史消息:组件会把它插入到顶部。', timestamp: new Date(Date.now() - 480000) },
      { id: nextId++, role: 'user', content: '更早的用户提问。', timestamp: new Date(Date.now() - 420000) }
    ]
    messages.value = [...older, ...messages.value]
    loading.value = false
  }, 900)
}
</script>

<template>
  <ConversationList height="200px" :loading-more="loading" @load-more="handleLoadMore">
    <ChatMessage
      v-for="message in messages"
      :key="message.id"
      :role="message.role"
      :timestamp="message.timestamp"
    >
      {{ message.content }}
    </ChatMessage>
  </ConversationList>
</template>

双框架使用示例 ​

vue
<template>
  <ConversationList
    height="480px"
    :loading-more="loading"
    loading-text="正在拉取更早的对话记录..."
    :preserve-scroll-on-prepend="true"
    @load-more="handleLoadMore"
  >
    <ChatMessage
      v-for="msg in messages"
      :key="msg.id"
      :role="msg.role"
      :timestamp="msg.timestamp"
    >
      {{ msg.content }}
    </ChatMessage>
  </ConversationList>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { ConversationList, ChatMessage } from '@bobocn/element/vue'
import '@bobocn/element/style.css'

const loading = ref(false)
const messages = ref([
  { id: 1, role: 'user' as const, content: '你好,请帮我分析这份日志', timestamp: new Date() },
  { id: 2, role: 'assistant' as const, content: '请提供日志内容,我将为您分析排查。', timestamp: new Date() }
])

const handleLoadMore = () => {
  if (loading.value) return
  loading.value = true
  setTimeout(() => {
    // 模拟向前插入 2 条历史记录
    messages.value.unshift(
      { id: -2, role: 'user' as const, content: '之前的会话 1', timestamp: new Date(Date.now() - 3600000) },
      { id: -1, role: 'assistant' as const, content: '之前的回复 1', timestamp: new Date(Date.now() - 3500000) }
    )
    loading.value = false
  }, 1000)
}
</script>
tsx
import React, { useState } from 'react'
import { ConversationList, ChatMessage } from '@bobocn/element/react'
import '@bobocn/element/style.css'

export default function App() {
  const [loading, setLoading] = useState(false)
  const [messages, setMessages] = useState([
    { id: 1, role: 'user' as const, content: '你好,请帮我分析这份日志', timestamp: new Date() },
    { id: 2, role: 'assistant' as const, content: '请提供日志内容,我将为您分析排查。', timestamp: new Date() }
  ])

  const handleLoadMore = () => {
    if (loading) return
    setLoading(true)
    setTimeout(() => {
      setMessages(prev => [
        { id: -2, role: 'user' as const, content: '之前的会话 1', timestamp: new Date(Date.now() - 3600000) },
        { id: -1, role: 'assistant' as const, content: '之前的回复 1', timestamp: new Date(Date.now() - 3500000) },
        ...prev
      ])
      setLoading(false)
    }, 1000)
  }

  return (
    <ConversationList
      height="480px"
      loadingMore={loading}
      loadingText="正在拉取更早的对话记录..."
      preserveScrollOnPrepend
      onLoadMore={handleLoadMore}
    >
      {messages.map(msg => (
        <ChatMessage key={msg.id} role={msg.role} timestamp={msg.timestamp}>
          {msg.content}
        </ChatMessage>
      ))}
    </ConversationList>
  )
}

API ​

属性 (Attributes / Props) ​

属性名说明类型默认值
autoScroll新消息追加时是否自动平滑滚到底部booleantrue
loadingMore是否正在向上加载历史消息booleanfalse
height容器固定高度(支持 CSS 单位如 '480px' / '60vh')string—
maxHeight容器最大高度string'100%'
preserveScrollOnPrepend向上加载历史消息前插时保持当前视口位置(滚动补偿)booleantrue
emptyText无消息时的空状态文案string'暂无对话'
loadingText向上加载历史消息时的提示文本string'加载更早记录...'

事件 (Events) ​

事件名说明回调参数
load-more / onLoadMore滚动到顶部(触发阈值)时触发加载更多—
scroll / onScroll列表滚动时触发(scrollTop: number)

方法 (Exposes / Ref) ​

方法名说明类型
scrollToBottom手动平滑滚动到列表最底部() => void
getElement获取滚动容器原生 DOM 节点() => HTMLElement | null

插槽 / Children ​

插槽名说明
default消息元素容器(通常为 ChatMessage 集合)

基于 MIT 许可发布