<template>
|
<div class="flex flex-col py-6 gap-2">
|
<!-- #region ====================== 附件展示 ======================-->
|
<div class="flex px-4 rounded-lg relative flex-row-reverse items-center" :key="`${msg.historyId}_${msg.role}`">
|
<img class="rounded-full size-12 flex-0 ml-4 invisible" :src="roleImageMap[msg.role]" alt="" srcset="" />
|
<div class="flex-auto flex justify-end">
|
<div class="inline-flex flex-col">
|
<div class="rounded-[6px] leading-relaxed group">
|
<div v-if="msg.attachList?.length > 0" class="flex gap-3.5 w-full overflow-x-auto">
|
<div
|
v-for="(item, index) in msg.attachList"
|
:key="index"
|
class="flex items-center gap-2 bg-[#e9e9e9] px-2 py-3 rounded-lg w-[220px] relative group cursor-pointer"
|
@click="openAttachPreview(item)"
|
>
|
<template v-if="item.type === 'file'">
|
<el-image
|
:zoom-rate="1.2"
|
fit="cover"
|
class="w-[24px] rounded cursor-pointer"
|
v-if="item.model.groupType === 'image'"
|
:src="item.model.previewUrl"
|
:preview-src-list="[item.model.previewUrl]"
|
/>
|
<div v-else class="ywifont !text-[24px] flex-0" :class="[`ywicon-${item.icon}`, item.iconClass]"></div>
|
<div class="flex flex-col gap-0.5 w-full flex-auto justify-between">
|
<div class="font-bold over-ellipsis w-full">{{ item.title }}</div>
|
<div v-if="item.model.type" class="text-info text-sm over-ellipsis w-full">
|
{{ `${item.model.type ?? ''},${item.model.size ?? ''}` }}
|
</div>
|
<el-tooltip effect="dark" content="下载" placement="top">
|
<span
|
class="group-hover:visible invisible ywifont ywicon-download2 absolute right-2 cursor-pointer"
|
@click.stop="downloadAttach(item)"
|
></span>
|
</el-tooltip>
|
</div>
|
</template>
|
<template v-if="item.type === 'metric'">
|
<div class="ywifont !text-[24px] flex-0" :class="[`ywicon-${item.icon}`, item.iconClass]"></div>
|
<div class="flex flex-col gap-0.5 w-full flex-auto">
|
<div class="font-bold over-ellipsis w-full">{{ item.title }}</div>
|
<div class="text-info text-sm over-ellipsis w-full">{{ `指标,${item.model.values?.length} 条记录` }}</div>
|
</div>
|
<el-tooltip effect="dark" content="引用" placement="top">
|
<span
|
class="group-hover:visible invisible ywifont ywicon-quote absolute right-2 cursor-pointer"
|
@click.stop="quoteAttach(item)"
|
></span>
|
</el-tooltip>
|
</template>
|
<template v-if="item.type === 'table'">
|
<div class="ywifont !text-[24px] flex-0" :class="[`ywicon-${item.icon}`, item.iconClass]"></div>
|
<div class="flex flex-col gap-0.5 w-full flex-auto">
|
<div class="font-bold over-ellipsis w-full">{{ item.title }}</div>
|
<div class="text-info text-sm over-ellipsis w-full">{{ `业务表格,${item.model.values?.length} 条记录` }}</div>
|
</div>
|
<el-tooltip effect="dark" content="引用" placement="top">
|
<span
|
class="group-hover:visible invisible ywifont ywicon-quote absolute right-2 cursor-pointer"
|
@click.stop="quoteAttach(item)"
|
></span>
|
</el-tooltip>
|
</template>
|
</div>
|
</div>
|
</div>
|
</div>
|
</div>
|
</div>
|
<BusinessTablePreview :data="attachPreviewData" v-model="attachPreviewIsShow" />
|
|
<MetricValuesPreview v-model="metricPreviewIsShow" :data="metricPreviewData" />
|
|
<!-- #endregion -->
|
|
<div class="flex px-4 rounded-lg relative flex-row-reverse items-center" :key="`${msg.historyId}_${msg.role}`">
|
<img class="rounded-full size-12 flex-0 ml-4" :src="roleImageMap[msg.role]" alt="" srcset="" />
|
<div class="flex-auto flex justify-end">
|
<div class="inline-flex flex-col">
|
<div class="rounded-[6px] p-4 leading-relaxed group" :style="{ backgroundColor: 'rgb(197 224 255)' }">
|
<!-- #region ====================== 用户操作按钮 ======================-->
|
<div v-if="msg.content?.values && !isSharePage" class="absolute flex items-center bottom-0 group invisible">
|
<div class="bg-[#fff] flex items-center relative mr-4 space-x-2 flex-nowrap rounded-[6px] py-2 px-2 group-hover:visible">
|
<el-tooltip effect="dark" content="复制" placement="top">
|
<div class="flex items-center justify-center size-[20px]">
|
<i
|
class="p-2 ywifont ywicon-copy cursor-pointer hover:text-[#0284ff] font-medium !text-[15px] hover:!text-[18px]"
|
@click="copyUserClick(msg)"
|
/>
|
</div>
|
</el-tooltip>
|
<el-tooltip effect="dark" content="设为常用语" placement="top">
|
<div class="flex items-center justify-center size-[20px]">
|
<i
|
class="p-2 ywifont ywicon-cubelifangti cursor-pointer hover:text-[#0284ff] text-[#000] font-[590] !text-[15px] hover:!text-[18px]"
|
@click="setCommonQuestionClick(msg)"
|
/>
|
</div>
|
</el-tooltip>
|
<el-tooltip effect="dark" content="分享" placement="top">
|
<div class="flex items-center justify-center size-[15px]">
|
<i
|
:class="{ 'text-[#0284ff]': msg.state === AnswerState.Unlike }"
|
class="p-2 ywifont ywicon-fenxiang cursor-pointer hover:text-[#0284ff] !text-[15px] hover:!text-[18px]"
|
@click="shareClick(msg)"
|
/>
|
</div>
|
</el-tooltip>
|
</div>
|
</div>
|
|
<!-- #endregion -->
|
<!-- #region ====================== 消息内容 ======================-->
|
<template v-if="msg.content?.values">
|
<!-- #region ====================== 回答组件 ======================-->
|
<component
|
:conclusion="msg.conclusion"
|
:is="answerTypeMapCom[msg.content.type]"
|
:data="msg.content.values"
|
:originData="msg"
|
/>
|
|
<!-- #endregion -->
|
</template>
|
|
<!-- #endregion -->
|
</div>
|
</div>
|
</div>
|
</div>
|
</div>
|
</template>
|
|
<script setup lang="ts" name="UserMsg">
|
import { useClipboard } from '@vueuse/core';
|
import { ElMessage } from 'element-plus';
|
import { AnswerState, answerTypeMapCom, roleImageMap, type ChatMessage } from '../model/types';
|
import { isSharePage } from '/@/stores/chatRoom';
|
import { onActivated, ref } from 'vue';
|
import BusinessTablePreview from '../components/playBar/businessTablePreview/index.vue';
|
import { Attach } from '../components/playBar/hook/useAttach';
|
import MetricValuesPreview from '../components/playBar/metricValues/MetricValuesPreview.vue';
|
|
import emitter from '/@/utils/mitt';
|
import { downloadFileByPost } from '/@/api/file';
|
|
const emit = defineEmits<{
|
(event: 'copyMsg', msgObj: ChatMessage): void;
|
(event: 'shareClick', msgObj: ChatMessage): void;
|
(event: 'setCommonQuestion', msgObj: ChatMessage): void;
|
}>();
|
const props = defineProps({
|
/** @description 当前消息 */
|
msg: {
|
type: Object,
|
},
|
});
|
const { copy } = useClipboard();
|
|
//用户复制问题
|
const copyUserClick = (item) => {
|
const text = item.content.values;
|
copy(text);
|
ElMessage.success('复制成功');
|
};
|
//用户问题设置为常用语
|
const setCommonQuestionClick = (item) => {
|
emit('setCommonQuestion', item);
|
};
|
|
const shareClick = (msg) => {
|
emit('shareClick', msg);
|
};
|
|
//#region ====================== 指标附件预览 ======================
|
const metricPreviewIsShow = ref(false);
|
const metricPreviewData = ref<Attach>();
|
|
//#endregion
|
//#region ====================== 附件预览 ======================
|
const attachPreviewIsShow = ref(false);
|
const attachPreviewData = ref<Attach>();
|
const openAttachPreview = (item: Attach) => {
|
if (item.type === 'file') {
|
openFileContent(item);
|
} else if (item.type === 'table') {
|
attachPreviewIsShow.value = true;
|
attachPreviewData.value = item;
|
} else if (item.type === 'metric') {
|
metricPreviewIsShow.value = true;
|
metricPreviewData.value = item;
|
}
|
};
|
//#endregion
|
|
//#region ====================== 附件引用 ======================
|
const quoteAttach = (item: Attach) => {
|
emitter.emit('quoteAttach', item);
|
};
|
//#endregion
|
|
//#region ====================== 查看文件文本内容 ======================
|
const openFileContent = (item: Attach) => {
|
emitter.emit('setFileContent', {
|
title: item.title,
|
content: item.model?.file_content ?? '',
|
});
|
};
|
//#endregion
|
|
//#region ====================== 附件下载 ======================
|
const downloadAttach = async (item: Attach) => {
|
const fileId = item.model?.file_id;
|
if (!fileId) return;
|
|
const res = await downloadFileByPost({
|
file_id: fileId,
|
});
|
|
const url = window.URL.createObjectURL(res as any);
|
const link = document.createElement('a');
|
link.href = url;
|
link.download = item.title || 'download'; // Use item title or fallback name
|
document.body.appendChild(link);
|
link.click();
|
document.body.removeChild(link);
|
window.URL.revokeObjectURL(url);
|
};
|
//#endregion
|
</script>
|
<style scoped lang="scss"></style>
|