diff --git a/vue-fastapi-frontend/src/api/aichat/aichat.js b/vue-fastapi-frontend/src/api/aichat/aichat.js index a9b2540..2f81317 100644 --- a/vue-fastapi-frontend/src/api/aichat/aichat.js +++ b/vue-fastapi-frontend/src/api/aichat/aichat.js @@ -22,8 +22,8 @@ export function getChatList(sessionId) { }) } -export function postChatMessage(data) { - return postStream('/aichat-api/stream-chat',data) +export function postChatMessage(data,signal) { + return postStream('/aichat-api/robot/robot_chat',data,signal) } export function DeleteChatSession(sessionId) { diff --git a/vue-fastapi-frontend/src/layout/components/AppMain.vue b/vue-fastapi-frontend/src/layout/components/AppMain.vue index 1cf3dd0..40412f5 100644 --- a/vue-fastapi-frontend/src/layout/components/AppMain.vue +++ b/vue-fastapi-frontend/src/layout/components/AppMain.vue @@ -11,8 +11,8 @@ -
- +
+
@@ -35,7 +35,6 @@ const route = useRoute() const tagsViewStore = useTagsViewStore() const showDiv = ref(false) const largeDiv = ref(true) -const chatDataList = ref([]) const aiChatDivStyle = ref({display: 'none',width: '450px',height: '600px',bottom: '16px',right: '16px'}) @@ -61,25 +60,6 @@ function largeChatDiv(){ function showDivClick(){ showDiv.value = !showDiv.value aiChatDivStyle.value.display = 'block' - if (Cookies.get("chatSessionId")){ - //调用子页面的赋值方法,给子页面赋值 - getChatList(Cookies.get("chatSessionId")).then(res=>{ - let array = res.data - if (array && array.length >0){ - for (let i = 0; i < array.length; i++) { - if (array[i].type === 'answer'){ - array[i].content = JSON.parse(array[i].content) - } - if (array[i].type === 'question'){ - array[i].file = JSON.parse(array[i].file) - } - } - } - chatDataList.value = array - }) - }else { - Cookies.set("chatSessionId",uuidv4()) - } } function littleChatDiv(){ diff --git a/vue-fastapi-frontend/src/utils/request.js b/vue-fastapi-frontend/src/utils/request.js index 20b98a0..1e38938 100644 --- a/vue-fastapi-frontend/src/utils/request.js +++ b/vue-fastapi-frontend/src/utils/request.js @@ -157,11 +157,12 @@ export default service * @param data 请求body * @returns */ -export function postStream(url, data) { +export function postStream(url, data,signal) { const headers = { 'Content-Type': 'application/json' } return fetch(url, { method: 'POST', body: data ? JSON.stringify(data) : undefined, - headers: headers + headers: headers, + signal: signal.signal }) } diff --git a/vue-fastapi-frontend/src/views/aichat/aichat.vue b/vue-fastapi-frontend/src/views/aichat/aichat.vue index a05273f..ce2f8f8 100644 --- a/vue-fastapi-frontend/src/views/aichat/aichat.vue +++ b/vue-fastapi-frontend/src/views/aichat/aichat.vue @@ -44,7 +44,7 @@ v-if="item.isStop && !item.isEnd" @click="startChat(index)" link - >继续 + >重新生成 停止回答 @@ -150,7 +150,7 @@
- + { return dialogScrollbar.value.scrollHeight } const handleScrollTop = ($event) => { scrollTop.value = $event.scrollTop - if ( - dialogScrollbar.value.scrollHeight - (scrollTop.value + scrollDiv.value.wrapRef.offsetHeight) <= - 30 - ) { - scorll.value = true - } else { - scorll.value = false - } emit('scroll', { ...$event, dialogScrollbar: dialogScrollbar.value, scrollDiv: scrollDiv.value }) } const handleScroll = () => { - if (!props.log && scrollDiv.value) { + if (scrollDiv.value) { // 内部高度小于外部高度 就需要出滚动条 if (scrollDiv.value.wrapRef.offsetHeight < dialogScrollbar.value.scrollHeight) { // 如果当前滚动条距离最下面的距离在 规定距离 滚动条就跟随 - if (scorll.value) { - scrollDiv.value.setScrollTop(getMaxHeight()) - } + scrollDiv.value.setScrollTop(getMaxHeight()) + } } } @@ -303,7 +294,8 @@ const handleFileSuccess = (response, file, fileList) => { currentFiles.value.push({file:response.data.file,bucket:response.data.bucket}) upload.open = false; upload.isUploading = false; - proxy.$alert("
" + response.msg + "
", "导入结果", { dangerouslyUseHTMLString: true }); + proxy.$modal.msgSuccess(response.msg); + // proxy.$alert("
" + response.msg + "
", "导入结果", { dangerouslyUseHTMLString: true }); }; /** 提交上传文件 */ @@ -425,7 +417,9 @@ async function sendChatHandle(event) { if (!event.ctrlKey) { // 如果没有按下组合键ctrl,则会阻止默认事件 event.preventDefault() - if (inputValue.value.trim() && ((chatList.value.length > 1 && chatList.value[chatList.value.length - 1].isEnd) || chatList.value.length < 2)) { + if (inputValue.value.trim() && (chatList.value.length === 0|| + (chatList.value[chatList.value.length - 1].isStop || + chatList.value[chatList.value.length - 1].isEnd))) { chatList.value.push({ "type": "question", "content": inputValue.value.trim(), @@ -434,13 +428,13 @@ async function sendChatHandle(event) { "sessionName": chatList.value.length > 0 ? chatList.value[0].content.substring(0, 20) : inputValue.value.trim().substring(0, 20), "file": currentFiles.value }) + let question = JSON.parse(JSON.stringify(chatList.value[chatList.value.length - 1])) + question.file = JSON.stringify(question.file) + await addChat(question) nextTick(() => { // 将滚动条滚动到最下面 scrollDiv.value.setScrollTop(getMaxHeight()) }) - let question = JSON.parse(JSON.stringify(chatList.value[chatList.value.length - 1])) - question.file = JSON.stringify(question.file) - await addChat(question) inputValue.value = '' let data = { "query": inputValue.value.trim(), @@ -459,27 +453,36 @@ async function sendChatHandle(event) { } function sendChatMessage(data){ - postChatMessage(data).then(res=>{ - currentFiles.value = [] - chatList.value.push({"type":"answer","content":[],"isEnd":false,"isStop":false,"sessionId":chatList.value[0].sessionId,"sessionName":chatList.value[0].sessionName, "operate":'',"thumbDownReason":''}) - answerList.value.push({"index":chatList.value.length-1, "content":[], "isEnd":false}) - const reader = res.body.getReader() - const write = getWrite(reader) - reader.read().then(write).then(()=> { - let answer = JSON.parse(JSON.stringify(chatList.value[chatList.value.length - 1])) - answer.content = JSON.stringify(answer.content) - addChat(answer) - }) + controller.value = new AbortController() + postChatMessage(data,{signal:controller.value.signal}).then(res=>{ + if (res.status !== 200){ + chatList.value.push({"type":"answer","content":[{"type":"text","content":"服务异常,错误码:"+res.status}],"isEnd":true,"isStop":false,"sessionId":chatList.value[0].sessionId,"sessionName":chatList.value[0].sessionName,"operate":'',"thumbDownReason":''}) + }else { + currentFiles.value = [] + chatList.value.push({"type":"answer","content":[],"isEnd":false,"isStop":false,"sessionId":chatList.value[0].sessionId,"sessionName":chatList.value[0].sessionName, "operate":'',"thumbDownReason":''}) + answerList.value.push({"index":chatList.value.length-1, "content":[], "isEnd":false}) + const reader = res.body.getReader() + const write = getWrite(reader) + reader.read().then(write).then(()=> { + let answer = JSON.parse(JSON.stringify(chatList.value[chatList.value.length - 1])) + answer.content = JSON.stringify(answer.content) + addChat(answer) + }) + } }).catch((e) => { - chatList.value.push({"type":"answer","content":[{"type":"text","content":"服务异常"}],"isEnd":true,"isStop":false}) + chatList.value.push({"type":"answer","content":[{"type":"text","content":"服务异常"}],"isEnd":true,"isStop":false,"sessionId":chatList.value[0].sessionId,"sessionName":chatList.value[0].sessionName,"operate":"","thumbDownReason":""}) }) } watch( chatList, () => { - handleScroll() - }, - { deep: true, immediate: true } + nextTick(() => { + // 将滚动条滚动到最下面 + scrollDiv.value.setScrollTop(getMaxHeight()) + }) + },{ + deep:true, immediate:true + } ) const getWrite = (reader) => { let tempResult = ''; @@ -533,8 +536,9 @@ const getWrite = (reader) => { answerList.value[answerList.value.length - 1].content.push({"content":chunk.choices[0].delta.content,"type":"text"}) } } - if (!chatList.value[answerList.value[answerList.value.length - 1].index].isStop){ - chatList.value[chatList.value.length - 1].content =answerList.value[answerList.value.length - 1].content + let isStop = chatList.value[chatList.value.length-1].isStop + if (!isStop){ + chatList.value[chatList.value.length - 1].content = answerList.value[answerList.value.length - 1].content } nextTick(() => { // 将滚动条滚动到最下面 @@ -543,7 +547,7 @@ const getWrite = (reader) => { if (chunk.isEnd){ answerList.value[answerList.value.length - 1].isEnd = true answerList.value[answerList.value.length - 1].time = formatDate(new Date()) - if (!chatList.value[answerList.value[answerList.value.length - 1].index].isStop){ + if (!isStop){ chatList.value[chatList.value.length - 1].isEnd = true chatList.value[chatList.value.length - 1].time = formatDate(new Date()) } @@ -565,9 +569,15 @@ const getWrite = (reader) => { const stopChat = (index) => { chatList.value[index].isStop = true + if (controller.value !== null){ + controller.value.abort() + } + let answer = JSON.parse(JSON.stringify(chatList.value[index])) + answer.content = JSON.stringify(answer.content) + addChat(answer) } const startChat = (index) => { - chatList.value[index].isStop = false + regenerationChart(index) } defineExpose({ diff --git a/vue-fastapi-frontend/src/views/aichat/index.vue b/vue-fastapi-frontend/src/views/aichat/index.vue index 0023dc8..fe0e73d 100644 --- a/vue-fastapi-frontend/src/views/aichat/index.vue +++ b/vue-fastapi-frontend/src/views/aichat/index.vue @@ -109,7 +109,7 @@ function deleteLog(row) { listChatHistory(sessionId).then(response =>{ chatLogeData.value = response.data }) - proxy.$modal.msgSuccess("复制成功"); + proxy.$modal.msgSuccess("删除成功"); }) } @@ -159,7 +159,31 @@ function clickListHandle(item){ }) } watch(() => props.chatDataList, value => currentRecordList.value = JSON.parse(JSON.stringify(value))) - +onMounted( + ()=>{ + if (Cookies.get("chatSessionId")){ + //调用子页面的赋值方法,给子页面赋值 + getChatList(Cookies.get("chatSessionId")).then(res=>{ + let array = res.data + if (array && array.length >0){ + for (let i = 0; i < array.length; i++) { + if (array[i].type === 'answer'){ + array[i].content = JSON.parse(array[i].content) + } + if (array[i].type === 'question'){ + array[i].file = JSON.parse(array[i].file) + } + } + } + currentRecordList.value = array + }).then(()=>{ + AiChatRef.value.setScrollBottom() + }) + }else { + Cookies.set("chatSessionId",uuidv4()) + } + } +)