Chat
VxChatMessage va VxChatInput ni birlashtirgan to'liq ishlaydigan suhbat namunasi. Eski xabarlarni yuklash uchun yuqoriga aylantiring.
Features
- Yuqoriga aylantirib yuklash — eski xabarlarni spinner bilan yuklash uchun yuqoriga aylantiring, aylantirish pozitsiyasini saqlaydi, tarix tugaganda "Beginning of conversation" ni ko'rsatadi
- Xabarlar yuborish — yozing va Enter ni bosing yoki yuborish tugmasini bosing
- Avtomatik guruhlash — bir xil vaqtda bir xil jo'natuvchidan ketma-ket xabarlar Telegram uslubidagi chegara radiuslari bilan guruhlanadi
- Javob — xabarga o'ng tugma bilan bosing, Reply ni bosing, kiritish panelida oldindan ko'rinishni ko'rsatadi
- Nusxa ko'chirish — o'ng tugma bilan bosing, vaqtinchalik xotiraga nusxa ko'chirish uchun Copy ni bosing
- O'chirish — o'ng tugma bilan bosing, Delete ni bosish xabarni olib tashlaydi
- Holat o'sishi — yuborilgan xabarlar sending, sent, delivered, read bosqichlari orqali animatsiyalanadi
- Avtomatik javob — 2-4 soniyadan keyin tasodifiy javob qaytadi
- Fayl biriktirmalari — yuklash uchun fayllarni kiritish paneliga tashlang, yuklab olish uchun fayllarni bosing
- Photo/File amali — qog'oz qisqichini bosing, keyin Photo yoki File mahalliy fayl tanlagichini ochadi
- Emoji tanlagich — emojilarni tanlash uchun tabassum belgisini bosing
- Ovoz tugmasi — kiritish bo'sh bo'lganda ko'rsatiladi
vue
<script setup>
import { ref, reactive, nextTick } from 'vue'
const chatRef = ref(null)
const inputText = ref('')
const replyTo = ref(null)
const messages = ref([])
const loadingOlder = ref(false)
const hasMoreHistory = ref(true)
function scrollToBottom() {
nextTick(() => {
if (chatRef.value) chatRef.value.scrollTop = chatRef.value.scrollHeight
})
}
// Scroll-to-top loading
function onScroll() {
const el = chatRef.value
if (!el || loadingOlder.value || !hasMoreHistory.value) return
if (el.scrollTop <= 40) {
loadOlderMessages()
}
}
function loadOlderMessages() {
loadingOlder.value = true
const el = chatRef.value
const prevHeight = el ? el.scrollHeight : 0
fetchHistory().then((older) => {
if (older.length) {
messages.value.unshift(...older)
// Preserve scroll position
nextTick(() => {
if (el) el.scrollTop = el.scrollHeight - prevHeight
loadingOlder.value = false
})
} else {
hasMoreHistory.value = false
loadingOlder.value = false
}
})
}
function onSend(text) {
const msg = reactive({
id: nextId++,
name: 'You',
content: text,
time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }),
sent: true,
status: 'sending',
})
messages.value.push(msg)
scrollToBottom()
setTimeout(() => { msg.status = 'sent' }, 600)
setTimeout(() => { msg.status = 'delivered' }, 1500)
setTimeout(() => { msg.status = 'read' }, 3000)
}
</script>
<template>
<div style="display: flex; flex-direction: column; height: 500px;">
<div ref="chatRef" style="flex: 1; overflow-y: auto;" @scroll="onScroll">
<!-- Loader at top -->
<div v-if="loadingOlder" class="chat-loader">
<div class="chat-loader__spinner" />
</div>
<div v-else-if="!hasMoreHistory" class="chat-history-end">
Beginning of conversation
</div>
<VxChatMessage
v-for="(msg, i) in messages"
:key="msg.id"
v-bind="msg"
:prev-time="messages[i - 1]?.time"
:prev-sent="messages[i - 1]?.sent"
:next-time="messages[i + 1]?.time"
:next-sent="messages[i + 1]?.sent"
@action="(key, item) => onAction(key, item, msg)"
/>
</div>
<VxChatInput
v-model="inputText"
variant="outline"
:reply-to="replyTo"
@send="onSend"
@cancel-reply="replyTo = null"
@drop="onDrop"
/>
</div>
</template>