avances poderosos en ui
This commit is contained in:
83
ui/src/components/chat/CanvasChat.vue
Normal file
83
ui/src/components/chat/CanvasChat.vue
Normal file
@@ -0,0 +1,83 @@
|
||||
<script setup>
|
||||
import { ref, nextTick, onMounted, watch } from 'vue'
|
||||
import { useChat } from '@/stores/useChat'
|
||||
|
||||
const chat = useChat()
|
||||
const msg = ref('')
|
||||
const list = ref(null)
|
||||
|
||||
function scrollBottom () {
|
||||
nextTick(() => list.value?.scrollTo({ top: list.value.scrollHeight, behavior: 'smooth' }))
|
||||
}
|
||||
|
||||
function send () {
|
||||
const t = msg.value.trim()
|
||||
if (!t) return
|
||||
|
||||
if (t.startsWith('/')) chat.run(t.slice(1))
|
||||
else chat.add({ type: 'text', owner: 'yo', text: t })
|
||||
|
||||
msg.value = ''
|
||||
scrollBottom()
|
||||
}
|
||||
|
||||
function handleKey (e) {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
e.preventDefault()
|
||||
send()
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (!chat.items.length) {
|
||||
chat.add({ type: 'text', owner: 'bot', text: '¡Hola! Probá /empleados, /tareas, etc.' })
|
||||
}
|
||||
scrollBottom()
|
||||
})
|
||||
|
||||
watch(() => chat.items.length, scrollBottom)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- se adapta al contenedor flex, sin superponer la sidebar -->
|
||||
<div class="flex flex-col flex-1 min-h-0 bg-gray-50">
|
||||
<!-- historial -->
|
||||
<div ref="list" class="flex-1 min-h-0 overflow-auto p-6 space-y-4 custom-scroll">
|
||||
<template v-for="(m,i) in chat.items" :key="i">
|
||||
<!-- mensaje de texto -->
|
||||
<div :class="m.owner==='yo' ? 'flex justify-end' : 'flex justify-start'" v-if="m.type==='text'">
|
||||
<div
|
||||
class="max-w-lg rounded-lg px-4 py-2 shadow break-words"
|
||||
:class="m.owner==='yo' ? 'bg-teal-600 text-white' : 'bg-white text-gray-900'">
|
||||
{{ m.text }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- componente dinámico -->
|
||||
<component v-else :is="m.is" v-bind="m.props" />
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<!-- input -->
|
||||
<form @submit.prevent="send" class="border-t bg-white p-4 flex gap-2">
|
||||
<textarea
|
||||
v-model="msg"
|
||||
@keydown="handleKey"
|
||||
rows="1"
|
||||
placeholder="Escribí un mensaje… (Enter para enviar, Shift+Enter salto)"
|
||||
class="flex-1 resize-none rounded-lg border p-3 focus:outline-none focus:ring-2 focus:ring-teal-500 custom-scroll"
|
||||
/>
|
||||
<button type="submit" class="px-4 py-2 rounded-lg bg-teal-600 text-white hover:bg-teal-700 transition">
|
||||
➤
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.custom-scroll::-webkit-scrollbar { width: 8px; }
|
||||
.custom-scroll::-webkit-scrollbar-track { background: transparent; }
|
||||
.custom-scroll::-webkit-scrollbar-thumb { background-color: rgba(13,148,136,.35); border-radius: 4px; }
|
||||
.custom-scroll:hover::-webkit-scrollbar-thumb { background-color: rgba(13,148,136,.7); }
|
||||
.custom-scroll { scrollbar-width: thin; scrollbar-color: rgba(13,148,136,.6) transparent; }
|
||||
</style>
|
||||
Reference in New Issue
Block a user