<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;
try {
// 通过try cath捕获终止请求
const resp = await fetch(
configService.aiUrl + "/v1/api/completion",
{
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: ["text/event-stream", "application/json"],
Authorization: this.aiToken,
},
signal,
body: JSON.stringify({
conversation_id: "aedc12748b6211efa8234e3599f53586",
messages: [
{
content: this.userMessage,
role: "user",
},
],
}),
}
);
this.lodingAi = true; // 显示加载中
this.listData.push({
// 存储会话记录
oldUserMessage: this.userMessage,
messages: "",
idx: this.msgIndex++,
});
this.userMessage = ""; // 保存后清空上次记录
// 处理流数据
const reader = resp.body.getReader(); //读取返回结果的流数据方法,内置 .read()方法
const decoder = new TextDecoder("utf-8"); //解码器
while (true) {
//解构读取返回数据,在流式接口中,done 为请求结束,value 是未解码前的二进制数据
const { done, value } = await reader.read();
if (done) {
break;
}
const str = decoder.decode(value); //二进制解码
const text = str?.split("data:")[1] || "";
let textJson = null;
// 防止解析报错
try {
textJson = JSON.parse(text);
} catch (e) {
// console.log("解析 JSON 报错:", e);
}
if (!textJson?.data?.answer) {
// 数据读取完
this.lodingAi = false;
this.getDoc(this.listData[this.listData.length - 1]);
return;
} else {
// 更新会话记录
this.listData[this.listData.length - 1].messages =
textJson.data.answer;
this.scrollToBottom(); // 更新滚动条到最底部
// 储存对应文档以及文档名称
const doc_aggs = textJson?.data?.reference?.doc_aggs || [];
if (doc_aggs.length > 0) {
this.listData[this.listData.length - 1].docAggs = {
docId: doc_aggs[0].doc_id,
docName: doc_aggs[0].doc_name,
};
}
}
}
} catch (error) {
if (error.name === "AbortError") {
this.lodingAi = false;
console.log("Ai请求已被中止");
}
}
},
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 对话 fetch
最后编辑于 :
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
相关阅读更多精彩内容
- 使用postcss-pxtorem 项目中的px转换为rem,rem单位用于适配不同宽度的屏幕。 如何在vue-c...
- 基于vue + vuex + less + ES6/7 + webpack + fetch + vue-route...
- 在做项目的时候,遇到一个需求,通过后端返回生成二维码,并将页面变成一整张图片,可以分享给微信好友等功能,当时第一反...