|
|
|
@ -7,17 +7,24 @@ |
|
|
|
|
|
|
|
import { doAiChat,aiChatData} from "@/api/doc/space" |
|
|
|
import { useUserStore } from "@/store/modules/user"; |
|
|
|
import { VueMarkdown } from '@crazydos/vue-markdown' |
|
|
|
import rehypeRaw from 'rehype-raw' |
|
|
|
|
|
|
|
//选中的咨询模式 |
|
|
|
const userStore = useUserStore(); |
|
|
|
const userid="p0"+userStore.userInfoCont.userId; |
|
|
|
|
|
|
|
const regulaKey=import.meta.env.VITE_REGUL_AI_AGENT; |
|
|
|
const baseURL=import.meta.env.VITE_APP_BASE_API |
|
|
|
const regulaURL =`${baseURL}/aibot/agents/${regulaKey}/chat` |
|
|
|
const conversation=ref("") //当前会话的uuid |
|
|
|
const controller = ref<AbortController | null>(null) |
|
|
|
|
|
|
|
const interact_msg=ref<{ask:boolean,think:string,content:string}[]>([]) |
|
|
|
const interact_msg=ref<{ask:boolean,think:string,content:string}[]>([{ask:false,think:"是",content:`你好!我是你的AI助手, |
|
|
|
你可以直接向我提问,或者编辑表单,我会自动为你提供相关信息。`}]) |
|
|
|
|
|
|
|
// 输入框内容 |
|
|
|
const inputText = ref(''); |
|
|
|
const respMsg=ref("") //markdown 内容,问答智能体的动态回复内容 |
|
|
|
|
|
|
|
//消息体 |
|
|
|
interface message{ |
|
|
|
@ -37,29 +44,34 @@ interface chatRecord{ |
|
|
|
defineExpose({onSendParamToAI}) |
|
|
|
|
|
|
|
async function onSendParamToAI(arr:Array<{ uuids: string[]; params: { [key: string]: any } }>){ |
|
|
|
interact_msg.value=[] |
|
|
|
//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(userid,uid,ele.params) |
|
|
|
let para={ |
|
|
|
inputs: { |
|
|
|
"checkType":"travel", |
|
|
|
"checkInfo":JSON.stringify(ele.params) |
|
|
|
}, |
|
|
|
response_mode:"streaming", |
|
|
|
user:userid,//这里已经base64解析了 |
|
|
|
} |
|
|
|
//添加问题记录 |
|
|
|
interact_msg.value.push({ask:true,think:"", content:JSON.stringify(para)}) |
|
|
|
|
|
|
|
await doRequest(`${baseURL}/aibot/assisted/${uid}/workflow`,para) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
async function doRequest(_user:string,uuid:string,param:any){ |
|
|
|
async function doRequest(furl:string,param:any){ |
|
|
|
let mRespMsg="" |
|
|
|
const params={ |
|
|
|
"checkType":"travel", |
|
|
|
"checkInfo":JSON.stringify(param) |
|
|
|
} |
|
|
|
|
|
|
|
controller.value = new AbortController(); |
|
|
|
try{ |
|
|
|
const res= await doAiChat(`${baseURL}/aibot/assisted/${uuid}/workflow`,{ |
|
|
|
inputs: params, |
|
|
|
response_mode:"streaming", |
|
|
|
user:_user,//这里已经base64解析了 |
|
|
|
},controller.value.signal |
|
|
|
const res= await doAiChat( |
|
|
|
furl, |
|
|
|
param, |
|
|
|
controller.value.signal |
|
|
|
) |
|
|
|
|
|
|
|
if (!res.ok) { |
|
|
|
@ -86,6 +98,9 @@ async function doRequest(_user:string,uuid:string,param:any){ |
|
|
|
if(json.event==="text_chunk"){ |
|
|
|
//conversation.value=json.conversation_id |
|
|
|
mRespMsg+=json.data.text |
|
|
|
}else if(json.event==="message"){ |
|
|
|
respMsg.value+=json.answer |
|
|
|
mRespMsg+=json.answer |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
@ -95,19 +110,18 @@ async function doRequest(_user:string,uuid:string,param:any){ |
|
|
|
console.log('用户手动中断') |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
interact_msg.value.unshift({ask:true,think:"", content:JSON.stringify(param)}) |
|
|
|
respMsg.value="" |
|
|
|
const arr=mRespMsg.split("</think>") |
|
|
|
if(arr.length===1){ |
|
|
|
interact_msg.value.unshift({ask:false,think:"",content:arr[0]}) |
|
|
|
interact_msg.value.push({ask:false,think:"",content:arr[0]}) |
|
|
|
}else{ |
|
|
|
//思考模式 |
|
|
|
let st = arr[1].trim().match(/({.*})/s) |
|
|
|
if (st){ |
|
|
|
let res= JSON.parse(st[1]) |
|
|
|
interact_msg.value.unshift({ask:false,think:res.success,content:res.reason}) |
|
|
|
interact_msg.value.push({ask:false,think:res.success,content:res.reason}) |
|
|
|
}else{ |
|
|
|
interact_msg.value.unshift({ask:false,think:arr[0],content:arr[1]}) |
|
|
|
interact_msg.value.push({ask:false,think:arr[0],content:arr[1]}) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
@ -118,50 +132,242 @@ function resetContext(){ |
|
|
|
//props.closefunc() |
|
|
|
} |
|
|
|
|
|
|
|
// 发送消息 |
|
|
|
const sendMessage = () => { |
|
|
|
const content = inputText.value.trim(); |
|
|
|
if (!content) return; |
|
|
|
|
|
|
|
// 添加用户消息 |
|
|
|
interact_msg.value.push({ ask: true,think:"", content }); |
|
|
|
|
|
|
|
const params={ |
|
|
|
inputs: {"onlineSearch":"否", |
|
|
|
"useDataset":"是", |
|
|
|
"queryUrl":""}, |
|
|
|
query:content, |
|
|
|
response_mode:"streaming", |
|
|
|
conversation_id:conversation.value, |
|
|
|
user:userid |
|
|
|
} |
|
|
|
|
|
|
|
doRequest(regulaURL,params) |
|
|
|
|
|
|
|
// 清空输入框 |
|
|
|
inputText.value = ''; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//渲染完页面再执行 |
|
|
|
// 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" :class="{merr:msg.think==false}"> |
|
|
|
<div class="chat-container"> |
|
|
|
<!-- 头部 --> |
|
|
|
<div class="chat-header"> |
|
|
|
<span class="header-icon">🤖</span> |
|
|
|
AI 助手 |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- 消息区域 --> |
|
|
|
<div class="reply-area"> |
|
|
|
<template v-for="(msg, index) in interact_msg" :key="index"> |
|
|
|
<!-- 用户消息 --> |
|
|
|
<div v-if="msg.ask" class="message message-ask"> |
|
|
|
{{ msg.content }} |
|
|
|
<!-- <VueMarkdown :markdown="msg.content"></VueMarkdown> --> |
|
|
|
</div> |
|
|
|
<!-- AI 回复 --> |
|
|
|
<div v-else class="message message-resp" :class="{ 'message-error': msg.think == false }"> |
|
|
|
<VueMarkdown :markdown="msg.content" :rehype-plugins="[rehypeRaw]" ></VueMarkdown> |
|
|
|
</div> |
|
|
|
</template> |
|
|
|
|
|
|
|
<div v-show="respMsg" class="message message-resp"> |
|
|
|
<VueMarkdown :markdown="respMsg" :rehype-plugins="[rehypeRaw]" class="t_resp"></VueMarkdown> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- 底部输入区域 --> |
|
|
|
<div class="input-area"> |
|
|
|
<input |
|
|
|
v-model="inputText" |
|
|
|
placeholder="输入您的问题..." |
|
|
|
class="input-box" |
|
|
|
type="text" |
|
|
|
/> |
|
|
|
<button |
|
|
|
@click="sendMessage" |
|
|
|
:disabled="!inputText.trim()" |
|
|
|
class="send-button" |
|
|
|
> |
|
|
|
<span>发送</span> |
|
|
|
</button> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
</template> |
|
|
|
|
|
|
|
<style lang="scss" scoped> |
|
|
|
|
|
|
|
.reply_area{ |
|
|
|
width: 260px; |
|
|
|
background: white; |
|
|
|
.chat-container { |
|
|
|
width: 360px; |
|
|
|
background: #ffffff; |
|
|
|
border-radius: 20px; |
|
|
|
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.12); |
|
|
|
display: flex; |
|
|
|
flex-direction: column; |
|
|
|
overflow: hidden; |
|
|
|
font-family: 'Segoe UI', system-ui, -apple-system, sans-serif; |
|
|
|
} |
|
|
|
|
|
|
|
/* 头部样式 */ |
|
|
|
.chat-header { |
|
|
|
background: linear-gradient(180deg, #469edf, #bbdefb); |
|
|
|
color: white; |
|
|
|
padding: 8px 20px; |
|
|
|
font-size: 18px; |
|
|
|
font-weight: 600; |
|
|
|
text-align: center; |
|
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); |
|
|
|
} |
|
|
|
|
|
|
|
.header-icon { |
|
|
|
margin-right: 8px; |
|
|
|
font-size: 20px; |
|
|
|
} |
|
|
|
|
|
|
|
/* 消息区域 */ |
|
|
|
.reply-area { |
|
|
|
flex: 1; |
|
|
|
padding: 20px; |
|
|
|
overflow-y: auto; |
|
|
|
background: #f8f9ff; |
|
|
|
display: flex; |
|
|
|
flex-direction: column; |
|
|
|
overflow-wrap:anywhere; |
|
|
|
gap: 12px; |
|
|
|
} |
|
|
|
|
|
|
|
/* 消息气泡 */ |
|
|
|
.message { |
|
|
|
max-width: 85%; |
|
|
|
padding: 12px 16px; |
|
|
|
border-radius: 18px; |
|
|
|
line-height: 1.5; |
|
|
|
font-size: 14px; |
|
|
|
word-wrap: break-word; |
|
|
|
animation: fadeIn 0.3s ease-out; |
|
|
|
} |
|
|
|
|
|
|
|
/* 用户消息 - 右对齐 */ |
|
|
|
.message-ask { |
|
|
|
align-self: flex-end; |
|
|
|
background: linear-gradient(135deg, #4b6cb7 0%, #3a56a0 100%); |
|
|
|
color: white; |
|
|
|
border-bottom-right-radius: 6px; |
|
|
|
box-shadow: 0 4px 12px rgba(75, 108, 183, 0.25); |
|
|
|
} |
|
|
|
|
|
|
|
/* AI 回复 - 左对齐 */ |
|
|
|
.message-resp { |
|
|
|
align-self: flex-start; |
|
|
|
background: #e8f5e9; |
|
|
|
color: #2e7d32; |
|
|
|
border-bottom-left-radius: 6px; |
|
|
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.05); |
|
|
|
} |
|
|
|
|
|
|
|
/* 错误状态 */ |
|
|
|
.message-error { |
|
|
|
background: #ffebee; |
|
|
|
color: #c62828; |
|
|
|
} |
|
|
|
|
|
|
|
/* 底部输入区域 */ |
|
|
|
.input-area { |
|
|
|
display: flex; |
|
|
|
padding: 16px; |
|
|
|
background: white; |
|
|
|
border-top: 1px solid #e8eaf6; |
|
|
|
gap: 10px; |
|
|
|
} |
|
|
|
|
|
|
|
/* 输入框 */ |
|
|
|
.input-box { |
|
|
|
flex: 1; |
|
|
|
padding: 12px 18px; |
|
|
|
border: 1px solid #e0e0e0; |
|
|
|
border-radius: 24px; |
|
|
|
outline: none; |
|
|
|
font-size: 14px; |
|
|
|
transition: all 0.3s ease; |
|
|
|
background: #f5f7ff; |
|
|
|
} |
|
|
|
|
|
|
|
.input-box:focus { |
|
|
|
border-color: #4b6cb7; |
|
|
|
background: white; |
|
|
|
box-shadow: 0 0 0 3px rgba(75, 108, 183, 0.15); |
|
|
|
} |
|
|
|
|
|
|
|
/* 发送按钮 */ |
|
|
|
.send-button { |
|
|
|
padding: 12px 22px; |
|
|
|
background: linear-gradient(135deg, #4b6cb7 0%, #3a56a0 100%); |
|
|
|
color: white; |
|
|
|
border: none; |
|
|
|
border-radius: 24px; |
|
|
|
font-size: 14px; |
|
|
|
font-weight: 600; |
|
|
|
cursor: pointer; |
|
|
|
transition: all 0.3s ease; |
|
|
|
min-width: 64px; |
|
|
|
} |
|
|
|
|
|
|
|
.send-button:hover:not(:disabled) { |
|
|
|
transform: translateY(-2px); |
|
|
|
box-shadow: 0 6px 16px rgba(75, 108, 183, 0.35); |
|
|
|
filter: brightness(1.05); |
|
|
|
} |
|
|
|
|
|
|
|
.send-button:active:not(:disabled) { |
|
|
|
transform: translateY(0); |
|
|
|
} |
|
|
|
|
|
|
|
.send-button:disabled { |
|
|
|
opacity: 0.5; |
|
|
|
cursor: not-allowed; |
|
|
|
transform: none; |
|
|
|
} |
|
|
|
|
|
|
|
.t_ask{ |
|
|
|
margin: 5px; |
|
|
|
align-self: end; |
|
|
|
background-color: rgb(188 211 241); |
|
|
|
border-radius:10px; |
|
|
|
/* 动画效果 */ |
|
|
|
@keyframes fadeIn { |
|
|
|
from { |
|
|
|
opacity: 0; |
|
|
|
transform: translateY(8px); |
|
|
|
} |
|
|
|
to { |
|
|
|
opacity: 1; |
|
|
|
transform: translateY(0); |
|
|
|
} |
|
|
|
} |
|
|
|
.t_resp{ |
|
|
|
margin: 5px; |
|
|
|
font-size:14px; |
|
|
|
align-self: start; |
|
|
|
color: black; |
|
|
|
background-color: rgb(222, 243, 222); |
|
|
|
|
|
|
|
/* 滚动条美化 */ |
|
|
|
.reply-area::-webkit-scrollbar { |
|
|
|
width: 6px; |
|
|
|
} |
|
|
|
.merr{ |
|
|
|
background-color: rgb(253 176 211); |
|
|
|
|
|
|
|
.reply-area::-webkit-scrollbar-track { |
|
|
|
background: #f1f4ff; |
|
|
|
} |
|
|
|
|
|
|
|
.reply-area::-webkit-scrollbar-thumb { |
|
|
|
background: #4b6cb7; |
|
|
|
border-radius: 3px; |
|
|
|
} |
|
|
|
|
|
|
|
.reply-area::-webkit-scrollbar-thumb:hover { |
|
|
|
background: #3a56a0; |
|
|
|
} |
|
|
|
</style> |
|
|
|
|