yd-yunxing-web/src/views/ai/chat/new-chat/message/Message.vue

130 lines
3.1 KiB
Vue

<script lang="ts" setup>
import { computed, ref } from 'vue'
import { ElMessage } from 'element-plus'
import TextComponent from './TextComponent.vue'
import { copyToClip } from '@/utils/copy'
interface Props {
dateTime?: string
text?: string
inversion?: boolean
error?: boolean
loading?: boolean
}
interface Emit {
(ev: 'delete'): void
}
const props = defineProps<Props>()
const emit = defineEmits<Emit>()
const isHover = ref(false)
const textRef = ref<HTMLElement>()
const asRawText = ref(props.inversion)
const messageRef = ref<HTMLElement>()
const options = computed(() => {
const common = [
{
label: '复制',
key: 'copyText',
icon: 'DocumentCopy'
}
]
if (!props.inversion) {
common.push({
label: asRawText.value ? '预览' : '显示原文',
key: 'toggleRenderType',
icon: asRawText.value ? 'View' : 'Document'
})
}
return common
})
function handleSelect(key: 'copyText' | 'delete' | 'toggleRenderType') {
switch (key) {
case 'copyText':
handleCopy()
return
case 'toggleRenderType':
asRawText.value = !asRawText.value
return
case 'delete':
emit('delete')
}
}
async function handleCopy() {
try {
await copyToClip(props.text || '')
ElMessage.success('复制成功')
} catch {
ElMessage.error('复制失败')
}
}
</script>
<template>
<div
ref="messageRef"
:class="[{ 'flex-row-reverse': inversion }]"
class="flex w-full overflow-hidden"
>
<div
:class="[inversion ? 'ml-2' : 'mr-2']"
class="flex items-center justify-center bg-gray-200 flex-shrink-0 h-8 overflow-hidden rounded-full basis-8"
>
<SvgIcon v-if="inversion" icon="solar:user-broken" />
<SvgIcon v-else icon="mingcute:ai-line" />
</div>
<div :class="[inversion ? 'items-end' : 'items-start']" class="overflow-hidden text-sm">
<p :class="[inversion ? 'text-right' : 'text-left']" class="text-xs text-[#b4bbc4]">
{{ dateTime }}
</p>
<div
@mouseover="isHover = true"
@mouseleave="isHover = false"
:class="[inversion ? 'flex-row-reverse' : 'flex-row']"
class="flex items-end gap-1 mt-2 transition-all"
>
<TextComponent
ref="textRef"
:as-raw-text="asRawText"
:error="error"
:inversion="inversion"
:loading="loading"
:text="text"
/>
<div class="flex flex-col transition-all w-[45px]">
<div v-if="isHover" class="flex gap-1.5 flex-nowrap justify-end">
<el-tooltip
v-for="item in options"
:key="item.key"
:content="item.label"
placement="top"
>
<el-button
link
:icon="item.icon"
@click="handleSelect(item.key as any)"
class="transition text-neutral-400 hover:text-neutral-800"
/>
</el-tooltip>
</div>
</div>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
:deep(.el-button) {
padding: 4px;
height: auto;
}
</style>