95 lines
2.3 KiB
Vue
95 lines
2.3 KiB
Vue
|
|
<template>
|
|||
|
|
<div class="flex flex-col w-full h-full bg-base-200 overflow-hidden">
|
|||
|
|
<!-- 消息气泡列表 -->
|
|||
|
|
<div ref="bubbleListRef" class="flex-1 overflow-y-auto p-4 ">
|
|||
|
|
<!-- 空状态 -->
|
|||
|
|
<div v-if="bubbles.length === 0" class="flex items-center justify-center h-full">
|
|||
|
|
<p class="text-base-content opacity-50">暂无消息,发送第一条消息开始对话吧~</p>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 消息列表 -->
|
|||
|
|
<template v-else>
|
|||
|
|
<ChatBubble v-for="bubble in bubbles" :key="bubble.id" :content="bubble.content" :is-self="bubble.isSelf"
|
|||
|
|
:time="bubble.time" :avatar="getAvatar(bubble.isSelf)" />
|
|||
|
|
</template>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 输入框区域 -->
|
|||
|
|
<ChatInput @send="handleSend" />
|
|||
|
|
</div>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script setup lang="ts">
|
|||
|
|
import { ref, watch } from 'vue'
|
|||
|
|
import ChatBubble from './ChatBubble.vue'
|
|||
|
|
import ChatInput from './ChatInput.vue'
|
|||
|
|
import type { ChatBubble as ChatBubbleType } from '@/composables/useChat'
|
|||
|
|
|
|||
|
|
const props = defineProps<{
|
|||
|
|
// 消息气泡列表
|
|||
|
|
bubbles: ChatBubbleType[]
|
|||
|
|
}>()
|
|||
|
|
|
|||
|
|
const emit = defineEmits<{
|
|||
|
|
send: [content: string]
|
|||
|
|
}>()
|
|||
|
|
|
|||
|
|
const bubbleListRef = ref<HTMLDivElement | null>(null)
|
|||
|
|
|
|||
|
|
// 获取头像(根据是否为自己发送的消息)
|
|||
|
|
const getAvatar = (isSelf: boolean): string => {
|
|||
|
|
return isSelf
|
|||
|
|
? 'https://api.dicebear.com/7.x/avataaars/svg?seed=User'
|
|||
|
|
: 'https://api.dicebear.com/7.x/avataaars/svg?seed=Assistant'
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 滚动到底部
|
|||
|
|
const scrollToBottom = () => {
|
|||
|
|
const el = bubbleListRef.value
|
|||
|
|
if (el) {
|
|||
|
|
// 使用平滑滚动
|
|||
|
|
el.scrollTo({
|
|||
|
|
top: el.scrollHeight,
|
|||
|
|
behavior: 'smooth'
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 监听消息变化,自动滚动到底部
|
|||
|
|
watch(
|
|||
|
|
() => props.bubbles.length,
|
|||
|
|
() => {
|
|||
|
|
// 下一帧滚动,确保 DOM 已更新
|
|||
|
|
setTimeout(() => {
|
|||
|
|
scrollToBottom()
|
|||
|
|
}, 0)
|
|||
|
|
},
|
|||
|
|
{ immediate: true }
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// 处理发送消息
|
|||
|
|
const handleSend = (content: string) => {
|
|||
|
|
emit('send', content)
|
|||
|
|
}
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<style scoped>
|
|||
|
|
/* 自定义滚动条样式 */
|
|||
|
|
.overflow-y-auto::-webkit-scrollbar {
|
|||
|
|
width: 6px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.overflow-y-auto::-webkit-scrollbar-track {
|
|||
|
|
background: #f1f1f1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.overflow-y-auto::-webkit-scrollbar-thumb {
|
|||
|
|
background: #c1c1c1;
|
|||
|
|
border-radius: 3px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.overflow-y-auto::-webkit-scrollbar-thumb:hover {
|
|||
|
|
background: #a8a8a8;
|
|||
|
|
}
|
|||
|
|
</style>
|