uniapp中renderjs使用主要解决在APP端无法使用web端的js问题
如果只开发web端就不用考虑renderjs 跨端app中可以使用,解决通信阻塞问题
这是一个在app端使用sse的方案
下面展示一些。
<template>
<!-- #ifdef APP -->
<page-meta :root-font-size="footSize" style="display: block;"></page-meta>
<!-- #endif -->
<view class="page-top">
<!-- #ifdef APP -->
<view class="status_bar">
<view class="top_view"></view>
</view>
<!-- #endif -->
<uni-nav-bar fixed class="nav-title" left-icon="back" :title="aiName" backgroundColor="#f8f8f8" @clickLeft="toPrev"></uni-nav-bar>
<view class="cu-chat">
<blockquote v-for="(item,index) in chatList" :key="item.id">
<view class="cu-item" :class="{'self':item.self == 1}" v-if="item.self == 1">
<view class="main">
<view class="content shadow">
<text class="self-content">{{item.content}}</text>
</view>
</view>
<view class="cu-avatar radius" :style="{'background-image': 'url('+item.avatarImg+')'}"></view>
</view>
<view class="cu-item" v-else>
<view class="cu-avatar radius" :style="{'background-image': 'url('+item.avatarImg+')'}"></view>
<view class="main">
<view class="content shadow">
<text class="self-content">{{item.content}}</text>
</view>
<view class="flex chat-tools" v-if="item.isEnd && !item.self">
<view class="reloadContent cu-btn bg-blue sm" @click="reloadContent()" v-if="chatList.length == (index+1)">
刷新
</view>
<view class="copyContent cu-btn bg-blue sm" @click="copyContentBtn(item.content)">
复制
</view>
</view>
</view>
</view>
</blockquote>
</view>
<view class="cu-bar foot input">
<input class="solid-bottom" :placeholder="placeholderStr"
v-model="content"
placeholder-class="input-placeholder"
:adjust-position="false"
:focus="false"
maxlength="2500"
cursor-spacing="10"></input>
<button :prop="msg" :change:prop="renderScript.onChange" class="cu-btn bg-blue shadow" :disabled="ifSend" @click="sendMsg">发送</button>
</view>
</view>
</template>
<script>
// import {BASE_URL} from '@/util/request.js'
import {copyContent} from '@/util/tools.js'
// import {EventSourcePolyfill} from 'event-source-polyfill'
import avatarDef from '@/static/img/avatar_deft.png'
import aislogo from '@/static/img/aislogo.png'
export default {
data() {
return {
footSize: this.$footFontSize,
msg:{//监听msg中的变化去执行renderjs中的onchange方法
userId:'',
conversationId:'',
renderContent:''
},
chatList:[],
answerWords:[],
eventSource:null,
contentIdx:0,
placeholderStr:'',
aiLoadding:'',
aiName:'',
content:'',
welcome:'',
ifSend:false,
conversationId:'',
userId:'',
quesId:'',
quesIds:'',
subjectId:'',
planId:'',
batchId:'',
userPlanId:'',
user:{
nickName: '',
avatar: avatarDef,
},
aiUser:{
nickName: '',
avatar: aislogo,
}
}
},
onLoad(options){
this.quesId = options.quesID;
this.quesIds = options.quesIds;
this.subjectId = options.subjectId;
this.planId = options.planId;
this.batchId = options.batchId;
this.userPlanId = options.userPlanId;
let buffer = {
quesId:this.quesId
}
var that = this;
this.$myRequest({
url:'/api/bot/conversation',
method:'POST',
data:{
userPlanId:this.userPlanId,
quesId:this.quesId,
ran:Math.random()
}
}).then(res=>{
that.welcome = res.data.welcome;
that.conversationId = res.data.conversation_id;
that.content = res.data.first_content;
that.userId = res.data.userId;
that.placeholderStr = res.data.placeholder;
that.aiLoadding = res.data.aiLoadding;
that.aiName = res.data.aiName;
that.msg.userId = res.data.userId;
that.msg.conversationId = res.data.conversation_id;
that.msg.renderContent = res.data.first_content;
that.firstChat();
})
},
methods: {
firstChat(){
//首次进入在内容生成之前发送按钮不可使用
this.ifSend = true;
this.chatList.push({
modelRole: this.aiUser.nickName,
avatarImg: this.aiUser.avatar,
self: 0,
id: +new Date(),
content: this.welcome,
isEnd: false,
isList:false
})
this.chatList.push({
modelRole: this.user.nickName,
avatarImg: this.user.avatar,
self: 1,
id: +new Date()+1,
content: this.content,
isEnd: false,
isList:false
})
this.chatList.push({
modelRole: this.aiUser.nickName,
avatarImg: this.aiUser.avatar,
self: 0,
id: +new Date()+10,
content: this.aiLoadding,
isEnd: false,
isList:false
})
this.content = '';
this.scrollBottom(10000);
//创建EventSource 并发起会话
// this.eventSourceBox();
},
eventSourceBox(){
let lastItem = this.chatList[this.chatList.length - 1];
var that = this;
const url = BASE_URL+"/stream-chat/chat.php?question="+encodeURIComponent(this.content.replace(/\+/g, '{[$add$]}'))+"&conversationId="+this.conversationId+"&userId="+this.userId;
this.eventSource = new EventSourcePolyfill(url);
this.eventSource.onopen = (event)=>{
console.log("连接已建立");
}
this.content = '';
lastItem.contentList = [];
this.eventSource.onmessage = (event)=>{
try {
var result = JSON.parse(event.data);
// console.log(result);
if(result.time && result.content ){
lastItem.isList = true;
that.ifSend = true;//没有生成完毕禁止点击
if(lastItem.content == this.aiLoadding){
lastItem.content = '';
lastItem.content += result.content;
}else{
lastItem.content += result.content;
}
that.contentIdx += 1;
that.scrollBottom();
}
} catch (error) {
console.log(error);
}
}
// this.chatList[this.chatList.length - 1]['contentList'];
this.eventSource.onerror = (event)=>{
let lastItem = that.chatList[that.chatList.length - 1];
this.eventSource.close();
lastItem.isEnd = true;
that.ifSend = false;
console.log((new Date().getTime()), 'answer end error')
// console.error("发生错误:", JSON.stringify(event));
// console.error("发生错误:");
}
this.eventSource.onclose = (event)=>{
let lastItem = that.chatList[that.chatList.length - 1];
// console.log("连接已关闭", JSON.stringify(event.data));
this.eventSource.close();
isEnd = true;
console.log((new Date().getTime()), 'answer end close');
}
},
sendMsg(){
if (!this.content) {
return uni.showToast({
title:'发送内容不能为空',
icon:'none'
});
}
this.chatList.push({
modelRole: this.user.nickName,
avatarImg: this.user.avatar,
self: 1,
id: +new Date()+1,
content: this.content,
})
this.chatList.push({
modelRole: this.aiUser.nickName,
avatarImg: this.aiUser.avatar,
self: 0,
id: +new Date()+10,
content: this.aiLoadding,
isEnd: false,
isList:false
})
this.ifSend = true;
this.msg.renderContent = this.content;
this.content = '';
this.$nextTick(this.scrollBottom());
//创建eventSource
// this.eventSourceBox();
},
scrollBottom(height){
const query = uni.createSelectorQuery().select('.page-top');
query.boundingClientRect((data) => {
let pageScrollTop = Math.round(data.height);
if(height != undefined){
pageScrollTop = height;
}
// console.log(pageScrollTop);
setTimeout(() => {
uni.pageScrollTo({
scrollTop: pageScrollTop, //滚动的距离
duration: 0, //过渡时间
})
},50)
}).exec()
},
toPrev(){
uni.reLaunch({
url:'/pages/print/worksDetail/worksDetail?batchId='+this.batchId+'&subjectId=' + this.subjectId + "&quesIds=" + this.quesIds + "&planId=" + this.planId + '&userPlanId=' +this.userPlanId
})
},
copyContentBtn(item){
copyContent(item)
},
reloadContent(){
let ques = this.chatList[this.chatList.length - 2]['content'];
this.chatList[this.chatList.length - 1]['content'] = this.aiLoadding;
this.chatList[this.chatList.length - 1]['isEnd'] = false;
this.ifSend = true;
// console.log(ques);
this.msg.renderContent = '';
this.msg.renderContent = ' '+ques;
this.$nextTick(this.scrollBottom());
// this.eventSourceBox();
},
// 接收renderjs发回的数据
acceptDataFromRenderjs(options) {
let lastItem = this.chatList[this.chatList.length - 1];
this.ifSend = true;//没有生成完毕禁止点击
if(lastItem.content == this.aiLoadding){
lastItem.content = '';
lastItem.content += options.messageData;
}else{
lastItem.content += options.messageData;
}
this.scrollBottom();
// this.projNotices = JSON.parse(options.type)
},
acceptDataFromRenderjsEnd(options){
let lastItem = this.chatList[this.chatList.length - 1];
lastItem.isEnd = options.renderIsEnd;
this.ifSend = options.renderIfSend;//没有生成完毕禁止点击
}
}
}
</script>
<script module="renderScript" lang="renderjs">
import {BASE_URL} from '@/util/request.js'
import {EventSourcePolyfill} from 'event-source-polyfill'
export default {
data(){
return {
message:'',
renderIsEnd:false,
renderIfSend:false
}
},
mounted() {
},
methods: {
onChange(newValue, oldValue, ownerInstance, instance){
if(newValue.userId != '' && newValue.conversationId != ''){
this.sse(newValue.userId,newValue.conversationId,newValue.renderContent)
}
},
// 发送数据到service层
emitData() {
// #ifdef APP
UniViewJSBridge.publishHandler('onWxsInvokeCallMethod', {
cid: this._$id,
method: 'acceptDataFromRenderjs',
args: {
messageData: this.messageData
}
})
// #endif
// #ifdef H5
var options = {
messageData:this.messageData
};
this.acceptDataFromRenderjs(options);
// #endif
},
emitDataEnd(){
// #ifdef APP
UniViewJSBridge.publishHandler('onWxsInvokeCallMethod', {
cid: this._$id,
method: 'acceptDataFromRenderjsEnd',
args: {
renderIsEnd: this.renderIsEnd,
renderIfSend:this.renderIfSend
}
})
// #endif
// #ifdef H5
var options = {
renderIsEnd: this.renderIsEnd,
renderIfSend:this.renderIfSend
};
this.acceptDataFromRenderjsEnd(options);
// #endif
},
sse(userId,conversationId,renderContent){
// console.log(renderContent);
let that = this;
const url = BASE_URL+"/stream-chat/chat.php?question="+encodeURIComponent(renderContent.replace(/\+/g, '{[$add$]}'))+"&conversationId="+conversationId+"&userId="+userId;
const eventSource = new EventSourcePolyfill(url);
eventSource.onopen = (event)=>{
console.log("连接已建立");
}
eventSource.onmessage = (event)=>{
var result = JSON.parse(event.data);
if(result.time && result.content ){
that.messageData = '';
that.messageData = result.content;
that.emitData();
}
}
eventSource.onerror = (event)=>{
console.log((new Date().getTime()), 'answer end error')
that.renderIsEnd = true;
that.renderIfSend = false;
that.emitDataEnd();
}
eventSource.onclose = (event)=>{
// console.log("连接已关闭", JSON.stringify(event.data));
eventSource.close();
console.log((new Date().getTime()), 'answer end close');
}
}
}
}
</script>
<style lang="less">
/* #ifdef APP */
page {
background-color: #F1F1F1;
}
/* #endif */
</style>
<style lang="less" scoped>
/* #ifdef H5 */
page {
background-color: #F1F1F1;
}
/* #endif */
/* #ifdef APP */
.status_bar {
height: var(--status-bar-height);
width: 100%;
}
.top_view {
height: var(--status-bar-height);
width: 100%;
position: fixed;
background-color: #F8F8F8;
top: 0;
z-index: 999;
}
/* #endif */
.nav-title /deep/ .uni-nav-bar-text{
font-size: 0.3rem;
overflow: initial;
}
.cu-avatar{
background-color: #ffffff !important;
}
.self-content{
overflow: auto;
}
.main{
flex-direction: column;
align-items: flex-end !important;
}
.chat-tools{
padding: 10rpx;
justify-content: flex-end;
}
.chat-tools .cu-btn{
border-radius: 10rpx;
margin-left: 12rpx;
}
</style>