<template>
<view class="container">
<view>
<view v-for="item in listData" :key="item.id">
<view class="icon" style="justify-content: flex-end">
<view class="model-titel right"
><span> {{ item.oldUserMessage }}</span></view
>
<span class="icon-title right">: 提问</span
><img
style="margin-right: 0px; margin-left: 2px"
src="../../static/imgs/user-icon.png"
/></view>
<view class="icon">
<img
src="../../static/imgs/service-icon.png"
style="width: 22px; padding: 0 1px"
/><span class="icon-title"> 回复:</span>
<view class="model-titel"
><span> {{ item.messages }}</span></view
>
</view>
</view>
<view class="btn">
<view style="padding-bottom: 10rpx">
<u-input
v-model="userMessage"
:disabled="lodingAi"
@keyup.enter="sendMessage"
placeholder="输入消息"
/>
</view>
<u-button @click="sendMessage" :disabled="lodingAi">发送</u-button>
</view>
</view>
</view>
</template>
<script>
let controller = new AbortController();
let signal = controller.signal;
export default {
components: {},
data() {
return {
messages: [],
lodingAi: false,
userMessage: "",
msgIndex: 0,
listData: [
{
oldUserMessage: "测试问题",
messages: "测试AI回复",
id: 1,
},
],
eventSource: null,
};
},
methods: {
async sendMessage() {
if (!this.userMessage.trim()) return;
const obj = {
message: '问题',
model: { id: ’id-1‘, idx: 3}
}
// 数据处理
const queryString = JSON.stringify(obj); // 将对象转换为查询字符串
const param = encodeURIComponent(queryString); //转url编码
const param1 = param.replace(/\%/g, '%25'); // 转换%号传给后端
const param2 = param1.replace(/\+/g, '%2B'); // 转换+号传给后端
// eventSource 请求
this.eventSource = new EventSource(JAVA_API_BASE_URL +"/lark/getAnswerEmitterToWebUi?req=" + param2)
this.lodingAi = true; // 显示加载中
this.listData.push({
// 存储会话记录
oldUserMessage: this.userMessage,
messages: "",
idx: this.msgIndex++,
});
this.userMessage = ""; // 保存后清空上次记录
// 处理流数据
this.eventSource.onmessage = (event) => {
this.scrollToBottom(); // 更新滚动条到最底部
// 接口异常处理
if ( event.data &&this.isJSON(event.data) &&JSON.parse(event.data)?.errorCode === 406 ) {
this.$tip.toast("抱歉!提问解析答案异常,请联系相关开发人员解决");
return
}
const text = event.data;
textJson = JSON.parse(text);
let textJson = null;
if (textJson?.data?.answer) {
// 解析文字
const answer = textJson?.data?.answer?.replace(
/\*\*(.*?)\*\*/g,
"<strong>$1</strong>"
);
// const javastr = answer.replace(/System\.out\.println/g, 'console.log'); // 解析java代码, js直接读取会报错
const answer03 = answer.replace(
/```([\s\S]*?)```/g,
'<p style="background: rgb(52,53,65);padding: 10px;border-radius: 4px; color: rgb(255,255,255);font-size: 12px;" >$1</p>'
);
const answer01 = answer03.replace(/\\div/g, "/");
const answer0 = answer01.replace(/\\\[(.*?)\\\]/g, "[$1]");
const answer1 = answer0?.replace(/\##(.+?)\$\$/g, (match) => {
return `<a class="dialog" style="display: inline-block; width: 18px;" idx="${
this.listData.length - 1
}" id="${match}" href="javascript:void(0);" data-action="click"><img style="width: 18px; height:18px; margin-right: 0;" src=${imgicon} alt=""></a>`;
});
const answer2 = answer1?.replace(/### (.*)/g, "<h4>$1</h4>");
const answer3 = answer2.replace(/\\\((.*?)\\\)/g, "($1)");
// 更新会话记录
this.listData[this.listData.length - 1].messages = answer3 ;
// 更新弹窗内容
this.listData[this.listData.length - 1].chunks =
textJson?.data?.reference?.chunks || [];
this.scrollToBottom(); // 更新滚动条到最底部
// 储存对应文档以及文档名称
const doc_aggs = textJson?.data?.reference?.doc_aggs || [];
this.listData[this.listData.length - 1].docAggs = doc_aggs;
}
// 处理接收到的数据
};
this.eventSource.onerror = (error) => {
console.error("Error:", error);
this.eventSource.close();
this.lodingAi = false;
};
},
},
getDoc(v) {
console.log(v);
},
// 停止输出
stopMessage() {
controller.abort();
// 重新创建 AbortController 实例
controller = new AbortController();
signal = controller.signal;
},
},
created() {},
beforeDestroy() {
this.disconnect();
},
};
</script>
<style lang="scss" scoped>
.container {
padding: 5rpx 10rpx;
padding-bottom: 180rpx;
height: 100%;
.icon {
padding: 20rpx 20rpx;
display: flex;
align-items: flex-start;
img {
margin-right: 2px;
}
.model-titel {
padding-top: 2px;
flex: 1;
display: flex;
span {
white-space: pre-wrap;
padding: 5px 8px;
letter-spacing: 2px;
border-radius: 5px;
display: flex;
width: fit-content;
background: #fff;
}
}
.icon-title {
width: 3em;
text-align: left;
padding-top: 2px;
}
.right {
justify-content: flex-end;
span{
background: #1e90ff;
color: #fff;
}
}
}
}
.btn {
position: fixed;
z-index: 99;
bottom: 0;
width: 100%;
height: 180rpx;
padding: 10rpx 30rpx;
background: #fff;
.u-button {
}
}
</style>
基于uniapp/vue 通过实现ai 对话流 EventSource的形式
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
相关阅读更多精彩内容
- 使用postcss-pxtorem 项目中的px转换为rem,rem单位用于适配不同宽度的屏幕。 如何在vue-c...
- 在做项目的时候,遇到一个需求,通过后端返回生成二维码,并将页面变成一整张图片,可以分享给微信好友等功能,当时第一反...