9 changed files with 333 additions and 31 deletions
@ -0,0 +1,177 @@ |
|||
<!-- |
|||
@ 作者: han2015 |
|||
@ 时间: 2025-05-12 15:39:13 |
|||
@ 备注: aibot组件 |
|||
--> |
|||
<script lang="ts" setup> |
|||
|
|||
import { doAiChat,aiChatData} from "@/api/doc/space" |
|||
import {ElText,ElInput, ButtonInstance} from "element-plus"; |
|||
import { VueMarkdown } from '@crazydos/vue-markdown' |
|||
import rehypeRaw from 'rehype-raw' |
|||
import remarkGfm from 'remark-gfm' |
|||
|
|||
//选中的咨询模式 |
|||
const checkedModel = ref([]) |
|||
|
|||
const baseURL=import.meta.env.VITE_APP_BASE_API |
|||
const conversation=ref("") //当前会话的uuid |
|||
const myquestion=ref('') |
|||
const controller = ref<AbortController | null>(null) |
|||
|
|||
const interact_msg=ref<{ask:boolean,think:string,content:string}[]>([]) |
|||
const props = withDefaults(defineProps<{ |
|||
//closefunc:()=>void, |
|||
//agent:{model:boolean,name:string,uuid:string[]} |
|||
}>(),{}) |
|||
|
|||
|
|||
//消息体 |
|||
interface message{ |
|||
ask:boolean, |
|||
think:string, |
|||
content:string |
|||
} |
|||
//会话记录 |
|||
interface chatRecord{ |
|||
uuid:string, |
|||
agentuuid:string, |
|||
brief:string, |
|||
messages:message[] |
|||
} |
|||
|
|||
/* 中断函数,供按钮调用 */ |
|||
function abortFetch() { |
|||
if (controller.value) { |
|||
controller.value.abort() |
|||
controller.value = null |
|||
} |
|||
} |
|||
|
|||
defineExpose({onSendParamToAI}) |
|||
|
|||
async function onSendParamToAI(user:string,arr:Array<{ uuids: string[]; params: { [key: string]: any } }>){ |
|||
interact_msg.value=[] |
|||
for (let ele of arr){ |
|||
interact_msg.value.unshift({ask:true,think:"", content:"AI正在分析。。。"}) |
|||
for (let uid of ele.uuids){ |
|||
await doRequest(user,uid,ele.params) |
|||
} |
|||
} |
|||
} |
|||
|
|||
async function doRequest(_user:string,uuid:string,param:any){ |
|||
let mRespMsg="" |
|||
const params={ |
|||
"onlineSearch":"否", |
|||
"useDataset":"否" |
|||
} |
|||
for (let item of checkedModel.value){ |
|||
if(item==="onlineSearch") params.onlineSearch="是" |
|||
if(item==="useDataset") params.useDataset="是" |
|||
} |
|||
|
|||
controller.value = new AbortController(); |
|||
try{ |
|||
const res= await doAiChat(`${baseURL}/aibot/assisted/${uuid}/chat`,{ |
|||
inputs: params, |
|||
query:JSON.stringify(param), |
|||
response_mode:"streaming", |
|||
conversation_id:"",//conversation.value, |
|||
user:_user,//这里已经base64解析了 |
|||
},controller.value.signal |
|||
) |
|||
|
|||
if (!res.ok) { |
|||
throw new Error(`HTTP ${res.status} ${res.statusText}`); |
|||
} |
|||
|
|||
const reader = res.body!.getReader(); |
|||
const decoder = new TextDecoder('utf-8'); |
|||
|
|||
let chunk = ''; // 行缓存 |
|||
while (true) { |
|||
const {done, value} = await reader.read() |
|||
if (done) break; |
|||
|
|||
// 服务器可能一次返回多行,需要手动按行拆分 |
|||
chunk += decoder.decode(value, {stream: true}); |
|||
const lines = chunk.split(/\n/); |
|||
chunk = lines.pop() || '' |
|||
for (const line of lines) { |
|||
if (!line.trim()) continue; // 跳过空行 |
|||
if (line.startsWith('data: ')) { |
|||
const data = line.slice(6); |
|||
const json = JSON.parse(data); |
|||
if(json.event==="message"){ |
|||
//conversation.value=json.conversation_id |
|||
mRespMsg+=json.answer |
|||
} |
|||
} |
|||
} |
|||
} |
|||
}catch (e: any) { |
|||
if (e.name === 'AbortError') { |
|||
console.log('用户手动中断') |
|||
} |
|||
} |
|||
|
|||
interact_msg.value.unshift({ask:true,think:"", content:JSON.stringify(param)}) |
|||
const arr=mRespMsg.split("</think>") |
|||
if(arr.length===1){ |
|||
interact_msg.value.unshift({ask:false,think:"",content:arr[0]}) |
|||
}else{ |
|||
//思考模式 |
|||
interact_msg.value.unshift({ask:false,think:arr[0],content:arr[1]}) |
|||
} |
|||
} |
|||
|
|||
function resetContext(){ |
|||
interact_msg.value=[] |
|||
conversation.value="" |
|||
//props.closefunc() |
|||
} |
|||
|
|||
//渲染完页面再执行 |
|||
// onMounted(() => { |
|||
// }); |
|||
</script> |
|||
|
|||
<template> |
|||
<div :style="{backgroundColor:'#f3f3f3'}"> |
|||
<div class="reply_area" > |
|||
<template v-for="msg of interact_msg"> |
|||
<div v-if="msg.ask" class="t_ask" >{{ msg.content }}</div> |
|||
<div v-else class="t_resp"> |
|||
{{ msg.content }} |
|||
<!-- <VueMarkdown :markdown="msg.content"></VueMarkdown> --> |
|||
</div> |
|||
</template> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<style lang="scss" scoped> |
|||
|
|||
.reply_area{ |
|||
width: 260px; |
|||
background: white; |
|||
overflow-y: auto; |
|||
display: flex; |
|||
flex-direction: column; |
|||
overflow-wrap:anywhere; |
|||
} |
|||
|
|||
.t_ask{ |
|||
margin: 5px; |
|||
align-self: end; |
|||
background-color: rgb(188 211 241); |
|||
border-radius:10px; |
|||
} |
|||
.t_resp{ |
|||
margin: 5px; |
|||
align-self: start; |
|||
color: black; |
|||
background-color: rgb(222, 243, 222); |
|||
} |
|||
</style> |
|||
Loading…
Reference in new issue