| | |
| | | <div class="flex h-full"> |
| | | <div class="flex flex-col h-full flex-auto relative"> |
| | | <!-- 消息列表区域 --> |
| | | <div v-resize="updateChatWidth" ref="chatListDom" class="relative h-full flex flex-col items-center overflow-y-auto" style="height: calc(100% - 50px)"> |
| | | <div v-resize="updateChatWidth" ref="chatListDom" class="relative h-full flex flex-col items-center overflow-y-auto flex-auto"> |
| | | <span |
| | | class="more-loading absolute text-blue-400 left-[50%] translate-x-[-50%] cursor-pointer w-10" |
| | | v-loading="moreIsLoading" |
| | |
| | | </div> |
| | | |
| | | <!-- 回到底部按钮 --> |
| | | <div class="absolute right-28 bottom-40" v-if="!isBottom"> |
| | | <div class="absolute right-28 bottom-72" v-if="!isBottom"> |
| | | <div |
| | | class="flex items-center justify-center size-[38px] cursor-pointer hover:text-[#0284ff] border rounded-full hover:bg-[#f6f7f9] shadow bg-white" |
| | | @click="scrollToBottom" |
| | |
| | | <i class="ywifont ywicon-xiangxiajiantou !text-[20px]" /> |
| | | </div> |
| | | </div> |
| | | <div |
| | | v-show="digitalHumanIsShow" |
| | | v-loading="humanIsLoading" |
| | | class="absolute right-0 bottom-6 z-[2]" |
| | | :style="{ width: digitalHumanWidth, height: `calc(${digitalHumanWidth} * 16 / 9 - 300px)`, overflow: 'hidden' }" |
| | | > |
| | | <span class="ywifont ywicon-guanbi text-[20px] cursor-pointer absolute top-2 right-8 z-[1]" @click="closeDigitalHuman"></span> |
| | | <div class="duix-container h-full w-full" :style="{ width: digitalHumanWidth, height: `calc(${digitalHumanWidth} * 16 / 9)` }"></div> |
| | | </div> |
| | | |
| | | <div v-if="!digitalHumanIsShow" class="absolute right-[24px] bottom-[100px] z-[2]"> |
| | | <el-tooltip content="数字人" placement="top"> |
| | | <button class="digital-human-button" @click="openDigitalHuman"> |
| | | <img alt="Icon" :src="iconSrc" /> |
| | | </button> |
| | | <!-- <div |
| | | class="flex items-center justify-center size-[38px] cursor-pointer hover:text-[#0284ff] border rounded-full hover:bg-[#f6f7f9] shadow bg-white" |
| | | @click="openDigitalHuman" |
| | | > |
| | | <i class="ywifont ywicon-shuziren !text-[20px]" /> |
| | | </div> --> |
| | | </el-tooltip> |
| | | </div> |
| | | |
| | | <!-- 输入区域 --> |
| | | <div class="sticky bottom-0 w-full px-6 pt-12 pb-6 bg-[rgb(247,248,250)] flex justify-center z-[1]" v-if="!isSharePage"> |
| | | <div class="w-full px-6 pb-6 bg-[rgb(247,248,250)] flex justify-center z-[1] flex-0" v-if="!isSharePage"> |
| | | <slot name="input-area" /> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="w-[30%] flex-0 flex flex-col" v-if="fileContentIsShow"> |
| | | <div class="bg-[#f9fbff] flex items-center justify-between p-4 border-b"> |
| | | <div class="text-lg font-medium">{{ fileContent.title }}</div> |
| | | <div class="cursor-pointer hover:text-[#0284ff]" @click="fileContentIsShow = false"> |
| | | <i class="ywifont ywicon-guanbi text-[20px]" /> |
| | | </div> |
| | | </div> |
| | | <span class="bg-[#edf2fb] flex-1 overflow-y-auto p-4 break-words whitespace-pre-wrap"> |
| | | {{ fileContent.content }} |
| | | </span> |
| | | </div> |
| | | |
| | | <slot name="drawer" /> |
| | |
| | | </template> |
| | | |
| | | <script setup lang="ts"> |
| | | import { ref } from 'vue'; |
| | | import { useScroll } from '../hooks/useScroll'; |
| | | import { onActivated, onDeactivated, ref } from 'vue'; |
| | | import { useChatWidth } from '../hooks/useChatWidth'; |
| | | |
| | | import { useScroll } from '../hooks/useScroll'; |
| | | import type { QuestionLifecycle } from '../types'; |
| | | import { useDigitalHuman } from './playBar/hook/useDigitalHuman'; |
| | | import { iconSrc } from './constants'; |
| | | import emitter from '/@/utils/mitt'; |
| | | const props = defineProps<{ |
| | | loading?: boolean; |
| | | moreIsLoading?: boolean; |
| | | isSharePage?: boolean; |
| | | }>(); |
| | | |
| | | const chatListDom = ref<HTMLDivElement>(); |
| | | const emit = defineEmits<{ |
| | | autoSendMessage: [string, QuestionLifecycle]; |
| | | }>(); |
| | | |
| | | const chatListDom = ref<HTMLDivElement>(); |
| | | const { openDigitalHuman, isHumanTalking, humanIsLoading, digitalHumanIsShow, closeDigitalHuman, digitalHumanWidth } = useDigitalHuman( |
| | | { |
| | | container: '.duix-container', |
| | | autoSendMessage: (question: string, lifecycleCall?: QuestionLifecycle) => { |
| | | emit('autoSendMessage', question, lifecycleCall); |
| | | }, |
| | | } |
| | | ); |
| | | const { scrollToBottom, isBottom } = useScroll({ |
| | | chatListDom, |
| | | }); |
| | | |
| | | const fileContentIsShow = ref(false); |
| | | |
| | | const fileContent = ref({ |
| | | title: '', |
| | | content: '', |
| | | }); |
| | | |
| | | const setFileContent = (data: { title: string; content: string }) => { |
| | | fileContentIsShow.value = true; |
| | | fileContent.value = data; |
| | | }; |
| | | |
| | | onActivated(() => { |
| | | emitter.on('setFileContent', setFileContent); |
| | | }); |
| | | |
| | | onDeactivated(() => { |
| | | emitter.off('setFileContent', setFileContent); |
| | | }); |
| | | |
| | | const { updateChatWidth, chatWidth } = useChatWidth(); |
| | |
| | | height: var(--loading-size); |
| | | } |
| | | } |
| | | |
| | | .digital-human-button { |
| | | width: 40px; |
| | | height: 40px; |
| | | border: none; |
| | | border-radius: 50%; |
| | | background-color: #ffffff; |
| | | cursor: pointer; |
| | | padding: 0; |
| | | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
| | | transition: all 0.3s ease; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .digital-human-button:hover { |
| | | transform: translateY(-2px); |
| | | box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); |
| | | } |
| | | |
| | | .digital-human-button:active { |
| | | transform: translateY(0); |
| | | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); |
| | | } |
| | | |
| | | .digital-human-button img { |
| | | width: 18px; |
| | | height: 18px; |
| | | object-fit: contain; |
| | | } |
| | | </style> |