AiCsMessageList.vue 1.51 KB
<template>
  <div ref="listEl" class="cs-message-list">
    <AiCsMessageBubble
      v-for="msg in messages"
      :key="msg.id"
      :message="msg"
    />
    <!-- 滚动锚点 -->
    <div ref="bottomEl"></div>
  </div>
</template>

<script setup lang="ts">
import AiCsMessageBubble from "./AiCsMessageBubble.vue";
import type { CsMessage } from "@/stores/customerService";

const props = defineProps<{ messages: CsMessage[] }>();

const listEl = ref<HTMLElement | null>(null);
const bottomEl = ref<HTMLElement | null>(null);

function scrollToBottom(smooth = true) {
  nextTick(() => {
    bottomEl.value?.scrollIntoView({ behavior: smooth ? "smooth" : "instant" });
  });
}

// 监听消息变化时自动滚底
watch(
  () => props.messages.length,
  () => scrollToBottom(),
);

// 监听最后一条消息内容增量(流式)时滚底
watch(
  () => {
    const last = props.messages[props.messages.length - 1];
    return last?.content?.length ?? 0;
  },
  () => {
    const last = props.messages[props.messages.length - 1];
    if (last?.status === "streaming") scrollToBottom(false);
  },
);

defineExpose({ scrollToBottom });
</script>

<style scoped>
.cs-message-list {
  flex: 1;
  overflow-y: auto;
  padding: 16px 20px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  background: #f5f7fa;
}

.cs-message-list::-webkit-scrollbar {
  width: 4px;
}

.cs-message-list::-webkit-scrollbar-track {
  background: transparent;
}

.cs-message-list::-webkit-scrollbar-thumb {
  background: #dcdfe6;
  border-radius: 2px;
}
</style>