uniapp renderjs使用 SSE方案

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>
  • 10
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值