From 78e1cda41cf6ad133e84efc78da50d77f2e43eca Mon Sep 17 00:00:00 2001 From: charles <981744753@qq.com> Date: 星期五, 02 八月 2024 15:56:43 +0800 Subject: [PATCH] feat:完成新增会话的,会话记录的聊天模块 --- src/views/dmx/model/components/addModel.vue | 11 - src/views/session/sessionManager/components/addSession.vue | 75 +++++++++-- src/api/session.ts | 4 src/api/interceptor.ts | 4 src/views/session/sessionManager/index.vue | 24 ++-- .idea/codeStyles/Project.xml | 4 src/views/sessionRecords/sessionRecordsManager/index.vue | 236 ++++++++++++++++++++++++++++---------- 7 files changed, 249 insertions(+), 109 deletions(-) diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index 3e22f4b..70dac63 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -3,7 +3,7 @@ <HTMLCodeStyleSettings> <option name="HTML_SPACE_INSIDE_EMPTY_TAG" value="true" /> </HTMLCodeStyleSettings> - <JSCodeStyleSettings version="0"> + <JSCodeStyleSettings> <option name="FORCE_SEMICOLON_STYLE" value="true" /> <option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" /> <option name="FORCE_QUOTE_STYlE" value="true" /> @@ -11,7 +11,7 @@ <option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" /> <option name="SPACES_WITHIN_IMPORTS" value="true" /> </JSCodeStyleSettings> - <TypeScriptCodeStyleSettings version="0"> + <TypeScriptCodeStyleSettings> <option name="FORCE_SEMICOLON_STYLE" value="true" /> <option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" /> <option name="FORCE_QUOTE_STYlE" value="true" /> diff --git a/src/api/interceptor.ts b/src/api/interceptor.ts index f08ca35..0f73743 100644 --- a/src/api/interceptor.ts +++ b/src/api/interceptor.ts @@ -55,7 +55,7 @@ const res = response.data; // if the custom code is not 20000, it is judged as an error. - if ( + /*if ( (res.retcode && res.retcode !== 0) || (res.code && res.code !== 20000) ) { @@ -88,7 +88,7 @@ response.config.url === '/base/login' ) { setAuthorization(response.headers.authorization); - } + }*/ return res; }, (error) => { diff --git a/src/api/session.ts b/src/api/session.ts index fe2b47f..bd56742 100644 --- a/src/api/session.ts +++ b/src/api/session.ts @@ -26,4 +26,8 @@ // 鑾峰彇浼氳瘽璇︽儏 export function getSessionDetailsApi(conversation_id:string) { return axios.get<ISessionListResult>('/api/conversation/get?modeltype=localragflow',{params:{conversation_id}}); +} +// 鑾峰彇鏅鸿兘鍔╂墜鍒楄〃 +export function getDialogListApi() { + return axios.get<ISessionListResult>('/api/dialog/list'); } \ No newline at end of file diff --git a/src/views/dmx/model/components/addModel.vue b/src/views/dmx/model/components/addModel.vue index eeab066..456a8fa 100644 --- a/src/views/dmx/model/components/addModel.vue +++ b/src/views/dmx/model/components/addModel.vue @@ -1,4 +1,3 @@ - <template> <a-button type="primary" @click="handleClick" style="margin-left: 10px"> <template #icon> @@ -138,7 +137,6 @@ done(false) }else { console.log('璇锋眰鏁版嵁'); - } }) }; @@ -176,12 +174,3 @@ }) </script> - -<script lang="ts"> -export default { - name: 'add', - methods: { - - } -}; -</script> \ No newline at end of file diff --git a/src/views/session/sessionManager/components/addSession.vue b/src/views/session/sessionManager/components/addSession.vue index 0a1c997..37ebde1 100644 --- a/src/views/session/sessionManager/components/addSession.vue +++ b/src/views/session/sessionManager/components/addSession.vue @@ -1,34 +1,75 @@ <script setup lang="ts"> - import { defineProps ,ref,defineEmits} from 'vue'; + import { defineProps,ref,defineEmits, onMounted } from 'vue'; import { Message } from '@arco-design/web-vue'; - import { addSessionApi }from '@/api/session'; + import { addSessionApi ,getDialogListApi }from '@/api/session'; const props=defineProps({ modalObj:Object }); - const conversation_name=ref(''); + const conversation=ref({ dialog_id:'', conversation_desc:'' }); + const dialogList=ref([]); const emit = defineEmits(['addSession']); - const handleOk=async ()=>{ - if(conversation_name.value){ - const {code}=await addSessionApi({conversation_name:conversation_name.value}); - if(code===200){ - Message.success('娣诲姞鎴愬姛'); - emit('addSession') - } - }else{ - Message.warning('浼氳瘽鍚嶇О涓嶈兘涓虹┖'); - } + const queryDialogList=async ()=>{ + const { code, data } = await getDialogListApi(); + if(code===200){ + dialogList.value = data; + } + }; + + const rules = { + dialog_id: [ + { + required: true, + message: '鏅鸿兘鍔╂墜涓嶈兘涓虹┖', + }, + ], + conversation_desc: [ + { + required: true, + message: '鎻忚堪涓嶈兘涓虹┖', + } + ] + } + onMounted(()=>{ + queryDialogList(); + }); + const formRef = ref(); + const handleOk=()=>{ + formRef.value.validate().then(async(res)=>{ + if(!res){ + const { code }=await addSessionApi({ ...conversation.value }); + if(code===200){ + Message.success('娣诲姞鎴愬姛'); + emit('addSession'); + setTimeout(()=>{ + props.modalObj.add=false; + },500); + }else{ + Message.warning('娣诲姞澶辫触'); + } + } + }); + + return false; + } + const destroyData = ()=>{ + formRef.value.resetFields(); } </script> <template> <div> - <a-modal v-model:visible="modalObj.add" @ok="handleOk" @cancel="modalObj.add=false"> + <a-modal v-model:visible="modalObj.add" @before-ok="handleOk" @cancel="modalObj.add=false" @before-close="destroyData"> <template #title> 鏂板浼氳瘽 </template> - <a-form> - <a-form-item label="浼氳瘽鍚嶇О:"> - <a-input placeholder="璇疯緭鍏ヤ細璇濆悕绉�" v-model="conversation_name" style="width: 80%"></a-input> + <a-form ref="formRef" :model="conversation" :rules="rules"> + <a-form-item label="鍔╂墜鍏宠仈:" field="dialog_id" @submit="handleSubmit"> + <a-select style="width: 80%" v-model="conversation.dialog_id" placeholder="璇烽�夋嫨鍏宠仈鍔╂墜"> + <a-option v-for="dialog in dialogList" :key="dialog.id" :value="dialog.id">{{dialog.name}}</a-option> + </a-select> + </a-form-item> + <a-form-item label="鎻忚堪:" field="conversation_desc"> + <a-textarea placeholder="璇疯緭鍏ユ弿杩�" :max-length="100" show-word-limit :auto-size="{minRows:4,maxRows:5}" v-model="conversation.conversation_desc" style="width: 80%"></a-textarea> </a-form-item> </a-form> </a-modal> diff --git a/src/views/session/sessionManager/index.vue b/src/views/session/sessionManager/index.vue index 6cdb28a..1348516 100644 --- a/src/views/session/sessionManager/index.vue +++ b/src/views/session/sessionManager/index.vue @@ -25,20 +25,20 @@ const streamStr=ref(''); const modalObj=reactive({ add:false }); //鏌ヨ浼氳瘽鍒楄〃 - const querySessionList = async () => { + const querySessionList = async () => { const { code, data } =await sessionListApi(); - if (code === 200) { - sessionList.value = data; - if(Array.isArray(data)&&data.length>0){ - activeSessionId.value=data[0].id; - const res= await getSessionDetailsApi(data[0].id); - if(res.code===200){ - sessionDetailList.value=res.data.message; - refreshScroll(); + if (code === 200) { + sessionList.value = data; + if(Array.isArray(data)&&data.length>0){ + activeSessionId.value=data[0].id; + const res= await getSessionDetailsApi(data[0].id); + if(res.code===200){ + sessionDetailList.value=res.data.message; + refreshScroll(); + } } - } }else{ - Message.warning('鏌ヨ澶辫触'); + Message.warning('鏌ヨ澶辫触'); } }; //鏍规嵁浼氳瘽id鍒犻櫎浼氳瘽 @@ -142,7 +142,7 @@ </a-button> </template> <a-scrollbar class="left-list" style="height: 60vh;overflow-y: auto;"> - <div class="item" :class="{isLeftActive:activeSessionId===session.id}" v-for="session in sessionList" :key="session.id" @click="querySessionDetail(session)"> + <div class="item" :class="{ isLeftActive:activeSessionId===session.id }" v-for="session in sessionList" :key="session.id" @click="querySessionDetail(session)"> <div class="item-left"> <IconQuestionCircleFill/>銆� {{session.name}} diff --git a/src/views/sessionRecords/sessionRecordsManager/index.vue b/src/views/sessionRecords/sessionRecordsManager/index.vue index 528c003..3b3a1e9 100644 --- a/src/views/sessionRecords/sessionRecordsManager/index.vue +++ b/src/views/sessionRecords/sessionRecordsManager/index.vue @@ -1,14 +1,65 @@ <script setup lang="ts"> import { IconSearch,IconTiktokColor ,IconSend,IconClose} from '@arco-design/web-vue/es/icon'; import { useAppStore} from '@/store'; - import {computed,ref,onMounted,reactive} from 'vue'; - import {sessionListApi}from '@/api/session'; + import { computed, ref, onMounted, reactive, nextTick } from 'vue'; import { Message } from '@arco-design/web-vue'; import moment from 'moment'; import AddSession from '@/views/session/sessionManager/components/addSession.vue'; + import { sessionListApi, deleteSessionApi,getSessionDetailsApi,chatApi }from '@/api/session'; + const sessionDetailList=ref([]);//鏍规嵁浼氳瘽id鍑烘潵鐨勪細璇濊鎯� const sessionList=ref([]);//浼氳瘽鍒楄〃 - const modalObj=reactive({add:false}); - //鏌ヨ浼氳瘽鍒楄〃 + const modalObj=reactive({ add:false }); + + const currIndex = ref(0) + const displayedText = ref('');// 姝e湪鏄剧ず鐨勬枃瀛� + let timer: number|null = null; + const streamStr=ref(''); + const inputMsg=ref(''); + const activeSessionId=ref(''); + + const sendMessage= async (event)=>{ + event.preventDefault(); + if(!activeSessionId.value){ + Message.warning('璇烽�夋嫨浼氳瘽'); + return; + } + if(inputMsg.value){ + const {code,data} =await chatApi({conversation_id:activeSessionId.value,messages:inputMsg.value}); + const res= await getSessionDetailsApi(activeSessionId.value); + if(res.code===200){ + sessionDetailList.value=res.data.message.map((item,index)=>{ + if(index===res.data.message.length-1){ + item.role='last'; + displayedText.value=''; + currIndex.value=0; + streamStr.value=item.content; + startDisplayStr(); + } + return item; + }); + refreshScroll(); + } + inputMsg.value=''; + }else{ + Message.warning('娑堟伅涓嶈兘涓虹┖'); + } + }; + const querySessionDetail=async (session)=>{ + activeSessionId.value=session.id; + const {code,data}= await getSessionDetailsApi(session.id); + if(code===200){ + sessionDetailList.value=data.message; + refreshScroll();//鍒锋柊婊氬姩鏉′綅缃� + } + }; + const scrollbar = ref(null); + const refreshScroll=()=>{ + nextTick(()=>{ + const container = document.getElementById('home'); + scrollbar.value.scrollTop(container.scrollHeight); + }); + }; + // 鏌ヨ浼氳瘽鍒楄〃 const querySessionList=async ()=>{ const {code,data} =await sessionListApi(); if(code===200){ @@ -28,6 +79,22 @@ const theme = computed(() => { return appStore.theme; }); + //鏂囧瓧鍔ㄦ�佽緭鍑� + const startDisplayStr = () => { + if (timer) { + clearTimeout(timer!); + } + const res = streamStr.value; + // 灏嗘暟缁勪腑鐨勫瓧绗︿覆鎷兼帴璧锋潵 + if (currIndex.value < res.length) { + displayedText.value += res[currIndex.value]; + currIndex.value++; + setTimeout(startDisplayStr, 100); + } else { + clearTimeout(timer!); + timer = null + } + } </script> <template> @@ -50,7 +117,7 @@ </a-card> <a-card class="left"> <a-scrollbar class="left-list" style="height: calc(100vh - 160px);overflow-y: auto;overflow-x: hidden;"> - <div class="item" v-for="session in sessionList"> + <div class="item" v-for="session in sessionList" @click="querySessionDetail(session)" :class="{ isLeftActive:activeSessionId===session.id }"> <div class="text" :class="{light:theme==='dark'}">{{session.name}}</div> <div class="time">{{moment(new Date(session.create_time)).format('YYYY-MM-DD HH:mm:ss')}}</div> </div> @@ -59,68 +126,102 @@ </a-col> <a-col :span="15"> <a-card class="center"> - <div class="center-title">鏅鸿兘闂瓟</div> - <div class="center-content"> - 鎴戝彲浠ョ悊瑙e拰瀛︿範浜虹被鐨勮瑷�锛屽叿澶囧杞璇濈殑鑳藉姏锛岀幇鍦ㄥ拰鎴戝紑濮嬩氦娴佸惂~ - </div> - <div class="center-question"> - <div class="center-question-left">璇曚竴璇曡繖鏍烽棶鎴�</div> - <div class="center-question-right"> - <a-button type="primary">鎹竴鎹�</a-button> + <div v-if="sessionDetailList.length===0"> + <div class="center-title">鏅鸿兘闂瓟</div> + <div class="center-content"> + 鎴戝彲浠ョ悊瑙e拰瀛︿範浜虹被鐨勮瑷�锛屽叿澶囧杞璇濈殑鑳藉姏锛岀幇鍦ㄥ拰鎴戝紑濮嬩氦娴佸惂~ </div> + <div class="center-question"> + <div class="center-question-left">璇曚竴璇曡繖鏍烽棶鎴�</div> + <div class="center-question-right"> + <a-button type="primary">鎹竴鎹�</a-button> + </div> + </div> + <a-row justify="space-around" class="center-list"> + <a-col :span="7" class="item"> + <div class="item-title"> + <IconTiktokColor></IconTiktokColor>鎶栭煶甯﹁揣鑴氭湰 + </div> + <div class="item-content" :class="{dark:theme==='dark'}"> + 缂栧啓寮曚汉娉ㄧ洰涓斿叿鏈夎鏈嶅姏鐨勩�侀�傜敤浜庝骇鍝佺殑... + </div> + </a-col> + <a-col :span="7" class="item"> + <div class="item-title"> + <IconTiktokColor></IconTiktokColor>鎶栭煶甯﹁揣鑴氭湰 + </div> + <div class="item-content" :class="{dark:theme==='dark'}"> + 缂栧啓寮曚汉娉ㄧ洰涓斿叿鏈夎鏈嶅姏鐨勩�侀�傜敤浜庝骇鍝佺殑... + </div> + </a-col> + <a-col :span="7" class="item"> + <div class="item-title"> + <IconTiktokColor></IconTiktokColor>鎶栭煶甯﹁揣鑴氭湰 + </div> + <div class="item-content" :class="{dark:theme==='dark'}"> + 缂栧啓寮曚汉娉ㄧ洰涓斿叿鏈夎鏈嶅姏鐨勩�侀�傜敤浜庝骇鍝佺殑... + </div> + </a-col> + <a-col :span="7" class="item"> + <div class="item-title"> + <IconTiktokColor></IconTiktokColor>鎶栭煶甯﹁揣鑴氭湰 + </div> + <div class="item-content" :class="{dark:theme==='dark'}"> + 缂栧啓寮曚汉娉ㄧ洰涓斿叿鏈夎鏈嶅姏鐨勩�侀�傜敤浜庝骇鍝佺殑... + </div> + </a-col> + <a-col :span="7" class="item"> + <div class="item-title"> + <IconTiktokColor></IconTiktokColor>鎶栭煶甯﹁揣鑴氭湰 + </div> + <div class="item-content" :class="{dark:theme==='dark'}"> + 缂栧啓寮曚汉娉ㄧ洰涓斿叿鏈夎鏈嶅姏鐨勩�侀�傜敤浜庝骇鍝佺殑... + </div> + </a-col> + <a-col :span="7" class="item"> + <div class="item-title"> + <IconTiktokColor></IconTiktokColor>鎶栭煶甯﹁揣鑴氭湰 + </div> + <div class="item-content" :class="{dark:theme==='dark'}"> + 缂栧啓寮曚汉娉ㄧ洰涓斿叿鏈夎鏈嶅姏鐨勩�侀�傜敤浜庝骇鍝佺殑... + </div> + </a-col> + </a-row> </div> - <a-row justify="space-around" class="center-list"> - <a-col :span="7" class="item"> - <div class="item-title"> - <IconTiktokColor></IconTiktokColor>鎶栭煶甯﹁揣鑴氭湰 - </div> - <div class="item-content" :class="{dark:theme==='dark'}"> - 缂栧啓寮曚汉娉ㄧ洰涓斿叿鏈夎鏈嶅姏鐨勩�侀�傜敤浜庝骇鍝佺殑... - </div> - </a-col> - <a-col :span="7" class="item"> - <div class="item-title"> - <IconTiktokColor></IconTiktokColor>鎶栭煶甯﹁揣鑴氭湰 - </div> - <div class="item-content" :class="{dark:theme==='dark'}"> - 缂栧啓寮曚汉娉ㄧ洰涓斿叿鏈夎鏈嶅姏鐨勩�侀�傜敤浜庝骇鍝佺殑... - </div> - </a-col> - <a-col :span="7" class="item"> - <div class="item-title"> - <IconTiktokColor></IconTiktokColor>鎶栭煶甯﹁揣鑴氭湰 - </div> - <div class="item-content" :class="{dark:theme==='dark'}"> - 缂栧啓寮曚汉娉ㄧ洰涓斿叿鏈夎鏈嶅姏鐨勩�侀�傜敤浜庝骇鍝佺殑... - </div> - </a-col> - <a-col :span="7" class="item"> - <div class="item-title"> - <IconTiktokColor></IconTiktokColor>鎶栭煶甯﹁揣鑴氭湰 - </div> - <div class="item-content" :class="{dark:theme==='dark'}"> - 缂栧啓寮曚汉娉ㄧ洰涓斿叿鏈夎鏈嶅姏鐨勩�侀�傜敤浜庝骇鍝佺殑... - </div> - </a-col> - <a-col :span="7" class="item"> - <div class="item-title"> - <IconTiktokColor></IconTiktokColor>鎶栭煶甯﹁揣鑴氭湰 - </div> - <div class="item-content" :class="{dark:theme==='dark'}"> - 缂栧啓寮曚汉娉ㄧ洰涓斿叿鏈夎鏈嶅姏鐨勩�侀�傜敤浜庝骇鍝佺殑... - </div> - </a-col> - <a-col :span="7" class="item"> - <div class="item-title"> - <IconTiktokColor></IconTiktokColor>鎶栭煶甯﹁揣鑴氭湰 - </div> - <div class="item-content" :class="{dark:theme==='dark'}"> - 缂栧啓寮曚汉娉ㄧ洰涓斿叿鏈夎鏈嶅姏鐨勩�侀�傜敤浜庝骇鍝佺殑... - </div> - </a-col> - </a-row> + <a-scrollbar ref="scrollbar" id="home" v-else class="chat-list" style="width:90%;overflow:auto;height: 60vh;margin: 0px auto"> + <div class="chat-item" v-for="sessionDetail in sessionDetailList"> + <a-comment + v-if="sessionDetail.role==='user'" + avatar="https://p1-arco.byteimg.com/tos-cn-i-uwbnlip3yd/3ee5f13fb09879ecb5185e440cef6eb9.png~tplv-uwbnlip3yd-webp.webp" + > + <template #content> + <div :class="{light:theme==='light'}">{{sessionDetail.content}}</div> + </template> + </a-comment> + <a-comment + v-else-if="sessionDetail.role==='assistant'" + avatar="https://p1-arco.byteimg.com/tos-cn-i-uwbnlip3yd/9eeb1800d9b78349b24682c3518ac4a3.png~tplv-uwbnlip3yd-webp.webp" + > + <template #content> + <a-card class="chat-item-answer" style="background-color: rgba(63, 64, 79, 1);"> + <div :class="{light:theme==='light'}">{{sessionDetail.content}}</div> + </a-card> + </template> + </a-comment> + <a-comment + v-else-if="sessionDetail.role==='last'" + avatar="https://p1-arco.byteimg.com/tos-cn-i-uwbnlip3yd/9eeb1800d9b78349b24682c3518ac4a3.png~tplv-uwbnlip3yd-webp.webp" + > + <template #content> + <a-textarea readonly auto-size v-model="displayedText" class="chat-item-answer" style="background-color: rgba(63, 64, 79, 1);"> + </a-textarea> + </template> + </a-comment> + </div> + </a-scrollbar> + <div class="center-bottom"> - <a-textarea style="height: 180px" placeholder="杈撳叆鎮ㄦ兂浜嗚В鐨勫唴瀹癸紝Shift+Enter鎹㈣" :max-length="500" allow-clear show-word-limit> + <a-textarea v-model="inputMsg" @keydown.shift.enter="sendMessage" style="height: 180px" placeholder="杈撳叆鎮ㄦ兂浜嗚В鐨勫唴瀹癸紝Shift+Enter鍙戦��" :max-length="500" allow-clear show-word-limit> </a-textarea> </div> </a-card> @@ -181,6 +282,9 @@ </template> <style scoped lang="scss"> + .isLeftActive{ + background-color:lightgrey; + } .light{ color: white !important; } @@ -210,10 +314,12 @@ } .text{ color: black; + padding-left: 10px; } .time{ color: gray; font-size: 12px; + padding-left: 10px; } } } @@ -269,7 +375,7 @@ .center-bottom{ position: absolute; width: 90%; - bottom: 70px; + bottom: 20px; left:5%; } } -- Gitblit v1.8.0