| | |
| | | <div |
| | | class="group flex px-4 py-6 hover:bg-slate-100 rounded-lg relative" |
| | | :class="{ 'flex-row-reverse': item.role === RoleEnum.user }" |
| | | v-for="(item, index) of computedMessageList" |
| | | v-for="(item, msgIndex) of computedMessageList" |
| | | :key="`${item.historyId}_${item.role}`" |
| | | > |
| | | <div class="absolute top-0 left-[72px] text-[#8d8e99]">{{ item?.createTime }}</div> |
| | |
| | | :style="{ backgroundColor: item.role === RoleEnum.user ? 'rgb(197 224 255)' : 'white' }" |
| | | > |
| | | <div class="flex flex-col" v-if="item?.stepList?.length > 0"> |
| | | <div |
| | | @click="toggleStepList(item)" |
| | | class="cursor-pointer border border-gray-300 border-solid w-fit px-2 flex items-center space-x-2 rounded-lg mb-3 hover:bg-gray-100 active:bg-gray-200" |
| | | > |
| | | <span> |
| | | {{ toggleStepLabel(item) }} |
| | | </span> |
| | | <span class="ywifont" :class="{ 'ywicon-unfold': !item.stepIsShow, 'ywicon-fold': item.stepIsShow }"></span> |
| | | <div class="flex items-center mb-3"> |
| | | <span class="mr-2">意图分析:</span> |
| | | <div |
| | | @click="toggleStepList(item)" |
| | | class="cursor-pointer border border-gray-300 border-solid w-fit px-2 flex items-center space-x-2 rounded-lg hover:bg-gray-100 active:bg-gray-200" |
| | | > |
| | | <span> |
| | | {{ toggleStepLabel(item) }} |
| | | </span> |
| | | <span class="ywifont" :class="{ 'ywicon-unfold': !item.stepIsShow, 'ywicon-fold': item.stepIsShow }"></span> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 过程输出 --> |
| | |
| | | :title="subItem.title" |
| | | :status="stepEnumMap[subItem.status]" |
| | | > |
| | | <!-- <template #icon> |
| | | <span v-if="subItem.status === StepEnum.Loading" class="ywifont ywicon-loading animate-spin"></span> |
| | | <span v-else class="ywifont ywicon-loading1 animate-spin"></span> |
| | | </template> --> |
| | | <template |
| | | #icon |
| | | v-if="index + 1 === item.stepList.length && isTalking && msgIndex === computedMessageList.length - 1" |
| | | > |
| | | <span class="ywifont ywicon-loading1 animate-spin !text-[24px]"></span> |
| | | </template> |
| | | <template #title> |
| | | <span class="text-sm">{{ subItem.title }}</span> |
| | | </template> |
| | |
| | | </div> |
| | | </div> |
| | | <template v-else> |
| | | <el-popover placement="bottom-start" trigger="hover" :popper-style="{ minWidth: '70px' }" :width="70"> |
| | | <template #default> |
| | | <div class="action" v-if="item.role === RoleEnum.user"> |
| | | <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]" |
| | | @click="copyUserClick(item)" |
| | | /> |
| | | </div> |
| | | <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]" |
| | | @click="setCommonQuestionClick(item)" |
| | | /> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | <template #reference> |
| | | <component |
| | | :is="answerTypeMapCom[item.content.type]" |
| | | v-if="item.role === RoleEnum.user" |
| | | :data="item.content.values" |
| | | :originData="item" |
| | | /> |
| | | </template> |
| | | </el-popover> |
| | | <component |
| | | :is="answerTypeMapCom[item.content.type]" |
| | | v-if="item.role !== RoleEnum.user" |
| | | :data="item.content.values" |
| | | :originData="item" |
| | | /> |
| | | <component :is="answerTypeMapCom[item.content.type]" :data="item.content.values" :originData="item" /> |
| | | <div |
| | | v-if="item.role === RoleEnum.assistant && item.content.origin?.ext_call_list" |
| | | class="flex font-bold items-center mt-6" |
| | |
| | | </template> |
| | | </div> |
| | | <!-- 操作 --> |
| | | <div |
| | | v-if="item.role === RoleEnum.user && item.content?.values" |
| | | class="absolute flex items-center right-0 mr-4 space-x-2" |
| | | > |
| | | <!-- <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(item)" |
| | | /> |
| | | </div> --> |
| | | <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(item)" |
| | | /> |
| | | </div> |
| | | </div> |
| | | <div |
| | | v-if="item.role === RoleEnum.assistant && item.content?.values" |
| | | class="absolute flex items-center right-0 mr-4 mt-2 space-x-2" |
| | |
| | | @showUpChatClick="showUpChatClick" |
| | | @showDownChatClick="showDownChatClick" |
| | | :style="{ width: chatWidth }" |
| | | :setCommonQuestionInfo="setCommonQuestionInfo" |
| | | ></PlayBar> |
| | | </div> |
| | | </div> |
| | |
| | | <script setup lang="ts"> |
| | | import _ from 'lodash'; |
| | | import moment from 'moment'; |
| | | import { v4 as uuidv4 } from 'uuid'; |
| | | import { computed, onMounted, ref } from 'vue'; |
| | | import FeedbackPanel from './components/FeedbackPanel.vue'; |
| | | import Loding from './components/Loding.vue'; |
| | | import { useAssistantContentOpt } from './hooks/useAssistantContentOpt'; |
| | | import { useQueryProcess } from './hooks/useQueryProcess'; |
| | | import { useScrollLoad } from './hooks/useScrollLoad'; |
| | | import { convertProcessItem, useScrollLoad } from './hooks/useScrollLoad'; |
| | | import { useScrollToBottom } from './hooks/useScrollToBottom'; |
| | | import type { ChatContent } from './model/types'; |
| | | import { AnswerState, AnswerType, RoleEnum, answerTypeMapCom, roleImageMap, type ChatMessage } from './model/types'; |
| | | import { QuestionAi, extCallQuery, questionStreamByPost } from '/@/api/ai/chat'; |
| | | import type { ChatContent, StepItem } from './model/types'; |
| | | import { AnswerState, AnswerType, RoleEnum, answerTypeMapCom, roleImageMap, stepEnumMap, type ChatMessage } from './model/types'; |
| | | import { extCallQuery, questionStreamByPost } from '/@/api/ai/chat'; |
| | | import PlayBar from '/@/components/chat/components/playBar/PlayBar.vue'; |
| | | import CustomDrawer from '/@/components/drawer/CustomDrawer.vue'; |
| | | import { Logger } from '/@/model/logger/Logger'; |
| | | import router from '/@/router'; |
| | | import { |
| | | activeChatRoom, |
| | |
| | | let questionRes = null; |
| | | |
| | | let finalCalcSectionAId = null; |
| | | const questionAi = async (text, assistantMsg: ChatMessage) => { |
| | | // processId.value = uuidv4(); |
| | | const questionAi = async (text) => { |
| | | let judgeParams = null; |
| | | if (!preQuestion.value) { |
| | | // const aiContent = computedMessageList.value.filter((item) => item.role === RoleEnum.assistant); |
| | |
| | | resetStep(); |
| | | let res = null; |
| | | await questionStreamByPost(params, (chunkRes) => { |
| | | Logger.info('chunk response:\n\n' + JSON.stringify(chunkRes)); |
| | | if (chunkRes.mode === 'result') { |
| | | res = chunkRes.value; |
| | | } else { |
| | |
| | | questionRes = extRes; |
| | | resMsgContent = parseContent(extRes); |
| | | } else { |
| | | resMsgContent = await questionAi(content.values, assistantItem); |
| | | resMsgContent = await questionAi(content.values); |
| | | } |
| | | nextUserMsgEndIndex.value++; |
| | | if (isNewChat) { |
| | |
| | | //显示上一条消息 |
| | | const showUpChatClick = () => { |
| | | if (computedMessageList.value.length === 0) return; |
| | | if (currentIndex.value === null) { |
| | | currentIndex.value = history_data.value.length - 1; |
| | | if (currentIndex.value == 0) { |
| | | messageContent.value.values = history_data.value[currentIndex.value].content.values; |
| | | return; |
| | | } else { |
| | | currentIndex.value = (currentIndex.value + history_data.value.length - 1) % history_data.value.length; |
| | | } |
| | |
| | | //显示下一条消息 |
| | | const showDownChatClick = () => { |
| | | if (computedMessageList.value.length === 0) return; |
| | | if (currentIndex.value == history_data.value.length - 1) { |
| | | messageContent.value.values = history_data.value[currentIndex.value].content.values; |
| | | return; |
| | | } |
| | | if (currentIndex.value === null) { |
| | | currentIndex.value = 0; |
| | | } else { |
| | |
| | | }; |
| | | //#endregion |
| | | //#region ====================== 用户询问的问题设置为常用语 ====================== |
| | | const setCommonQuestionInfo = ref({}); |
| | | //用户复制问题 |
| | | const copyUserClick = () => {}; |
| | | //用户问题设置为常用语 |
| | | const setCommonQuestionClick = () => {}; |
| | | const setCommonQuestionClick = (item) => { |
| | | setCommonQuestionInfo.value = item; |
| | | }; |
| | | //#endregion |
| | | </script> |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | .el-step.is-horizontal.stepActive { |
| | | .el-step__head.is-finish { |
| | | .el-step__line { |
| | | // 当前步骤数横线样式设置 |
| | | .el-step__line-inner { |
| | | width: 50% !important; |
| | | border-width: 1px !important; |
| | | } |
| | | } |
| | | |
| | | // 当前步骤数圆圈样式设置 |
| | | .el-step__icon.is-text { |
| | | // background: #409eff; |
| | | color: #fff; |
| | | } |
| | | } |
| | | :deep(.el-step__icon.is-text) { |
| | | --radius-size: 24px; |
| | | width: var(--radius-size); |
| | | height: var((--radius-size)); |
| | | } |
| | | |
| | | :deep(.el-step__icon-inner) { |