154 lines
3.6 KiB
Vue
154 lines
3.6 KiB
Vue
|
|
<script setup lang="ts">
|
||
|
|
import type { ChatMessage } from '@/types/chat'
|
||
|
|
import { useScroll } from '@vueuse/core'
|
||
|
|
import { isDark, toggleDark } from '@/composables/dark'
|
||
|
|
|
||
|
|
const { t } = useI18n()
|
||
|
|
|
||
|
|
// Sample chat data for demonstration
|
||
|
|
const messages = ref<ChatMessage[]>([
|
||
|
|
{
|
||
|
|
id: 1,
|
||
|
|
content: 'Hello! I\'m your AI assistant. How can I help you today?',
|
||
|
|
sender: 'ai',
|
||
|
|
timestamp: new Date('2025-12-17T10:00:00'),
|
||
|
|
status: 'sent',
|
||
|
|
},
|
||
|
|
{
|
||
|
|
id: 2,
|
||
|
|
content: 'Can you explain how to use Vue 3 with TypeScript?',
|
||
|
|
sender: 'user',
|
||
|
|
timestamp: new Date('2025-12-17T10:01:00'),
|
||
|
|
status: 'sent',
|
||
|
|
},
|
||
|
|
{
|
||
|
|
id: 3,
|
||
|
|
content: 'Vue 3 with TypeScript provides excellent type safety and developer experience. You can use the Composition API with typed refs and reactive objects.',
|
||
|
|
sender: 'ai',
|
||
|
|
timestamp: new Date('2025-12-17T10:02:00'),
|
||
|
|
status: 'sent',
|
||
|
|
},
|
||
|
|
])
|
||
|
|
|
||
|
|
const inputText = ref('')
|
||
|
|
const isLoading = ref(false)
|
||
|
|
const messagesContainer = ref<HTMLElement>()
|
||
|
|
|
||
|
|
const { arrivedState } = useScroll(messagesContainer, {
|
||
|
|
offset: { bottom: 100 },
|
||
|
|
})
|
||
|
|
|
||
|
|
// Auto-scroll to bottom when new messages arrive
|
||
|
|
watch(messages, () => {
|
||
|
|
nextTick(() => {
|
||
|
|
if (messagesContainer.value && arrivedState.bottom) {
|
||
|
|
messagesContainer.value.scrollTop = messagesContainer.value.scrollHeight
|
||
|
|
}
|
||
|
|
})
|
||
|
|
}, { deep: true })
|
||
|
|
|
||
|
|
function handleSend(text: string) {
|
||
|
|
if (!text.trim()) return
|
||
|
|
|
||
|
|
// Add user message
|
||
|
|
const userMessage: ChatMessage = {
|
||
|
|
id: Date.now(),
|
||
|
|
content: text,
|
||
|
|
sender: 'user',
|
||
|
|
timestamp: new Date(),
|
||
|
|
status: 'sending',
|
||
|
|
}
|
||
|
|
messages.value.push(userMessage)
|
||
|
|
|
||
|
|
// Clear input
|
||
|
|
inputText.value = ''
|
||
|
|
|
||
|
|
// Simulate AI response after delay
|
||
|
|
isLoading.value = true
|
||
|
|
setTimeout(() => {
|
||
|
|
userMessage.status = 'sent'
|
||
|
|
|
||
|
|
const aiMessage: ChatMessage = {
|
||
|
|
id: Date.now() + 1,
|
||
|
|
content: `I received your message: "${text}". This is a simulated response.`,
|
||
|
|
sender: 'ai',
|
||
|
|
timestamp: new Date(),
|
||
|
|
status: 'sent',
|
||
|
|
}
|
||
|
|
messages.value.push(aiMessage)
|
||
|
|
isLoading.value = false
|
||
|
|
}, 1000)
|
||
|
|
}
|
||
|
|
|
||
|
|
function handleKeydown(e: KeyboardEvent) {
|
||
|
|
// You can add additional keyboard shortcuts here
|
||
|
|
console.log('Keydown event:', e.key)
|
||
|
|
}
|
||
|
|
</script>
|
||
|
|
|
||
|
|
<template>
|
||
|
|
<div class="h-screen flex flex-col bg-gray-50 dark:bg-gray-950">
|
||
|
|
<!-- Header -->
|
||
|
|
<van-nav-bar
|
||
|
|
:title="t('llmChat.title', 'AI Chat')"
|
||
|
|
fixed
|
||
|
|
placeholder
|
||
|
|
safe-area-inset-top
|
||
|
|
>
|
||
|
|
<template #right>
|
||
|
|
<van-icon
|
||
|
|
:name="isDark ? 'sun-o' : 'moon-o'"
|
||
|
|
class="text-lg"
|
||
|
|
@click="toggleDark()"
|
||
|
|
/>
|
||
|
|
</template>
|
||
|
|
</van-nav-bar>
|
||
|
|
|
||
|
|
<!-- Messages container -->
|
||
|
|
<van-list
|
||
|
|
ref="messagesContainer"
|
||
|
|
class="flex-1 p-4 pt-16 pb-24"
|
||
|
|
:finished="true"
|
||
|
|
:loading="false"
|
||
|
|
finished-text=""
|
||
|
|
loading-text=""
|
||
|
|
>
|
||
|
|
<van-empty
|
||
|
|
v-if="messages.length === 0"
|
||
|
|
description="No messages yet. Start a conversation!"
|
||
|
|
class="mt-20"
|
||
|
|
/>
|
||
|
|
|
||
|
|
<div v-else>
|
||
|
|
<ChatBubble
|
||
|
|
v-for="message in messages"
|
||
|
|
:key="message.id"
|
||
|
|
:message="message"
|
||
|
|
:show-timestamp="true"
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
</van-list>
|
||
|
|
|
||
|
|
<!-- Input area -->
|
||
|
|
<div class="fixed bottom-0 left-0 right-0 z-10">
|
||
|
|
<ChatInput
|
||
|
|
v-model="inputText"
|
||
|
|
:disabled="isLoading"
|
||
|
|
:loading="isLoading"
|
||
|
|
placeholder="Type your message..."
|
||
|
|
@send="handleSend"
|
||
|
|
@keydown="handleKeydown"
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</template>
|
||
|
|
|
||
|
|
<route lang="json5">
|
||
|
|
{
|
||
|
|
name: 'LLMChat',
|
||
|
|
meta: {
|
||
|
|
title: 'AI Chat',
|
||
|
|
requiresAuth: false
|
||
|
|
}
|
||
|
|
}
|
||
|
|
</route>
|