110 lines
3.4 KiB
Vue
110 lines
3.4 KiB
Vue
<script setup lang="ts">
|
|
import type {PropType} from "vue";
|
|
import type {ChatMessage} from "~/typings/llm";
|
|
import MessageResponding from "~/components/Icon/MessageResponding.vue";
|
|
|
|
const props = defineProps({
|
|
message: {
|
|
type: Object as PropType<ChatMessage>,
|
|
required: true,
|
|
}
|
|
})
|
|
|
|
const dayjs = useDayjs()
|
|
|
|
const message_place_end = computed(() => props.message?.role !== 'assistant')
|
|
const message_avatar = computed(() => {
|
|
switch (props.message?.role) {
|
|
case 'user':
|
|
return 'i-fluent-emoji-slightly-smiling-face'
|
|
case 'assistant':
|
|
return 'i-fluent-emoji-robot'
|
|
case 'system':
|
|
return 'i-fluent-emoji-receipt'
|
|
}
|
|
})
|
|
const message_background = computed(() => {
|
|
if (props.message?.interrupted) {
|
|
return 'bg-red-200/50 dark:bg-red-800/20 border-red-300 dark:!border-red-500/50'
|
|
}
|
|
switch (props.message?.role) {
|
|
case 'user':
|
|
return 'bg-primary-100 dark:bg-primary-800'
|
|
case 'assistant':
|
|
case 'system':
|
|
return 'bg-neutral-100 dark:bg-neutral-800'
|
|
}
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<div class="chat" :class="{'justify-end': message_place_end}">
|
|
<div class="chat-inside" :class="{'items-end': message_place_end}">
|
|
<div class="chat-inside-avatar">
|
|
<Icon :name="message_avatar" class="text-lg"/>
|
|
</div>
|
|
<div class="flex flex-col" :class="{'items-end': message_place_end}">
|
|
<Transition mode="out-in" name="message-content-change">
|
|
<div
|
|
class="chat-inside-content relative"
|
|
:class="message_background"
|
|
:key="message.content"
|
|
>
|
|
<div v-if="message.content">
|
|
<!-- <span v-if="message.interrupted">-->
|
|
<!-- <svg class="inline -mt-0.5 opacity-80" xmlns="http://www.w3.org/2000/svg" width="1.2em"-->
|
|
<!-- height="1.2em" viewBox="0 0 24 24">-->
|
|
<!-- <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"-->
|
|
<!-- d="M12 9v4m-1.637-9.409L2.257 17.125a1.914 1.914 0 0 0 1.636 2.871h16.214a1.914 1.914 0 0 0 1.636-2.87L13.637 3.59a1.914 1.914 0 0 0-3.274 0zM12 16h.01"/>-->
|
|
<!-- </svg>-->
|
|
<!-- {{ message.content }}-->
|
|
<!-- </span>-->
|
|
<Markdown :source="message.content"/>
|
|
</div>
|
|
<span v-else>
|
|
<MessageResponding class="text-xl text-neutral-500 dark:text-neutral-300 mx-2"/>
|
|
</span>
|
|
</div>
|
|
</Transition>
|
|
<div v-if="message.create_at" class="chat-inside-extra">
|
|
{{ dayjs(message.create_at * 1000).format('YYYY-MM-DD HH:mm:ss') }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style lang="scss" scoped>
|
|
.chat {
|
|
@apply w-full flex;
|
|
|
|
&-inside {
|
|
@apply w-fit flex flex-col gap-2;
|
|
@apply md:max-w-[80%];
|
|
|
|
&-avatar {
|
|
@apply w-8 h-8 flex justify-center items-center rounded-xl;
|
|
@apply bg-white border shadow-card;
|
|
@apply dark:bg-neutral-800 dark:border-neutral-700;
|
|
}
|
|
|
|
&-content {
|
|
@apply px-2 py-2.5 rounded-xl text-sm w-fit;
|
|
@apply border dark:border-neutral-700;
|
|
}
|
|
|
|
&-extra {
|
|
@apply px-1 text-xs text-neutral-300 dark:text-neutral-700;
|
|
}
|
|
}
|
|
}
|
|
|
|
.message-content-change-enter-active,
|
|
.message-content-change-leave-active {
|
|
@apply transition-all duration-300 overflow-hidden;
|
|
}
|
|
|
|
.message-content-change-enter-from {
|
|
@apply opacity-0 translate-y-4;
|
|
}
|
|
</style> |