From 2a2c1177eef03dc2387950c59596704a0390622e Mon Sep 17 00:00:00 2001 From: gerson <1405270578@qq.com> Date: 星期日, 30 六月 2024 17:25:31 +0800 Subject: [PATCH] chat --- src/api/ai/chat.ts | 22 +++- src/components/chat/Chat.vue | 171 +++++++++++++++++++++++++--------- src/components/chat/chatComponents/recordSetCom/RecordSetCom.vue | 2 src/components/chat/chatComponents/knowledgeCom/KnowledgeCom.vue | 32 ++++++ src/components/chat/model/types.ts | 3 src/layout/component/sidebar/waterLeftAside/asideNew.vue | 36 +++++-- src/stores/chatRoom.ts | 12 -- src/components/chat/chatComponents/summaryCom/SummaryCom.vue | 6 + src/layout/component/sidebar/waterLeftAside/types.ts | 12 +- 9 files changed, 215 insertions(+), 81 deletions(-) diff --git a/src/api/ai/chat.ts b/src/api/ai/chat.ts index 2b06a5c..e34ab01 100644 --- a/src/api/ai/chat.ts +++ b/src/api/ai/chat.ts @@ -123,21 +123,18 @@ }; -export const GetHistoryGroups = async (params, req:any = request) => { +export const GetHistoryGroups = async ( req:any = request) => { return req({ url: "/history/get_history_groups", method: "POST", - data: params, - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - }, + }); }; -export const QueryHistoryGroup = async (params, req:any = request) => { +export const QueryHistoryDetail = async (params, req:any = request) => { return req({ url: "/history/query_history_detail", method: "POST", @@ -147,3 +144,16 @@ }, }); }; + + + +export const GetHistoryAnswer= async (params, req:any = request) => { + return req({ + url: "/history/get_history_answer", + method: "POST", + data: params, + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + }); + }; \ No newline at end of file diff --git a/src/components/chat/Chat.vue b/src/components/chat/Chat.vue index ec0c7c9..20ef884 100644 --- a/src/components/chat/Chat.vue +++ b/src/components/chat/Chat.vue @@ -2,18 +2,12 @@ <div class="flex flex-col h-full"> <div class="h-full flex flex-col items-center overflow-y-auto"> <div ref="chatListDom" class="h-full w-[100ch]"> - <div - class="group flex px-4 py-4 hover:bg-slate-100 rounded-lg" - v-for="(item, index) of messageList.filter((v) => v.role !== 'system')" - :key="index" - > + <div class="group flex px-4 py-4 hover:bg-slate-100 rounded-lg" v-for="(item, index) of computedMessageList" :key="index"> <img class="rounded-full size-12 mr-4" :src="roleImageMap[item.role]" alt="" srcset="" /> <div class="flex"> <div class="relative" v-if="item.content?.values"> - <div - class="text-sm rounded-[6px] p-4 leading-relaxed max-w-[100ch] bg-white" - > + <div class="text-sm rounded-[6px] p-4 leading-relaxed max-w-[100ch] bg-white"> <component class="max-w-[100ch]" :is="answerTypeMapCom[item.content.type]" :data="item.content.values" /> </div> @@ -37,17 +31,18 @@ </template> <script setup lang="ts"> -import { nextTick, onMounted, ref, watch } from 'vue'; +import { computed, nextTick, onMounted, ref, watch } from 'vue'; import useClipboard from 'vue-clipboard3'; import Loding from './components/Loding.vue'; import { RecordSet } from './model/Record'; import type { ChatContent } from './model/types'; import { AnswerType, RoleEnum, answerTypeMapCom, roleImageMap, type ChatMessage } from './model/types'; -import { QuestionAi } from '/@/api/ai/chat'; +import { GetHistoryAnswer, QueryHistoryDetail, QuestionAi } from '/@/api/ai/chat'; import PlayBar from '/@/components/chat/components/playBar/PlayBar.vue'; import router from '/@/router'; -import { activeChatRoom } from '/@/stores/chatRoom'; +import { activeChatRoom, activeRoomId } from '/@/stores/chatRoom'; import { ElMessage } from 'element-plus'; +import { content } from 'html2canvas/dist/types/css/property-descriptors/content'; let isTalking = ref(false); let messageContent = ref<ChatContent>({ @@ -56,39 +51,77 @@ }); const chatListDom = ref<HTMLDivElement>(); const messageList = ref<ChatMessage[]>([]); - -onMounted(() => { - if (!activeChatRoom.value) { - router.replace({ - name: 'Home', - }); - return; - } - messageContent.value = { - type: AnswerType.Text, - values: activeChatRoom.value.title, - }; - sendChatMessage(); +const computedMessageList = computed(() => { + return messageList.value.filter((v) => v.role !== RoleEnum.system); }); +// onMounted(() => { +// if (!activeChatRoom.value) { +// router.replace({ +// name: 'Home', +// }); +// return; +// } +// messageContent.value = { +// type: AnswerType.Text, +// values: activeChatRoom.value.title, +// }; +// sendChatMessage(); +// }); -const questionAi = async (text) => { - // const res = await QuestionAi({ - // question: text, - // }); - const res = { - json_ok: true, - question: '鏄ㄦ棩浜斾竴骞垮満鍘嬪姏', - answer_type: 'recordset', - values: { - names: ['yesterday', 'max_pressure'], - values: [ - ['2024-06-28 00:00:00', 24.378], - ['2024-06-29 00:00:00', 24.276], - ], - type: 'records', - title: '鏄ㄦ棩浜斾竴骞垮満(D_GW_04)鐨勬渶澶у帇鍔涘��', - }, - }; +const getAnswerById = async (historyId: string) => { + return await GetHistoryAnswer({ + history_id: historyId, + }); +}; +let currentSectionId = ''; +watch( + () => activeRoomId.value, + async (val) => { + if (!val) { + router.replace({ + name: 'Home', + }); + return; + } + + const res = await QueryHistoryDetail({ + history_group_id: activeRoomId.value, + }); + messageList.value = (res.details ?? []).map((item) => { + return { + role: RoleEnum.user, + content: { + type: AnswerType.Text, + values: item.question, + }, + } as ChatMessage; + }); + currentSectionId = res?.details?.[0]?.section_a_id; + const resList = await Promise.all((res.details ?? []).map((item) => getAnswerById(item.history_id))); + let i = 0; + + resList.map((item, index) => { + const insertIndex = index + 1 + i; + messageList.value.splice(insertIndex, 0, { + role: RoleEnum.assistant, + content: parseContent(item.answer), + }); + i++; + }); + // messageContent.value = { + // type: AnswerType.Text, + // values: activeChatRoom.value.title, + // }; + // sendChatMessage(); + + console.log("馃殌 ~ messageList.value:", messageList.value) + }, + { + immediate: true, + } +); + +const parseContent = (res) => { let content: ChatContent = { type: AnswerType.Text, values: '鍙戠敓閿欒锛�', @@ -103,12 +136,51 @@ case AnswerType.Text: content = { type: AnswerType.Text, - values: res.values, + values: res.values ?? res.answer, }; break; + + case AnswerType.Knowledge: + content = { + type: AnswerType.Knowledge, + values: res.knowledge, + }; + + break; default: + content = { + type: AnswerType.Text, + values: '鍏朵粬', + }; break; } + return content; +}; + +const questionAi = async (text) => { + const res = await QuestionAi({ + question: text, + section_a_id: currentSectionId, + history_group_id: activeRoomId.value, + }); + // const res = { + // json_ok: true, + // question: '鏄ㄦ棩浜斾竴骞垮満鍘嬪姏', + // answer_type: 'recordset', + // values: { + // names: ['yesterday', 'max_pressure'], + // values: [ + // ['2024-06-28 00:00:00', 24.378], + // ['2024-06-29 00:00:00', 24.276], + // ], + // type: 'records', + // title: '鏄ㄦ棩浜斾竴骞垮満(D_GW_04)鐨勬渶澶у帇鍔涘��', + // }, + // }; + + const content = parseContent(res); + console.log("馃殌 ~ content:", content) + return content; }; @@ -152,11 +224,18 @@ const scrollToBottom = () => { if (!chatListDom.value) return; - chatListDom.value.lastElementChild.scrollIntoView(); - // scrollTo(0, chatListDom.value.scrollHeight); + chatListDom.value.lastElementChild?.scrollIntoView(); }; -watch(messageList.value, () => nextTick(() => scrollToBottom())); +watch( + messageList, + () => { + nextTick(() => scrollToBottom()); + }, + { + deep: true, + } +); //#region ====================== 鑱婂ぉ鍐呭鎿嶄綔 ====================== diff --git a/src/components/chat/chatComponents/knowledgeCom/KnowledgeCom.vue b/src/components/chat/chatComponents/knowledgeCom/KnowledgeCom.vue new file mode 100644 index 0000000..a27b269 --- /dev/null +++ b/src/components/chat/chatComponents/knowledgeCom/KnowledgeCom.vue @@ -0,0 +1,32 @@ +<template> + <div class="whitespace-pre-line space-y-7"> + <div v-for="(item, index) in data" :key="index"> + <div>{{ item.answer }}</div> + <div class="space-y-1 mt-2"> + <div v-for="(cItem, index) in item.contexts" :key="index"> + <div class="text-blue-500 cursor-pointer inline-block" @click="pageLinkClick(cItem)"> + <SvgIcon name="ele-Link" /> + <span class="ml-2">{{ cItem.metadata?.Title }}</span> + </div> + </div> + </div> + </div> + </div> +</template> + +<script setup lang="ts"> + + +defineProps(['data']) + +const pageLinkClick = (item) => { + const nwin = window.open(''); + nwin.document.write(`<p style="white-space:pre-line">${item.page_content}</p>`) + nwin.focus(); + if(item.metadata.Title){ + nwin.document.title = item.metadata.Title + + } +}; +</script> +<style scoped lang="scss"></style> diff --git a/src/components/chat/chatComponents/recordSetCom/RecordSetCom.vue b/src/components/chat/chatComponents/recordSetCom/RecordSetCom.vue index e4c1aa0..ab18dc0 100644 --- a/src/components/chat/chatComponents/recordSetCom/RecordSetCom.vue +++ b/src/components/chat/chatComponents/recordSetCom/RecordSetCom.vue @@ -102,7 +102,9 @@ onMounted(() => { setTimeout(() => { + const parent = chartRef.value.parentElement; + const parentBound = parent.getBoundingClientRect(); chartInstance = echarts.init(chartRef.value,undefined,{ width:parentBound.width, diff --git a/src/components/chat/chatComponents/summaryCom/SummaryCom.vue b/src/components/chat/chatComponents/summaryCom/SummaryCom.vue new file mode 100644 index 0000000..c618dae --- /dev/null +++ b/src/components/chat/chatComponents/summaryCom/SummaryCom.vue @@ -0,0 +1,6 @@ +<template> + <div></div> +</template> + +<script setup lang="ts"></script> +<style scoped lang="scss"></style> diff --git a/src/components/chat/model/types.ts b/src/components/chat/model/types.ts index 6e95204..28750a2 100644 --- a/src/components/chat/model/types.ts +++ b/src/components/chat/model/types.ts @@ -1,5 +1,6 @@ import RecordSetCom from '../chatComponents/recordSetCom/RecordSetCom.vue'; import NormalTextCom from '../chatComponents/normalTextCom/NormalTextCom.vue'; +import knowledgeCom from '../chatComponents/knowledgeCom/KnowledgeCom.vue'; import assistantPic from '../images/assistant.jpg'; import userPic from '../images/user.svg'; @@ -10,7 +11,7 @@ } export const answerTypeMapCom = { - [AnswerType.Knowledge]: RecordSetCom, + [AnswerType.Knowledge]: knowledgeCom, [AnswerType.RecordSet]: RecordSetCom, [AnswerType.Text]:NormalTextCom }; diff --git a/src/layout/component/sidebar/waterLeftAside/asideNew.vue b/src/layout/component/sidebar/waterLeftAside/asideNew.vue index c2a4fd3..b48ef4e 100644 --- a/src/layout/component/sidebar/waterLeftAside/asideNew.vue +++ b/src/layout/component/sidebar/waterLeftAside/asideNew.vue @@ -35,7 +35,7 @@ @click="roomClick(item)" > <div class="ywicon icon-xiaoxi flex-0 mr-2.5"></div> - <div class="flex-auto text-ellipsis text-nowrap text-sm group-hover:text-[#0084ff]">chat room</div> + <div class="flex-auto text-ellipsis text-nowrap text-sm group-hover:text-[#0084ff]">{{ item.title }}</div> <div class="text-gray-100 flex items-center space-x-2 ml-1"> <div class="ywicon invisible icon-bianji visible group-hover:visible !text-sm"></div> @@ -56,7 +56,8 @@ import { nextTick, onMounted, reactive, ref } from 'vue'; import type { ChatRoomItem } from './types'; import router from '/@/router'; -import { activeRoomId, chatRoomList } from '/@/stores/chatRoom'; +import { CreateHistoryGroup, GetHistoryGroups } from '/@/api/ai/chat'; +import { chatRoomList, activeRoomId } from '/@/stores/chatRoom'; let state = reactive({ searchInput: '', selectDateOption: [ @@ -101,9 +102,13 @@ } }; -const newChatRoomClick = () => { +const newChatRoomClick = async () => { + const res = await CreateHistoryGroup({ + group_title: 'chat room', + }); + const newRoom = { - id: new Date().getTime() + '' + Math.floor(Math.random() * 1000), + id: res.history_group_id, isInitial: true, title: 'chat room', }; @@ -134,13 +139,22 @@ chatRoomRef.value.firstElementChild?.scrollIntoView(); }; -onMounted(() => { - if (router.currentRoute.value.name === 'Home') { - if (!chatRoomList.value || chatRoomList.value.length === 0) { - newChatRoomClick(); - } else { - roomClick(chatRoomList.value[0]); - } +onMounted(async () => { + const res = await GetHistoryGroups(); + + const resData = (res?.groups || []) as any[]; + chatRoomList.value = resData?.map((item) => { + return { + id: item.group_id, + title: item.group_title, + createTime: item.create_time, + isInitial: false, + }; + }); + if (!chatRoomList.value || chatRoomList.value.length === 0) { + newChatRoomClick(); + } else { + roomClick(chatRoomList.value[0]); } }); </script> diff --git a/src/layout/component/sidebar/waterLeftAside/types.ts b/src/layout/component/sidebar/waterLeftAside/types.ts index 34d41dd..1774883 100644 --- a/src/layout/component/sidebar/waterLeftAside/types.ts +++ b/src/layout/component/sidebar/waterLeftAside/types.ts @@ -1,8 +1,6 @@ - - export type ChatRoomItem = { - id:string; - isInitial:boolean, - title:string, - content?:string -} \ No newline at end of file + id: string; + isInitial:boolean, + title: string; + createTime?: string; +}; diff --git a/src/stores/chatRoom.ts b/src/stores/chatRoom.ts index 13421b5..891f97d 100644 --- a/src/stores/chatRoom.ts +++ b/src/stores/chatRoom.ts @@ -2,17 +2,9 @@ import type { ChatRoomItem } from '../layout/component/sidebar/waterLeftAside/types'; import { Local } from '../utils/storage'; -export const chatRoomList = ref<ChatRoomItem[]>(Local.get('chatRoomList')); +export const chatRoomList = ref<ChatRoomItem[]>([]); -watch( - () => chatRoomList.value, - (val) => { - Local.set('chatRoomList', val); - }, - { - deep: true, - } -); + export const activeRoomId = ref(null); export const activeChatRoom = computed(() => chatRoomList.value?.find((item) => item.id === activeRoomId.value)); -- Gitblit v1.9.3