Chat.vue 3.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. <script setup lang="ts">
  2. import AppLayout from "@/Layouts/AppLayout.vue"
  3. import {ArrowUpIcon, Loader2} from "lucide-vue-next"
  4. import {
  5. InputGroup,
  6. InputGroupAddon,
  7. InputGroupButton,
  8. InputGroupTextarea
  9. } from "@/Packages/Shadcn/Components/ui/input-group"
  10. import {computed, nextTick, watch} from "vue";
  11. import {useForm} from "@inertiajs/vue3";
  12. const { chat } = defineProps(['chats', 'chat'])
  13. const form = useForm({
  14. uuid: chat?.id ?? null,
  15. message: null
  16. })
  17. const submit = function () {
  18. if (!form.message) return
  19. form.post(route('chats.message'), {
  20. onSuccess: () => form.reset()
  21. })
  22. }
  23. const handleEnter = function (e) {
  24. if (e.metaKey || e.ctrlKey) {
  25. const {selectionStart, selectionEnd, value} = e.target
  26. form.message = value.slice(0, selectionStart) + '\n' + value.slice(selectionEnd)
  27. nextTick(() => {
  28. const pos = selectionStart + 1
  29. e.target.setSelectionRange(pos, pos)
  30. })
  31. } else submit()
  32. }
  33. const messages = computed(() => chat?.messages ?? [])
  34. watch(() => chat?.id, () => form.defaults('uuid', chat?.id ?? null))
  35. </script>
  36. <template>
  37. <AppLayout page-class="!py-0" :chats="chats">
  38. <div class="max-w-4xl mx-auto flex flex-col h-full">
  39. <div class="overflow-y-auto duration-500 space-y-4" :class="{'grow mt-2': messages.length !== 0}">
  40. <div v-for="message in messages">
  41. <div class="flex" v-if="message.from === 'user'">
  42. <div class="ml-auto border bg-card text-card-foreground p-2 rounded-lg max-w-[75%]"
  43. v-html="message.text"></div>
  44. </div>
  45. <div v-else>{{ message.text }}</div>
  46. </div>
  47. <!-- <div class=""></div>-->
  48. </div>
  49. <div class="my-auto pb-2" :class="{'sticky bottom-0': messages.length !== 0}">
  50. <InputGroup class="!bg-card">
  51. <InputGroupTextarea placeholder="Ask, Search or Chat..." v-model="form.message"
  52. @keydown.enter.prevent="handleEnter"/>
  53. <InputGroupAddon align="block-end">
  54. <InputGroupButton class="ml-auto cursor-pointer" variant="default"
  55. :disabled="!form.message" :loading="true"
  56. @click="() => submit()">
  57. <template v-if="!form.processing">
  58. <ArrowUpIcon class="size-5"/>
  59. <span>Отправить</span>
  60. </template>
  61. <template v-else>
  62. <Loader2 class="w-4 h-4 animate-spin"/>
  63. Отправка
  64. </template>
  65. </InputGroupButton>
  66. </InputGroupAddon>
  67. </InputGroup>
  68. </div>
  69. </div>
  70. </AppLayout>
  71. </template>
  72. <style scoped>
  73. </style>