| | |
| | | <!-- #region ====================== 过程输出 ======================--> |
| | | <el-steps v-show="item.stepIsShow" class="mt-3" direction="vertical" :active="activeStep"> |
| | | <el-step |
| | | :key="`template-${index}`" |
| | | v-for="(subItem, index) in item.stepList" |
| | | :title="subItem.title" |
| | | :status="stepEnumMap[subItem.status]" |
| | |
| | | |
| | | <template #description v-if="subItem?.subStep?.length > 0"> |
| | | <div class="my-1 flex flex-col gap-1 text-[14px]"> |
| | | <component |
| | | <div |
| | | :key="`${item.historyId}-${index + 1}-${multiChatIndex + 1}`" |
| | | v-for="(multiChatItem, multiChatIndex) in subItem.subStep" |
| | | :order="`${index + 1}-${multiChatIndex + 1}`" |
| | | :item="multiChatItem" |
| | | :is="multiChatTypeMapCom[multiChatItem.type]" |
| | | :disabled=" |
| | | !(index + 1 === item.stepList.length && isTalking && msgIndex === computedMessageList.length - 1) |
| | | " |
| | | /> |
| | | > |
| | | <component |
| | | v-if="multiChatItem.type === MultiChatType.Select" |
| | | :order="`${index + 1}-${multiChatIndex + 1}`" |
| | | :item="multiChatItem" |
| | | :is="multiChatTypeMapCom[multiChatItem.type]" |
| | | :disabled=" |
| | | !( |
| | | index + 1 === item.stepList.length && |
| | | isTalking && |
| | | msgIndex === computedMessageList.length - 1 |
| | | ) |
| | | " |
| | | /> |
| | | <component |
| | | v-else-if="multiChatItem.type === MultiChatType.Result" |
| | | :is="answerTypeMapCom['summary']" |
| | | :data="multiChatItem.data.content.values" |
| | | :originData="multiChatItem.data" |
| | | /> |
| | | <div v-else-if="multiChatItem.type === MultiChatType.Summary" class="ml-4 mt-5 pb-10"> |
| | | <div class="text-gray-600 mb-5">你可以继续问我:</div> |
| | | <div class="space-y-2 inline-flex flex-col"> |
| | | <div |
| | | v-for="item in multiChatItem.data.content.askMoreList" |
| | | :key="item.history_id" |
| | | class="bg-white p-3 hover:bg-[#c5e0ff] hover:text-[#1c86ff] cursor-pointer rounded-lg" |
| | | @click="askMoreClick(item)" |
| | | > |
| | | {{ item.question }} |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | </el-step> |
| | |
| | | </div> |
| | | <!-- #endregion --> |
| | | |
| | | <!-- #region ====================== 用户操作按钮 ======================--> |
| | | <div |
| | | v-if="item.role === RoleEnum.user && item.content?.values && !isSharePage && !isShareCheck" |
| | | 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(item)" |
| | | /> |
| | | </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(item)" |
| | | /> |
| | | </div> |
| | | </el-tooltip> |
| | | <el-tooltip effect="dark" content="分享" placement="top"> |
| | | <div class="flex items-center justify-center size-[15px]"> |
| | | <i |
| | | :class="{ 'text-[#0284ff]': item.state === AnswerState.Unlike }" |
| | | class="p-2 ywifont ywicon-fenxiang cursor-pointer hover:text-[#0284ff] !text-[15px] hover:!text-[18px]" |
| | | @click="shareClick(item)" |
| | | /> |
| | | </div> |
| | | </el-tooltip> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- #endregion --> |
| | | <!-- #region ====================== 消息内容 ======================--> |
| | | <template v-if="item.content?.values"> |
| | | <!-- #region ====================== 报错信息 ======================--> |
| | |
| | | import axios from 'axios'; |
| | | import { findLast, orderBy } from 'lodash-es'; |
| | | import moment from 'moment'; |
| | | import QRCode from 'qrcodejs2-fixes'; |
| | | import { computed, nextTick, onActivated, onMounted, ref } from 'vue'; |
| | | import { computed, onActivated, onMounted, ref } from 'vue'; |
| | | import useClipboard from 'vue-clipboard3'; |
| | | import { loadAmisSource } from '../amis/load'; |
| | | import FeedbackPanel from './components/FeedbackPanel.vue'; |
| | |
| | | import { convertProcessItem, useScrollLoad } from './hooks/useScrollLoad'; |
| | | import { useScrollToBottom } from './hooks/useScrollToBottom'; |
| | | import type { ChatContent, StepItem } from './model/types'; |
| | | import { AnswerState, AnswerType, RoleEnum, answerTypeMapCom, roleImageMap, stepEnumMap, type ChatMessage } from './model/types'; |
| | | import { extCallQuery, questionStreamByPost, shareChatHistoryByPost } from '/@/api/ai/chat'; |
| | | import { |
| | | AnswerState, |
| | | AnswerType, |
| | | MultiChatType, |
| | | 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 { SHARE_URL } from '/@/constants'; |
| | | import { Logger } from '/@/model/logger/Logger'; |
| | | |
| | | import { ElMessage } from 'element-plus'; |
| | | import ShareLinkDlg from './components/shareLink/index.vue'; |
| | | import UserMsg from './user/index.vue'; |
| | | import { multiChatTypeMapCom } from '/@/components/chat/chatComponents/multiChat'; |
| | | import router from '/@/router'; |
| | | import { |
| | | activeChatRoom, |
| | |
| | | isSharePage, |
| | | roomConfig, |
| | | } from '/@/stores/chatRoom'; |
| | | import UserMsg from './user/index.vue'; |
| | | import { multiChatTypeMapCom } from '/@/components/chat/chatComponents/multiChat'; |
| | | import emitter from '/@/utils/mitt'; |
| | | import { ErrorCode } from '/@/utils/request'; |
| | | import { toMyFixed } from '/@/utils/util'; |
| | | import { ElMessage } from 'element-plus'; |
| | | import { useCompRef } from '/@/utils/types'; |
| | | import { toMyFixed } from '/@/utils/util'; |
| | | const chatWidth = '75%'; |
| | | const voicePageIsShow = ref(false); |
| | | let isTalking = ref(false); |
| | |
| | | const computedMessageList = computed(() => { |
| | | return messageList.value.filter((v) => !!v); |
| | | }); |
| | | |
| | | const parseExtraContent = (res) => { |
| | | if (!res) return {}; |
| | | const askMoreList = orderBy(res.context_history, [(item) => Number(item.radio)], ['desc']); |
| | | const errCode = res?.err_code; |
| | | const errMsg = res?.json_msg; |
| | | const origin = res; |
| | | |
| | | return { |
| | | askMoreList, |
| | | errCode, |
| | | errMsg, |
| | | origin, |
| | | }; |
| | | }; |
| | | |
| | | const parseContent = (res, reportIsShow = false) => { |
| | | if (!res) return null; |
| | |
| | | // clearQueryProcess(); |
| | | // queryProcess(); |
| | | resetStep(); |
| | | let res = null; |
| | | let lastTimestamp = new Date().getTime(); |
| | | const resultP = new Promise(async (resolve, reject) => { |
| | | questionRes = {}; |
| | | |
| | | const resultP = new Promise((resolve, reject) => { |
| | | const currentSource = axios.CancelToken.source(); |
| | | lastAxiosSource = currentSource; |
| | | await questionStreamByPost( |
| | | questionStreamByPost( |
| | | params, |
| | | (chunkRes) => { |
| | | Logger.info('chunk response:\n\n' + JSON.stringify(chunkRes)); |
| | | if (chunkRes.mode === 'result') { |
| | | res = chunkRes.value; |
| | | const res = chunkRes.value; |
| | | questionRes = res; |
| | | resolve(res); |
| | | chunkRes.value = '准备数据分析'; |
| | | |
| | | // 将 summary 添加到 subStep 中 |
| | | // if (chunkRes.value?.summary) { |
| | | // const stepList = computedMessageList.value.at(-1).stepList; |
| | | // const lastStepItem = stepList.at(-1); |
| | | // if (!lastStepItem.subStep) { |
| | | // lastStepItem.subStep = []; |
| | | // } |
| | | // lastStepItem.subStep.push({ |
| | | // type: MultiChatType.Result, // 新增一个 summary 类型 |
| | | // data: { content: parseContent(chunkRes.value) }, |
| | | // }); |
| | | // scrollToBottom(); |
| | | // } |
| | | // return; |
| | | } |
| | | |
| | | if (chunkRes.mode === 'summary') { |
| | | const lastMsg = computedMessageList.value.at(-1); |
| | | const extraContent = parseExtraContent(chunkRes.value); |
| | | |
| | | // 此对话已经假如到对话列表 |
| | | if (lastMsg.content && extraContent) { |
| | | for (const key in extraContent) { |
| | | if (Object.prototype.hasOwnProperty.call(extraContent, key)) { |
| | | const value = extraContent[key]; |
| | | if (!lastMsg.content[key] || (Array.isArray(lastMsg.content[key]) && lastMsg.content[key].length === 0)) { |
| | | lastMsg.content[key] = value; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 此对话还未假如到对话列表 |
| | | if (!lastMsg.content && questionRes) { |
| | | questionRes = { |
| | | ...questionRes, |
| | | ...chunkRes.value, |
| | | }; |
| | | } |
| | | // computedMessageList.value[computedMessageList.value.length - 1] = finalMsg; |
| | | scrollToBottom(); |
| | | // chunkRes.value = '你可以继续问我'; |
| | | return; |
| | | } |
| | | |
| | | if (chunkRes.mode === 'conclusion') { |
| | |
| | | }); |
| | | }); |
| | | |
| | | questionRes = await resultP; |
| | | await resultP; |
| | | // isTalking.value = false; |
| | | |
| | | const content = parseContent(res, true); |
| | | const content = parseContent(questionRes, true); |
| | | return content; |
| | | }; |
| | | |
| | |
| | | } else { |
| | | cb?.(resMsgContent); |
| | | } |
| | | userItem.historyId = questionRes.history_id; |
| | | userItem.historyId = questionRes?.history_id; |
| | | userItem.content.values = questionRes?.question ?? userItem.content.values; |
| | | assistantItem.historyId = questionRes.history_id; |
| | | assistantItem.historyId = questionRes?.history_id; |
| | | assistantItem.sectionAId = finalCalcSectionAId; |
| | | appendLastMessageContent(resMsgContent); |
| | | setTimeout(() => { |
| | |
| | | scrollToBottom(); |
| | | }, 300); |
| | | } catch (error: any) { |
| | | console.log("🚀 ~ error:", error) |
| | | // appendLastMessageContent({ |
| | | // type: AnswerType.Text, |
| | | // values: '发生错误!', |