xy 2 лет назад
Родитель
Сommit
7156e6b550

+ 489 - 3
pages/AI_chat/AI_chat.vue

@@ -1,15 +1,142 @@
 <template>
 	<view class="container" :style="{'top': viewTop + 'px', 'height': viewHeight + 'px'}">
-		AI_聊天
+		<view class="content">
+			<scroll-view scroll-y="true"  :scroll-top="scroll_top" style="top: 0%;width: 80vw; height:65%;position: absolute;">
+					<view class="scroll-view-text">
+						<view  style="margin-left: 13%; display: flex; align-items: center;justify-content: center;width: 100%;  user-select: text;" v-for="(msgIndex,index) in recvMsgQueue ">
+							<!-- ai 的消息结构体 -->
+							<view v-if="isAItell(recvMsgQueue[index].who_msg)"  style="width: 100%;height: 100%;margin-bottom: 1%;">
+								<text style=" font-size: 10rpx;color: #B34FFF;font-size: 10rpx;margin-left: 2%;background: linear-gradient(90deg, #24BF74 10%, #FAEE07 90%);-webkit-background-clip: text;-webkit-text-fill-color: transparent;">
+									{{ai_name}}
+								</text>
+								<view  style="margin-bottom: 3%; margin-top: 1%;margin-left: 1%;padding: 1%; width: 70%;background: #2A2832;border-radius: 10rpx 10rpx 10rpx 10rpx;">
+									<view @tap="handleClick()" style="width: 100%;background-color: #2A2832;color: white;" class="htmlContent" ref="htmlContent" v-html="returnText(recvMsgQueue[index].msg_text)"></view>
+									<view @click="stopRecv()" v-if="recvStatus&&recvMsgQueue[index].isFinish==false" style="display: flex;width: 100%;align-items: center;justify-content: center;">
+										<image src="../../static/closed.png" style="width: 50upx;height: 50upx;align-self: center;"></image>
+									</view>
+									
+								</view>
+								
+					
+								<view v-if="recvMsgQueue[index].isFinish==true" style="margin-left: 65%;">
+									<image src="../../static/msg-opt-3.png" style="margin-left: 10upx;width: 30upx;height: 30upx;"></image>
+									<image src="../../static/msg-opt-4.png" style="margin-left: 10upx;width: 30upx;height: 30upx;"></image>
+								</view>
+								
+							</view>
+						
+							<!-- 我发送 的消息结构体 -->
+							<view v-if="!isAItell(recvMsgQueue[index].who_msg)"  style="width: 100%;height: 100%;margin-top: 1%;">
+								
+								<view style=" font-size: 10rpx;color: #B34FFF;font-size: 10rpx;margin-left: 2%;">
+									{{user_name}}
+								</view>
+								<view  style="margin-bottom: 3%; margin-top: 1%;margin-left: 1%;padding: 1%; width: 70%;background: #2A2832;border-radius: 10rpx 10rpx 10rpx 10rpx;">
+									<view  style="width: 100%;background-color: #2A2832;color: white;" class="htmlContent" ref="htmlContent" v-html="returnText(recvMsgQueue[index].msg_text)"></view>
+	
+									
+								</view>
+								
+								<view style="margin-left: 65%;">
+									<image src="../../static/msg-opt-1.png" style="margin-left: 10upx; width: 30upx;height: 30upx;"></image>
+									<image src="../../static/msg-opt-2.png" style="margin-left: 10upx;width: 30upx;height: 30upx;"></image>
+									<image src="../../static/msg-opt-3.png" style="margin-left: 10upx;width: 30upx;height: 30upx;"></image>
+									<image src="../../static/msg-opt-4.png" style="margin-left: 10upx;width: 30upx;height: 30upx;"></image>
+								</view>
+								
+							</view>
+							
+						</view>
+					</view>
+			</scroll-view>
+			
+			<view style="display: flex;width: 70vw;height: 90upx;position: absolute;bottom:500upx;">
+					<view style="display: flex;width: 90%;  border-radius: 25px 25px 25px 25px;opacity: 1;border: 1px solid #32B3AA;width: 100%;" class="textarea-box">
+					
+						<view style="width: 90%; height: 100%; display: flex;align-items: center;justify-content: center" >
+							<textarea  v-model="inputValue" :cursor-spacing="15" class="textarea" auto-height="true"
+								@keydown.enter="onKeydown('enter')" :disabled="disabled"
+								@input="onKeyInput"
+								placeholder="请输入消息内容(使用 Enter 发送)" :maxlength="-1" 
+								placeholder-class="input-placeholder"></textarea>
+						</view>
+						<view v-if="recvStatus==false" style="width: 10%; height: 100%; display: flex; color: aliceblue;align-items: center;justify-content: center" @click="sendMsg">
+							<image src="../../static/img_send.png" style="width: 29.57upx;height: 37.67upx;" ></image>
+						</view>
+					</view>
+					<view style=" display: flex;align-items: center;justify-content: center;width: 5%;position: relative;">
+						<image @click="onclickdelete()" src="../../static/img_delete.png" style="width: 50upx;height: 70upx;"></image>
+					</view>
+			</view>
+		</view>
+		
+		<view style="position: absolute;top: 10%;right: 5%;width: 200upx;display: flex;align-items: center;justify-content: center;">
+			<view style="color: white;margin-right: 10upx;">关联上文</view>
+			<image @click="is_open_related=false" v-show="is_open_related==true" src="../../static/img-on.png" style="width: 60upx;height: 30upx;"></image>
+			<image @click="is_open_related=true" v-show="is_open_related==false" src="../../static/img-off.png" style="width: 60upx;height: 30upx;"></image>
+		</view>
 	</view>
 </template>
 
 <script>
+	import marked from '../marked/marked.min.js';
+	import config from '../index/config.js';
+	import hljs from "../highlight.js/lib/common.js";
+	import "../highlight.js/styles/atom-one-dark.css";
+	// import  '../v-copy.js';
+	// 键盘的shift键是否被按下
+	let shiftKeyPressed = false
 	export default {
+		components:{
+			marked
+		},
 		data() {
 			return {
+				is_open_related:false,
+				code:'',
+				title: 'Hello',
+				disabled: false,
+				socketOpen:false,
+				socketMsgQueue:[],
+				recvMsgQueue:[],
+				sendMsgQueue:[],
+				inputValue: '',
+				inputOldValue: '',
+				_socketTask:null,
+				scroll_text_:"\n",
+				sendStatus:false,
+				recvStatus:false,
+				all_src_list:[],
+				src_index:0,
+				scroll_top:0,
+				user_name:'ZYPX',  //用户名字
+				ai_name:"SmartAssistant", //ai名字
+				user_mobile:"18612220000",//用户手机号
+				ani:'',
+				ani_style:{
+					width: "100upx",height: "100upx",rotate: 0,left:"25%",right:"25%",position:"absolute"
+				},
+				angle:359,
+			}	
+		},
+		mounted(){
+				marked.setOptions({
+				          renderer: new marked.Renderer(),
+				          gfm: true,
+				          tables: true,
+				          breaks: false,
+				          pedantic: false,
+				          sanitize: false,
+				          smartLists: true,
+				          smartypants: false,
+						  highlight: function (code) {
+						  					return hljs.highlightAuto(code).value;
+						 },
+						 langPrefix:"hljs language-"
+				});
+				this.initTcp();
 				
-			}
+			
 		},
 		props: {
 			viewTop: {
@@ -22,17 +149,376 @@
 			}
 		},
 		methods: {
+			onclickdelete(){
+				let self = this;
+				if(self.recvMsgQueue.length>0&&self.recvMsgQueue[self.recvMsgQueue.length-1].isFinish){
+					uni.showModal({
+						showCancel:true,
+						content:"是否删除记录",
+						success:(res)=>{
+							if(res.confirm){
+								self.recvMsgQueue = []
+							}
+						}
+					})
+				}
+				
+			},
+			// #ifdef H5 && VUE2
+			onKeydown(keyname) {
+				let self = this;
+				if(self.inputOldValue!=self.inputValue){
+					 self.sendMsg();
+				}else{
+				
+				}
+			},
+			onKeyup(keyname) {
+				let self = this;
+				self.inputOldValue=self.inputValue;
+			},
+			// #endif
+			setCopy(content){
+			    // 使用#ifdef H5和#endif控制各端调用情况
+			
+			    // 该方法不支持h5
+			    //#ifndef H5
+			    uni.setClipboardData({
+			        data: String(content), // 必须字符串
+			        success: function () {
+			            console.log('success');
+			        }
+			    });
+			    //#endif
 			
+			    // h5端赋值方法,使用创建节点
+			    // #ifdef H5
+			    if (!document.queryCommandSupported('copy')) { // 兼容某些浏览器的判断
+			        console.log('该浏览器不支持')
+			    }
+			    let textarea = document.createElement("textarea")
+			    textarea.value = content
+			    textarea.readOnly = "readOnly"
+			    document.body.appendChild(textarea)
+			    textarea.select() // 选择对象
+			    textarea.setSelectionRange(0, content.length) // 核心
+			    let result = document.execCommand("copy") // 执行浏览器复制命令
+			    if (result) {
+			        uni.showToast({
+			            title: '复制成功',
+			            duration: 2000
+			        });
+			    }
+			    textarea.remove()
+			    // #endif
+			},
+			updateSrcList(){
+				let self = this;
+				let list = this.getIndexList(self.scroll_text_);
+				for (var i = 0; i < list.length; i+=2) {
+					let start = list[i];
+					let end = list[i+1];
+					self.all_src_list.push(self.scroll_text_.substring(start,end))
+				}	
+				console.log("updateSrcList",self.all_src_list,self.scroll_text_)
+			},
+			stopRecv(){
+				let self =this;
+				self._socketTask.close()
+				self.initTcp(true);
+				self.finish_recv();
+			},
+			 handleClick(e) {
+				e = e || window.event;
+				let target = e.target || e.srcElement;
+				let name = target.tagName.toLowerCase();
+				let self =this;
+				if(name.substring(0,3)=="abc"){
+					let name_list = name.split('_');
+					if(name_list.length>0){
+						let index = parseInt(name_list[1])-1;
+						console.log("handleClick",index,self.all_src_list)
+						self.setCopy(self.all_src_list[index])
+					}
+					console.log("handleClick",self.all_src_list[index])
+					console.log(target.tagName.toLowerCase())
+				}
+				console.log(name.substring(0,3))
+			  },
+			copyUpdates(){
+				let self = this;
+				let str = self.recvMsgQueue[self.recvMsgQueue.length-1].msg_text
+				let key = "</code></pre>";
+				let index = self.all_src_list.length;
+				while(str.indexOf(key) != -1){
+					index++;
+					let s_tag =   "</code><abc_"+index+" class=\"copy-button\">Copy</abc_"+index+"></pre>"
+					console.log("s_tag",s_tag)
+					str=str.replace("</code></pre>", s_tag)
+				}
+				// str=str.replaceAll("</code></pre>", "</code><abc class=\"copy-button\">Copy</abc></pre>")
+				self.recvMsgQueue[self.recvMsgQueue.length-1].msg_text = str
+				// console.log("copyUpdates",str)
+				self.$forceUpdate();
+			},	
+			
+			getIndexList(box_str){
+				let box = box_str;
+				let boxarr=[];
+				let pos = box.indexOf('```');
+				while(pos>-1){
+					boxarr.push(pos);
+					pos= box.indexOf('```',pos+1);
+				}
+				return boxarr
+		
+			},
+			returnText(text){
+				return "<style> body { background-color: black; color: white; } pre {position: relative;}.copy-button { position: absolute; top: 0;right: 0;padding: 4px 8px;background-color: #333;color: #fff;cursor: pointer;user-select: none;} p {line-height: 2;} li{line-height: 1.5;} table {border-collapse: collapse;width: 100%;font-family: Arial, sans-serif;font-size: 14px;}table th,table td {border: 1px solid #ddd;padding: 8px;text-align: left;}</style>" +text
+			},
+			// 滚动窗口以显示最新的一条消息
+			showLastMsg() {
+				let self = this;
+				let container = uni.createSelectorQuery().in(this).select(".scroll-view-text");
+				// 利用uniapp提供的接口获取可视区域的高度和滚动高度
+					// let query=uni.createSelectorQuery()
+					// let container=query.select('.box');
+					container.fields({
+						// rect:true,   //是否返回节点布局位置信息{left,top,right,bottom}
+						size:true,  //是否返回节点尺寸信息{width,height}
+						scrollOffset:true //是否返回节点滚动信息{scrollLeft,scrollTop}
+					},(res)=>{
+						self.scroll_top = res.height;
+						// console.log(res)
+				}).exec()
+			},
+			isAItell(type){
+				if(type===config.type_ai){
+					return true;
+				}
+				return false;
+			},
+			make_avatar(type){
+				if(type===config.type_ai){
+					return "../../static/PubImgs_avatar_avatar7.png";
+				}
+				return "../../static/PubImgs_avatar_avatar8.png";
+			},
+			isShow(text){
+				return text!="";
+			},
+			initAni(){
+				let self = this;
+				let action = uni.createAnimation({
+						  duration: 10000,
+						  transformOrigin:"50% 50% 0",
+						  timingFunction: "ease",
+						  delay: 0
+						})
+				self.angle+=360;
+				action.rotate(self.angle).step()
+				self.ani =  action.export()
+				setTimeout(self.initAni,10000)
+			},
+			tryReConnect(){
+				let self = this;
+				uni.showLoading({
+					title:"正在尝试重新连接"
+				})	
+				self.disabled = false;
+				self.socketOpen = false;
+				self.initTcp()
+			},
+			initTcp(isStopRecv=false){
+				let self = this;		
+				self._socketTask = new WebSocket("ws://47.88.86.123:8000/ws") ;
+				self._socketTask.onopen = function (res) {
+				  console.log('WebSocket连接已打开!');
+				  if(!isStopRecv){
+					  uni.showLoading({
+					  						title:"登录成功"
+					  })
+				  }
+				  self.socketMsgQueue = [];
+				  uni.hideLoading()
+				  self.socketOpen = true;
+					  
+				};
+				self._socketTask.onclose = (res)=>{
+					// uni.showModal({
+					// 	showCancel:false,
+					// 	title:"WebSocket is onclose "
+					// })
+					console.log("WebSocket is onclose")
+					self._socketTask.close()
+					self.tryReConnect()
+				};
+				self._socketTask.onerror= function (res) {
+					// uni.showModal({
+					// 	showCancel:false,
+					// 	title:"WebSocket连接打开失败,请检查"
+					// })
+				  console.log('WebSocket连接打开失败,请检查!');
+				};
+				
+				self._socketTask.onmessage = function (res) {
+				  console.log('收到服务器内容:' + res.data);
+				  let content = ''
+				  let data = JSON.parse(res.data);
+				  content = data.content+''
+				  if(self.sendStatus){
+					  self.sendStatus = false;
+					  // content = '▪ ';
+					  self.scroll_text_ = '';
+					  // let index = self.recvMsgQueue.length>0?self.recvMsgQueue.length:0
+					  self.recvMsgQueue.push({who_msg:config.type_ai,msg_type:0,msg_text:'',isFinish:false})
+					  self.startUpdateView()
+				  }
+					// 在dom渲染完毕后 使聊天窗口滚动到最后一条消息
+					self.$nextTick(() => {
+						self.showLastMsg()
+					})
+				  // console.log('收到服务器内容:' +content );
+				  let finish_reason = data.finish_reason+''
+				  if(content!='null'){
+					  self.scroll_text_+=content;
+					  // if(content=='.'||content=='。'){
+						 //  self.scroll_text_+='\n▪ ';
+					  // }
+				  }
+				  if(finish_reason!='null'){
+					  self.finish_recv()
+					  // self.recvStatus = false;
+					  // self.disabled = false;
+					  // clearTimeout(self.initAni)
+					  // self.scroll_text_+="\n"
+					  // console.log("self.recvMsgQueue",self.scroll_text_)
+					  // self.stopUpdateView();
+					  // self.copyUpdates();
+					  // self.updateSrcList();
+					  // self.recvMsgQueue[self.recvMsgQueue.length-1].isFinish = true;
+				
+				  }else{
+					self.recvMsgQueue[self.recvMsgQueue.length-1].msg_text = marked.parse(self.scroll_text_);
+				  }
+				  // console.log("self.recvMsgQueue.length",self.recvMsgQueue.length)
+				};
+			},
+			async finish_recv(){
+				let self = this;
+				self.recvStatus = false;
+				self.disabled = false;
+				clearTimeout(self.initAni)
+				self.scroll_text_+="\n"
+				console.log("self.recvMsgQueue",self.scroll_text_)
+				self.stopUpdateView();
+				self.copyUpdates();
+				self.updateSrcList();
+				self.recvMsgQueue[self.recvMsgQueue.length-1].isFinish = true;
+			},
+			async sendMsg(){
+				// 如果内容为空
+				if (!this.inputValue) {
+					// 弹出提示框
+					return uni.showToast({
+						// 提示内容
+						title: '内容不能为空',
+						// 不显示图标
+						icon: 'none'
+					});
+				}
+				this.sendSocketMessage(this.inputValue)
+			},
+			onKeyInput(event) {
+				// console.log("onKeyInput",event.target.value)
+				let self = this;
+				// self.inputOldValue=self.inputValue
+			},
+			startUpdateView(){
+				let self = this;
+				self.$forceUpdate();
+				setTimeout(self.startUpdateView,500)
+			},
+			stopUpdateView(){
+				let self = this;
+				clearTimeout(self.startUpdateView)
+			},
+			sendSocketMessage(msg) {
+				let self = this;
+			  if (this.socketOpen) {
+				 //  uni.showLoading({
+					// title:"正在发送"
+				 //  })
+				self.sendStatus = true;
+				self.disabled = true;
+				self.ani_style.rotate = 0;
+				self.inputValue = '';
+				self.initAni();
+				self.recvMsgQueue.push({who_msg:config.type_self,msg_type:0,msg_text:msg,isFinish:false})
+				self.recvStatus = true;
+				let is_related = self.is_open_related ? 1: 0;
+				let mobile = self.user_mobile;
+				let msg_json = {"msg":msg,"mobile":mobile,"is_related":is_related};
+				self._socketTask.send(JSON.stringify(msg_json))
+			  } else {
+			    this.socketMsgQueue.push(msg);
+			  }
+			},
+		
+		
 		}
 	}
 </script>
 
-<style lang="scss">
+<style>
 	.container{
 		display: flex;
 		box-sizing: border-box;
+		flex-direction: column;
 		position: fixed;
+		/* position: relative; */
+		/* background-color: #2A2832; */
 		left: 0;
 		width: 100%;
+		justify-content: center;
+	}
+	.content {
+		height: 100%;
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		justify-content: center;
+		position: relative;
+		background-color: #2A2832;
+	}
+	.text-area {
+		display: flex;
+		justify-content: center;
+	}
+
+	.title {
+		font-size: 36rpx;
+		color: #8f8f94;
 	}
+	
+	.textarea-box,
+	.textarea {
+		width: 100%;
+		height: 100%;
+		margin-left: 3%;
+	},
+	textarea-box {
+		width: 100% !important;
+	}
+	
+	.textarea-box,
+	.textarea,
+	textarea,
+	textarea-box {
+		/* height: 120px; */
+	}
+
+	.textarea-box {
+		background-color: #FFF;
+	}
 </style>

+ 530 - 0
pages/index/废弃的头像模式.md

@@ -0,0 +1,530 @@
+<template>
+	<view class="container" :style="{'top': viewTop + 'px', 'height': viewHeight + 'px'}">
+		<view class="content">
+			<scroll-view scroll-y="true" :scroll-top="scroll_top"
+				style="top: 0%;width: 80vw; height:65%;position: absolute;">
+				<view class="scroll-view-text">
+					<view style=" margin-right: 5%; margin-left: 5%; margin-top: 3%; display: flex;user-select: text;"
+						v-for="(msgIndex,index) in recvMsgQueue ">
+						<!-- ai 的消息结构体 -->
+						<view v-if="isAItell(recvMsgQueue[index].who_msg)"
+							style="display: flex; width: 100%;height: 100%;margin-bottom: 3%;">
+							<image :src="make_avatar(recvMsgQueue[index].who_msg)"
+								style="width: 150upx;height: 150upx;margin-right: 1%;"></image>
+							<view
+								style="margin-bottom: 3%; margin-top: 1%;margin-left: 1%;padding: 1%; width: 70%;background: #EFEFEF;border-radius: 10rpx 10rpx 10rpx 10rpx;">
+								<!-- <text  style="width: 100%;align-self: auto;">
+										{{recvMsgQueue[index].msg_text}}
+									</text> -->
+								<view @tap="handleClick()" style="width: 100%;" class="htmlContent" ref="htmlContent"
+									v-html="returnText(recvMsgQueue[index].msg_text)"></view>
+								<view @click="stopRecv()" v-if="recvStatus&&recvMsgQueue[index].isFinish==false"
+									style="display: flex;width: 100%;align-items: center;justify-content: center;">
+									<image src="../../static/closed.png"
+										style="width: 50upx;height: 50upx;align-self: center;"></image>
+								</view>
+
+							</view>
+						</view>
+
+						<!-- 我发送 的消息结构体 -->
+						<view v-if="!isAItell(recvMsgQueue[index].who_msg)"
+							style="display: flex; width: 100%;height: 100%;margin-bottom: 3%;">
+
+							<view style="justify-content: end;display: flex; width: 100%;height: 100%; ">
+								<view
+									style="justify-content: space-between; display: flex;;align-items:center; padding: 1%;margin-right: 2%;background:green;border-radius: 10rpx 10rpx 10rpx 10rpx;">
+									<view></view>
+									<text style="align-self: auto;color: aliceblue;">
+										{{recvMsgQueue[index].msg_text}}
+									</text>
+								</view>
+								<view>
+									<image :src="make_avatar(recvMsgQueue[index].who_msg)"
+										style="width: 150upx;height: 150upx;right: 0%;"></image>
+								</view>
+
+							</view>
+
+						</view>
+
+					</view>
+				</view>
+			</scroll-view>
+
+			<view style="display: flex;width: 70vw;height: 90upx;position: absolute;bottom:500upx;">
+				<view
+					style="display: flex;width: 90%;  border-radius: 25px 25px 25px 25px;opacity: 1;border: 1px solid #32B3AA;width: 100%;"
+					class="textarea-box">
+
+					<view style="width: 90%; height: 100%; display: flex;align-items: center;justify-content: center">
+						<textarea v-model="inputValue" :cursor-spacing="15" class="textarea" auto-height="true"
+							@keyup.shift="onKeyup('shift')" @keydown.shift="onKeydown('shift')"
+							@keydown.enter="onKeydown('enter')" :disabled="disabled" placeholder="请输入消息内容(使用 Enter 发送)"
+							:maxlength="-1" placeholder-class="input-placeholder"></textarea>
+					</view>
+					<view v-if="recvStatus==false"
+						style="width: 10%; height: 100%; display: flex; color: aliceblue;align-items: center;justify-content: center"
+						@click="sendMsg">
+						<image src="../../static/img_send.png" style="width: 29.57upx;height: 37.67upx;"></image>
+					</view>
+				</view>
+				<view style=" display: flex;align-items: center;justify-content: center;width: 5%;position: relative;">
+					<image src="../../static/img_delete.png" style="width: 50upx;height: 70upx;"></image>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import marked from '../marked/marked.min.js';
+	import config from '../index/config.js';
+	import hljs from "../highlight.js/lib/common.js";
+	import "../highlight.js/styles/atom-one-dark.css";
+	// import  '../v-copy.js';
+	// 键盘的shift键是否被按下
+	let shiftKeyPressed = false
+	export default {
+		components: {
+			marked
+		},
+		data() {
+			return {
+				code: '',
+				title: 'Hello',
+				disabled: false,
+				socketOpen: false,
+				socketMsgQueue: [],
+				recvMsgQueue: [],
+				sendMsgQueue: [],
+				inputValue: '',
+				_socketTask: null,
+				scroll_text_: "\n",
+				sendStatus: false,
+				recvStatus: false,
+				all_src_list: [],
+				src_index: 0,
+				scroll_top: 0,
+				ani: '',
+				ani_style: {
+					width: "100upx",
+					height: "100upx",
+					rotate: 0,
+					left: "25%",
+					right: "25%",
+					position: "absolute"
+				},
+				angle: 359,
+			}
+		},
+		mounted() {
+			marked.setOptions({
+				renderer: new marked.Renderer(),
+				gfm: true,
+				tables: true,
+				breaks: false,
+				pedantic: false,
+				sanitize: false,
+				smartLists: true,
+				smartypants: false,
+				highlight: function(code) {
+					return hljs.highlightAuto(code).value;
+				},
+				langPrefix: "hljs language-"
+			});
+			this.initTcp();
+
+
+		},
+		props: {
+			viewTop: {
+				type: Number,
+				default: 0
+			},
+			viewHeight: {
+				type: Number,
+				default: 0
+			}
+		},
+		methods: {
+			// #ifdef H5 && VUE2
+			onKeydown(keyname) {
+				if (keyname == 'shift') {
+					//按下了shift键
+					shiftKeyPressed = true;
+				}
+				let self = this;
+				// 按下了回车 且 之前没按下 shift
+				if (keyname == 'enter' && !shiftKeyPressed) {
+					this.$nextTick(() => {
+						// self.sendMsg();
+					})
+				}
+			},
+			onKeyup(keyname) {
+				if (keyname == 'shift') {
+					//按下了shift键
+					shiftKeyPressed = false;
+				}
+			},
+			// #endif
+			setCopy(content) {
+				// 使用#ifdef H5和#endif控制各端调用情况
+
+				// 该方法不支持h5
+				//#ifndef H5
+				uni.setClipboardData({
+					data: String(content), // 必须字符串
+					success: function() {
+						console.log('success');
+					}
+				});
+				//#endif
+
+				// h5端赋值方法,使用创建节点
+				// #ifdef H5
+				if (!document.queryCommandSupported('copy')) { // 兼容某些浏览器的判断
+					console.log('该浏览器不支持')
+				}
+				let textarea = document.createElement("textarea")
+				textarea.value = content
+				textarea.readOnly = "readOnly"
+				document.body.appendChild(textarea)
+				textarea.select() // 选择对象
+				textarea.setSelectionRange(0, content.length) // 核心
+				let result = document.execCommand("copy") // 执行浏览器复制命令
+				if (result) {
+					uni.showToast({
+						title: '复制成功',
+						duration: 2000
+					});
+				}
+				textarea.remove()
+				// #endif
+			},
+			updateSrcList() {
+				let self = this;
+				let list = this.getIndexList(self.scroll_text_);
+				for (var i = 0; i < list.length; i += 2) {
+					let start = list[i];
+					let end = list[i + 1];
+					self.all_src_list.push(self.scroll_text_.substring(start, end))
+				}
+				console.log("updateSrcList", self.all_src_list, self.scroll_text_)
+			},
+			stopRecv() {
+				let self = this;
+				self._socketTask.close()
+				self.initTcp(true);
+				self.finish_recv();
+			},
+			handleClick(e) {
+				e = e || window.event;
+				let target = e.target || e.srcElement;
+				let name = target.tagName.toLowerCase();
+				let self = this;
+				if (name.substring(0, 3) == "abc") {
+					let name_list = name.split('_');
+					if (name_list.length > 0) {
+						let index = parseInt(name_list[1]) - 1;
+						console.log("handleClick", index, self.all_src_list)
+						self.setCopy(self.all_src_list[index])
+					}
+					console.log("handleClick", self.all_src_list[index])
+					console.log(target.tagName.toLowerCase())
+				}
+				console.log(name.substring(0, 3))
+			},
+			copyUpdates() {
+				let self = this;
+				let str = self.recvMsgQueue[self.recvMsgQueue.length - 1].msg_text
+				let key = "</code></pre>";
+				let index = self.all_src_list.length;
+				while (str.indexOf(key) != -1) {
+					index++;
+					let s_tag = "</code><abc_" + index + " class=\"copy-button\">Copy</abc_" + index + "></pre>"
+					console.log("s_tag", s_tag)
+					str = str.replace("</code></pre>", s_tag)
+				}
+				// str=str.replaceAll("</code></pre>", "</code><abc class=\"copy-button\">Copy</abc></pre>")
+				self.recvMsgQueue[self.recvMsgQueue.length - 1].msg_text = str
+				// console.log("copyUpdates",str)
+				self.$forceUpdate();
+			},
+
+			getIndexList(box_str) {
+				let box = box_str;
+				let boxarr = [];
+				let pos = box.indexOf('```');
+				while (pos > -1) {
+					boxarr.push(pos);
+					pos = box.indexOf('```', pos + 1);
+				}
+				return boxarr
+
+			},
+			returnText(text) {
+				return "<style> pre {position: relative;}.copy-button { position: absolute; top: 0;right: 0;padding: 4px 8px;background-color: #333;color: #fff;cursor: pointer;user-select: none;} p {line-height: 2;} li{line-height: 1.5;} table {border-collapse: collapse;width: 100%;font-family: Arial, sans-serif;font-size: 14px;}table th,table td {border: 1px solid #ddd;padding: 8px;text-align: left;}</style>" +
+					text
+			},
+			// 滚动窗口以显示最新的一条消息
+			showLastMsg() {
+				let self = this;
+				let container = uni.createSelectorQuery().in(this).select(".scroll-view-text");
+				// 利用uniapp提供的接口获取可视区域的高度和滚动高度
+				// let query=uni.createSelectorQuery()
+				// let container=query.select('.box');
+				container.fields({
+					// rect:true,   //是否返回节点布局位置信息{left,top,right,bottom}
+					size: true, //是否返回节点尺寸信息{width,height}
+					scrollOffset: true //是否返回节点滚动信息{scrollLeft,scrollTop}
+				}, (res) => {
+					self.scroll_top = res.height;
+					// console.log(res)
+				}).exec()
+			},
+			isAItell(type) {
+				if (type === config.type_ai) {
+					return true;
+				}
+				return false;
+			},
+			make_avatar(type) {
+				if (type === config.type_ai) {
+					return "../../static/PubImgs_avatar_avatar7.png";
+				}
+				return "../../static/PubImgs_avatar_avatar8.png";
+			},
+			isShow(text) {
+				return text != "";
+			},
+			initAni() {
+				let self = this;
+				let action = uni.createAnimation({
+					duration: 10000,
+					transformOrigin: "50% 50% 0",
+					timingFunction: "ease",
+					delay: 0
+				})
+				self.angle += 360;
+				action.rotate(self.angle).step()
+				self.ani = action.export()
+				setTimeout(self.initAni, 10000)
+			},
+			tryReConnect() {
+				let self = this;
+				uni.showLoading({
+					title: "正在尝试重新连接"
+				})
+				self.disabled = false;
+				self.socketOpen = false;
+				self.initTcp()
+			},
+			initTcp(isStopRecv = false) {
+				let self = this;
+				self._socketTask = new WebSocket("ws://47.88.86.123:8000/ws");
+				self._socketTask.onopen = function(res) {
+					console.log('WebSocket连接已打开!');
+					if (!isStopRecv) {
+						uni.showLoading({
+							title: "登录成功"
+						})
+					}
+					self.socketMsgQueue = [];
+					uni.hideLoading()
+					self.socketOpen = true;
+
+				};
+				self._socketTask.onclose = (res) => {
+					// uni.showModal({
+					// 	showCancel:false,
+					// 	title:"WebSocket is onclose "
+					// })
+					console.log("WebSocket is onclose")
+					self._socketTask.close()
+					self.tryReConnect()
+				};
+				self._socketTask.onerror = function(res) {
+					// uni.showModal({
+					// 	showCancel:false,
+					// 	title:"WebSocket连接打开失败,请检查"
+					// })
+					console.log('WebSocket连接打开失败,请检查!');
+				};
+
+				self._socketTask.onmessage = function(res) {
+					console.log('收到服务器内容:' + res.data);
+					let content = ''
+					let data = JSON.parse(res.data);
+					content = data.content + ''
+					if (self.sendStatus) {
+						self.sendStatus = false;
+						// content = '▪ ';
+						self.scroll_text_ = '';
+						// let index = self.recvMsgQueue.length>0?self.recvMsgQueue.length:0
+						self.recvMsgQueue.push({
+							who_msg: config.type_ai,
+							msg_type: 0,
+							msg_text: '',
+							isFinish: false
+						})
+						self.startUpdateView()
+					}
+					// 在dom渲染完毕后 使聊天窗口滚动到最后一条消息
+					self.$nextTick(() => {
+						self.showLastMsg()
+					})
+					// console.log('收到服务器内容:' +content );
+					let finish_reason = data.finish_reason + ''
+					if (content != 'null') {
+						self.scroll_text_ += content;
+						// if(content=='.'||content=='。'){
+						//  self.scroll_text_+='\n▪ ';
+						// }
+					}
+					if (finish_reason != 'null') {
+						self.finish_recv()
+						// self.recvStatus = false;
+						// self.disabled = false;
+						// clearTimeout(self.initAni)
+						// self.scroll_text_+="\n"
+						// console.log("self.recvMsgQueue",self.scroll_text_)
+						// self.stopUpdateView();
+						// self.copyUpdates();
+						// self.updateSrcList();
+						// self.recvMsgQueue[self.recvMsgQueue.length-1].isFinish = true;
+
+					} else {
+						self.recvMsgQueue[self.recvMsgQueue.length - 1].msg_text = marked.parse(self.scroll_text_);
+					}
+					// console.log("self.recvMsgQueue.length",self.recvMsgQueue.length)
+				};
+			},
+			async finish_recv() {
+				let self = this;
+				self.recvStatus = false;
+				self.disabled = false;
+				clearTimeout(self.initAni)
+				self.scroll_text_ += "\n"
+				console.log("self.recvMsgQueue", self.scroll_text_)
+				self.stopUpdateView();
+				self.copyUpdates();
+				self.updateSrcList();
+				self.recvMsgQueue[self.recvMsgQueue.length - 1].isFinish = true;
+			},
+			async sendMsg() {
+				// 如果内容为空
+				if (!this.inputValue) {
+					// 弹出提示框
+					return uni.showToast({
+						// 提示内容
+						title: '内容不能为空',
+						// 不显示图标
+						icon: 'none'
+					});
+				}
+				this.sendSocketMessage(this.inputValue)
+			},
+			onKeyInput(event) {
+				this.inputValue = event.target.value
+			},
+			startUpdateView() {
+				let self = this;
+				self.$forceUpdate();
+				setTimeout(self.startUpdateView, 500)
+			},
+			stopUpdateView() {
+				let self = this;
+				clearTimeout(self.startUpdateView)
+			},
+			sendSocketMessage(msg) {
+				let self = this;
+				if (this.socketOpen) {
+					//  uni.showLoading({
+					// title:"正在发送"
+					//  })
+					self.sendStatus = true;
+					self.disabled = true;
+					self.ani_style.rotate = 0;
+					self.inputValue = '';
+					self.initAni();
+					self.recvMsgQueue.push({
+						who_msg: config.type_self,
+						msg_type: 0,
+						msg_text: msg,
+						isFinish: false
+					})
+					self.recvStatus = true;
+					let msg_json = {
+						"msg": msg,
+						"mobile": "18612220000",
+						"is_related": "1"
+					};
+					self._socketTask.send(JSON.stringify(msg_json))
+				} else {
+					this.socketMsgQueue.push(msg);
+				}
+			},
+
+
+		}
+	}
+</script>
+
+<style>
+	.container {
+		display: flex;
+		box-sizing: border-box;
+		flex-direction: column;
+		position: fixed;
+		/* position: relative; */
+		/* background-color: #2A2832; */
+		left: 0;
+		width: 100%;
+		justify-content: center;
+	}
+
+	.content {
+		height: 100%;
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		justify-content: center;
+		position: relative;
+		background-color: #2A2832;
+	}
+
+	.text-area {
+		display: flex;
+		justify-content: center;
+	}
+
+	.title {
+		font-size: 36rpx;
+		color: #8f8f94;
+	}
+
+	.textarea-box,
+	.textarea {
+		width: 100%;
+		height: 100%;
+		margin-left: 3%;
+	}
+
+	,
+	textarea-box {
+		width: 100% !important;
+	}
+
+	.textarea-box,
+	.textarea,
+	textarea,
+	textarea-box {
+		/* height: 120px; */
+	}
+
+	.textarea-box {
+		background-color: #FFF;
+	}
+</style>

BIN
static/img-off.png


BIN
static/img-on.png


BIN
static/img_delete.png


BIN
static/img_send.png


BIN
static/msg-opt-1.png


BIN
static/msg-opt-2.png


BIN
static/msg-opt-3.png


BIN
static/msg-opt-4.png