src/api/ai/chat.ts | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/components/chat/Chat.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/components/chat/chatComponents/knowledgeCom/KnowledgeCom.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/components/chat/chatComponents/recordSetCom/RecordSetCom.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/components/chat/chatComponents/summaryCom/SummaryCom.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/components/chat/model/types.ts | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/layout/component/sidebar/waterLeftAside/asideNew.vue | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/layout/component/sidebar/waterLeftAside/types.ts | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/stores/chatRoom.ts | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
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', }, }); }; 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 ====================== è天å 容æä½ ====================== 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> 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, 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> 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 }; 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> src/layout/component/sidebar/waterLeftAside/types.ts
@@ -1,8 +1,6 @@ export type ChatRoomItem = { id:string; isInitial:boolean, title:string, content?:string } id: string; isInitial:boolean, title: string; createTime?: string; }; 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));