|  |  |  | <template> | 
					
						
							|  |  |  |   <view class="chat-wrap__main"> | 
					
						
							|  |  |  |     <view class="chat-wrap__main-content" :style="{ 'margin-bottom': `${chatMainMrgBottom}px` }"> | 
					
						
							|  |  |  |       <!-- <view>显示内容</view> --> | 
					
						
							|  |  |  |       <!-- <ClientChat @send="onSendQuestion" /> --> | 
					
						
							|  |  |  |       <scroll-view class="chat-wrapper" :scroll-into-view="bottom" :scroll-y="true" :scroll-with-animation="true" scroll-into-view-offset="10"> | 
					
						
							|  |  |  |         <view class="list-container"> | 
					
						
							|  |  |  |           <view class="info"> | 
					
						
							|  |  |  |             <view class="head-img"> | 
					
						
							|  |  |  |               <image class="rot-head-img" :src="robotObj.headImage"></image> | 
					
						
							|  |  |  |             </view> | 
					
						
							|  |  |  |             <view class="rot-title"> | 
					
						
							|  |  |  |               <view>你好!我是数字领航员</view> | 
					
						
							|  |  |  |               <view>{{ robotObj.name }}</view> | 
					
						
							|  |  |  |             </view> | 
					
						
							|  |  |  |           </view> | 
					
						
							|  |  |  |           <!-- for循环 --> | 
					
						
							|  |  |  |           <view class="chat-list" v-for="n,index in msgList" :key="index"> | 
					
						
							|  |  |  |             <view class="msg-container other" v-if="n.type =='reply' "> | 
					
						
							|  |  |  |               <!-- <image class="ava" :src="robotObj.headImage"></image> --> | 
					
						
							|  |  |  |               <view class="msg"> | 
					
						
							|  |  |  |                 <!-- <view class="msg-nickname">{{robotObj.name}}</view> --> | 
					
						
							|  |  |  |                 <view :class="n.contentType==='img' ? 'msg-content-img' : '' " class="msg-content"> | 
					
						
							|  |  |  |                   <template v-if="n.pending"> | 
					
						
							|  |  |  |                     <Pending></Pending> | 
					
						
							|  |  |  |                   </template> | 
					
						
							|  |  |  |                   <template v-else> | 
					
						
							|  |  |  |                     <template v-if="n.contentType === 'text'"> | 
					
						
							|  |  |  |                       <rich-text v-if="n.contentType === 'text'" :nodes="n.content"></rich-text> | 
					
						
							|  |  |  |                       <view v-if="n.is_final" class="msg-btns"> | 
					
						
							|  |  |  |                         <image class="btn-img" @click="copy(n.content)" src="./../../static/imgs/icon-copy.png"></image> | 
					
						
							|  |  |  |                         <image class="btn-img" @click="audio(n)" :src="audioActive == n.timestamp ?  './../../static/imgs/icon-bf-active.png':'./../../static/imgs/icon-bf.png'"></image> | 
					
						
							|  |  |  |                       </view> | 
					
						
							|  |  |  |                     </template> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     <image v-else-if="n.contentType === 'img'" mode="widthFix" class="cimg" src="./../../static/imgs/more.png"></image> | 
					
						
							|  |  |  |                   </template> | 
					
						
							|  |  |  |                 </view> | 
					
						
							|  |  |  |               </view> | 
					
						
							|  |  |  |             </view> | 
					
						
							|  |  |  |             <view v-if="n.type==='send' " class="msg-container self"> | 
					
						
							|  |  |  |               <view class="msg"> | 
					
						
							|  |  |  |                 <!-- <view class="msg-nickname">本人</view> --> | 
					
						
							|  |  |  |                 <view class="msg-content" :class="n.contentType==='img' ? 'msg-content-img' : '' "> | 
					
						
							|  |  |  |                   <text v-if="n.contentType === 'text'">{{n.content}}</text> | 
					
						
							|  |  |  |                   <image v-else-if="n.contentType === 'img'" mode="widthFix" class="cimg" :src="n.content"></image> | 
					
						
							|  |  |  |                 </view> | 
					
						
							|  |  |  |               </view> | 
					
						
							|  |  |  |               <!-- <image class="ava" src="./../../static/imgs/more.png"></image> --> | 
					
						
							|  |  |  |             </view> | 
					
						
							|  |  |  |           </view> | 
					
						
							|  |  |  |         </view> | 
					
						
							|  |  |  |         <view id="bottom" style="opacity:0"> | 
					
						
							|  |  |  |         </view> | 
					
						
							|  |  |  |       </scroll-view> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     </view> | 
					
						
							|  |  |  |     <view class="chat-wrap__main-footer"> | 
					
						
							|  |  |  |       <view class="disabled-loadding" v-if="disabledStatus"></view> | 
					
						
							|  |  |  |       <view class="chatinput-wrapper"> | 
					
						
							|  |  |  |         <view class="chatinput-content"> | 
					
						
							|  |  |  |           <view class='chatinput-input-wrap' v-if="videoStatus"> | 
					
						
							|  |  |  |             <view style="text-align: center; flex: 1; color:#9FA0A0" @longpress="startVideo" @touchend="stopVideo"> | 
					
						
							|  |  |  |               按住 说话 | 
					
						
							|  |  |  |             </view> | 
					
						
							|  |  |  |           </view> | 
					
						
							|  |  |  |           <view v-else class='chatinput-input-wrap'> | 
					
						
							|  |  |  |             <input v-model="inputValue" @focus="inputFocus" @input="inputChange" @confirm="inputSend" placeholder="想对TA说点什么呢…" confirm-type='send' /> | 
					
						
							|  |  |  |           </view> | 
					
						
							|  |  |  |           <view class="chatinput-btn-wrap"> | 
					
						
							|  |  |  |             <!-- <image v-if="!isEditInput" :src="videoStatus ? './../../static/imgs/icon-txt.png' :'./../../static/imgs/icon-video.png'" class='chatinput-img' @click="changeVideoStatus"></image> --> | 
					
						
							|  |  |  |             <!-- <image v-if="!isEditInput" src="./../../static/imgs/icon-pic.png" class='chatinput-img' @click="tapChooseImage"></image> --> | 
					
						
							|  |  |  |             <!-- <button v-if="isEditInput" class="chatinput-send">发送</button> --> | 
					
						
							|  |  |  |             <template v-if="videoStatus"> | 
					
						
							|  |  |  |               <image src="./../../static/imgs/icon-txt.png" class="chatinput-img" @click="changeVideoStatus"></image> | 
					
						
							|  |  |  |             </template> | 
					
						
							|  |  |  |             <template v-else> | 
					
						
							|  |  |  |               <image v-if="inputValue" src="./../../static/imgs/icon-up.png" class='chatinput-img' @click="inputSend"></image> | 
					
						
							|  |  |  |               <image v-else src="./../../static/imgs/icon-video.png" class='chatinput-img' @click="changeVideoStatus"></image> | 
					
						
							|  |  |  |             </template> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           </view> | 
					
						
							|  |  |  |         </view> | 
					
						
							|  |  |  |       </view> | 
					
						
							|  |  |  |     </view> | 
					
						
							|  |  |  |     <!--  <live-player src="https://domain/pull_stream" mode="RTC" autoplay bindstatechange="statechange" binderror="error" style="width: 300px; height: 225px;" /> --> | 
					
						
							|  |  |  |   </view> | 
					
						
							|  |  |  | </template> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | <script> | 
					
						
							|  |  |  | import Socket from './utils/socket' | 
					
						
							|  |  |  | import Audio from './utils/audio' | 
					
						
							|  |  |  | import PCMPlayer from './utils/pcm-player' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import { | 
					
						
							|  |  |  |   getHistroyMsg, | 
					
						
							|  |  |  |   setMsgData | 
					
						
							|  |  |  | } from './utils/message' | 
					
						
							|  |  |  | import ClientData from './utils/ClientData'; | 
					
						
							|  |  |  | import Pending from './components/pending'; | 
					
						
							|  |  |  | export default { | 
					
						
							|  |  |  |   props: { | 
					
						
							|  |  |  |     agentId: { | 
					
						
							|  |  |  |       type: String, | 
					
						
							|  |  |  |       default: '', | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   }, | 
					
						
							|  |  |  |   components: { | 
					
						
							|  |  |  |     Pending | 
					
						
							|  |  |  |   }, | 
					
						
							|  |  |  |   watch: { | 
					
						
							|  |  |  |     agentId: { | 
					
						
							|  |  |  |       handler(val) { | 
					
						
							|  |  |  |         if (val) { | 
					
						
							|  |  |  |           this.init(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       immediate: true, | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |   }, | 
					
						
							|  |  |  |   data() { | 
					
						
							|  |  |  |     return { | 
					
						
							|  |  |  |       title: 'Hello', | 
					
						
							|  |  |  |       socketObj: null, | 
					
						
							|  |  |  |       isEditInput: false, | 
					
						
							|  |  |  |       inputValue: '', | 
					
						
							|  |  |  |       chatMainMrgBottom: '', | 
					
						
							|  |  |  |       bottom: '', | 
					
						
							|  |  |  |       videoStatus: false, | 
					
						
							|  |  |  |       msgList: [], | 
					
						
							|  |  |  |       recorderManager: null, | 
					
						
							|  |  |  |       audioCtx: '', | 
					
						
							|  |  |  |       audioActive: '', | 
					
						
							|  |  |  |       $ClientData: '', | 
					
						
							|  |  |  |       robotObj: {}, | 
					
						
							|  |  |  |       audioArray: [], | 
					
						
							|  |  |  |       asrStatus: false, | 
					
						
							|  |  |  |       tmpMsg: {}, | 
					
						
							|  |  |  |       lastTxt: {}, | 
					
						
							|  |  |  |       txtAudioStaus: {}, | 
					
						
							|  |  |  |       requestMsg: {}, | 
					
						
							|  |  |  |       disabledStatus: false, | 
					
						
							|  |  |  |       audioOBJ: null, | 
					
						
							|  |  |  |       reAudioType: false | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   }, | 
					
						
							|  |  |  |   onLoad() { | 
					
						
							|  |  |  |   }, | 
					
						
							|  |  |  |   mounted() { | 
					
						
							|  |  |  |     console.log('【init message connect type------>】',); | 
					
						
							|  |  |  |     // this.init()
 | 
					
						
							|  |  |  |   }, | 
					
						
							|  |  |  |   destroyed() { | 
					
						
							|  |  |  |     console.log('【destroy message connect type------>】',); | 
					
						
							|  |  |  |     this.socketObj.selfCloseStatus = true | 
					
						
							|  |  |  |     this.socketObj.destroy() | 
					
						
							|  |  |  |     this.audioOBJ.selfCloseStatus = true | 
					
						
							|  |  |  |     this.audioOBJ.destroy() | 
					
						
							|  |  |  |     this.audioCtx && this.audioCtx.destroy() | 
					
						
							|  |  |  |     this.audioCtx = null | 
					
						
							|  |  |  |     this.audioActive = '' | 
					
						
							|  |  |  |     this.audioStatus = false | 
					
						
							|  |  |  |     this.asrStatus = false | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     this.player && this.player.destroy() | 
					
						
							|  |  |  |   }, | 
					
						
							|  |  |  |   onHide() { | 
					
						
							|  |  |  |     console.log('【hide message connect type------>】',); | 
					
						
							|  |  |  |   }, | 
					
						
							|  |  |  |   onShow() { | 
					
						
							|  |  |  |     this.scrollToBottom() | 
					
						
							|  |  |  |   }, | 
					
						
							|  |  |  |   methods: { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /* 4. 滚动日志 */ | 
					
						
							|  |  |  |     appendLog(str) { | 
					
						
							|  |  |  |       console.log(1111, str) | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     async init() { | 
					
						
							|  |  |  |       this.player = new PCMPlayer({ | 
					
						
							|  |  |  |         sampleRate: 16000, | 
					
						
							|  |  |  |         onPlay: (obj) => { | 
					
						
							|  |  |  |           console.log('播放文字', obj) | 
					
						
							|  |  |  |           this.disabledStatus = true | 
					
						
							|  |  |  |           if (obj.is_final) { | 
					
						
							|  |  |  |             // 最后一条语音
 | 
					
						
							|  |  |  |             console.log('=========最后一条语音=========', obj); | 
					
						
							|  |  |  |             this.disabledStatus = false // 语音播发结束
 | 
					
						
							|  |  |  |             this.audioActive = '' | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           if (this.reAudioType) { | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           const index = self.msgList.findIndex(it => it.request_id == obj.request_id && it.type == 'reply') | 
					
						
							|  |  |  |           if (index > -1) { | 
					
						
							|  |  |  |             self.msgList[index].content += obj.content | 
					
						
							|  |  |  |             self.msgList[index].is_final = obj.is_final | 
					
						
							|  |  |  |             self.$nextTick(() => { | 
					
						
							|  |  |  |               self.scrollToBottom() | 
					
						
							|  |  |  |             }) | 
					
						
							|  |  |  |           } else { | 
					
						
							|  |  |  |             if (self.msgList.length) { | 
					
						
							|  |  |  |               const item = self.msgList[self.msgList.length - 1] | 
					
						
							|  |  |  |               const tmpinfo = { | 
					
						
							|  |  |  |                 timestamp: new Date().getTime(), | 
					
						
							|  |  |  |                 name: this.robotObj.name, | 
					
						
							|  |  |  |                 headImage: this.robotObj.headImage, | 
					
						
							|  |  |  |                 type: 'reply', | 
					
						
							|  |  |  |                 contentType: 'text', | 
					
						
							|  |  |  |                 ...item, | 
					
						
							|  |  |  |                 pending: false, | 
					
						
							|  |  |  |                 request_id: obj.request_id, | 
					
						
							|  |  |  |                 content: obj.content, | 
					
						
							|  |  |  |               } | 
					
						
							|  |  |  |               if (item.pending) { | 
					
						
							|  |  |  |                 self.msgList[self.msgList.length - 1] = tmpinfo | 
					
						
							|  |  |  |                 self.$nextTick(() => { | 
					
						
							|  |  |  |                   self.scrollToBottom() | 
					
						
							|  |  |  |                 }) | 
					
						
							|  |  |  |               } else { | 
					
						
							|  |  |  |                 self.msgList.push(tmpinfo) | 
					
						
							|  |  |  |               } | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |               const tmp = { | 
					
						
							|  |  |  |                 ...obj, | 
					
						
							|  |  |  |                 type: 'reply', | 
					
						
							|  |  |  |                 contentType: 'text', | 
					
						
							|  |  |  |                 timestamp: new Date().getTime(), | 
					
						
							|  |  |  |                 name: this.robotObj.name, | 
					
						
							|  |  |  |                 headImage: this.robotObj.headImage, | 
					
						
							|  |  |  |                 content: obj.content, | 
					
						
							|  |  |  |                 pending: false | 
					
						
							|  |  |  |               } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |               self.msgList.push(tmp) | 
					
						
							|  |  |  |               self.$nextTick(() => { | 
					
						
							|  |  |  |                 self.scrollToBottom() | 
					
						
							|  |  |  |               }) | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           self.scrollToBottom() | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         onAudioEnd: () => {  | 
					
						
							|  |  |  |           console.log('语音播放结束') | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |       }) | 
					
						
							|  |  |  |       // 使用示例
 | 
					
						
							|  |  |  |       // const player = new AudioStreamPlayer()
 | 
					
						
							|  |  |  |       this.audioOBJ = new Audio({ | 
					
						
							|  |  |  |         onMessage: (e) => { | 
					
						
							|  |  |  |           if (e instanceof ArrayBuffer) { | 
					
						
							|  |  |  |             this.player.feed(e) | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           if (e.type == 2) { | 
					
						
							|  |  |  |             // 文本
 | 
					
						
							|  |  |  |             if (e.data.result && e.data) { | 
					
						
							|  |  |  |               this.player.addText(e.data) | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           if (e.type == 3) { | 
					
						
							|  |  |  |             // 合成结束
 | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           if (e instanceof ArrayBuffer) { | 
					
						
							|  |  |  |             // 语音
 | 
					
						
							|  |  |  |             player.processChunk(e) | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           if (e.type == 2) { | 
					
						
							|  |  |  |             // 文本
 | 
					
						
							|  |  |  |             player.processChunkTxt(e.data.result) | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           if (e.type == 0) { | 
					
						
							|  |  |  |             // 语音传输结束
 | 
					
						
							|  |  |  |             // player.play((txt) => {
 | 
					
						
							|  |  |  |             //   const index = self.msgList.findIndex(it => it.request_id == e.request_id && it.type == 'reply')
 | 
					
						
							|  |  |  |             //   if (index > -1) {
 | 
					
						
							|  |  |  |             //     self.msgList[index].content += txt
 | 
					
						
							|  |  |  |             //     self.$nextTick(() => {
 | 
					
						
							|  |  |  |             //       self.scrollToBottom()
 | 
					
						
							|  |  |  |             //     })
 | 
					
						
							|  |  |  |             //   } else {
 | 
					
						
							|  |  |  |             //     if (self.msgList.length) {
 | 
					
						
							|  |  |  |             //       const obj = self.msgList[self.msgList.length - 1]
 | 
					
						
							|  |  |  |             //       if (obj.pending) {
 | 
					
						
							|  |  |  |             //         const tmp = {
 | 
					
						
							|  |  |  |             //           ...e,
 | 
					
						
							|  |  |  |             //           pending: false,
 | 
					
						
							|  |  |  |             //           content: txt
 | 
					
						
							|  |  |  |             //         }
 | 
					
						
							|  |  |  |             //         self.msgList[self.msgList.length - 1] = tmp
 | 
					
						
							|  |  |  |             //         self.$nextTick(() => {
 | 
					
						
							|  |  |  |             //           self.scrollToBottom()
 | 
					
						
							|  |  |  |             //         })
 | 
					
						
							|  |  |  |             //       } else {
 | 
					
						
							|  |  |  |             //         self.msgList.push(e)
 | 
					
						
							|  |  |  |             //       }
 | 
					
						
							|  |  |  |             //     } else {
 | 
					
						
							|  |  |  |             //       const tmp = {
 | 
					
						
							|  |  |  |             //         ...e,
 | 
					
						
							|  |  |  |             //         content: txt,
 | 
					
						
							|  |  |  |             //         pending: false
 | 
					
						
							|  |  |  |             //       }
 | 
					
						
							|  |  |  |             //       self.msgList.push(tmp)
 | 
					
						
							|  |  |  |             //       self.$nextTick(() => {
 | 
					
						
							|  |  |  |             //         self.scrollToBottom()
 | 
					
						
							|  |  |  |             //       })
 | 
					
						
							|  |  |  |             //     }
 | 
					
						
							|  |  |  |             //   }
 | 
					
						
							|  |  |  |             //   self.scrollToBottom()
 | 
					
						
							|  |  |  |             // })
 | 
					
						
							|  |  |  |             // 结束之后开始播放
 | 
					
						
							|  |  |  |             // player.processChunk()
 | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }) | 
					
						
							|  |  |  |       this.audioOBJ.init() | 
					
						
							|  |  |  |       this.audioStatus = true | 
					
						
							|  |  |  |       this.asrStatus = true | 
					
						
							|  |  |  |       uni.showLoading() | 
					
						
							|  |  |  |       console.log('【init message connect type------>】',); | 
					
						
							|  |  |  |       const self = this | 
					
						
							|  |  |  |       this.socketObj = new Socket({ | 
					
						
							|  |  |  |         agentId: this.agentId, | 
					
						
							|  |  |  |         onMessage: (e) => { | 
					
						
							|  |  |  |           console.log(e.request_id + '回复内容', e.content) | 
					
						
							|  |  |  |           e.pending = false | 
					
						
							|  |  |  |           self.requestMsg[e.request_id] = (self.requestMsg[e.request_id] ? self.requestMsg[e.request_id] : '') + e.content | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           if (!e.is_from_self && e.is_final) { | 
					
						
							|  |  |  |             e.content = self.requestMsg[e.request_id] | 
					
						
							|  |  |  |             const audioParams = e | 
					
						
							|  |  |  |             // 回复结束 写入缓存
 | 
					
						
							|  |  |  |             setMsgData(audioParams) | 
					
						
							|  |  |  |             console.log('开始', audioParams); | 
					
						
							|  |  |  |             self.audio(audioParams, true) | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           self.scrollToBottom() | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }) | 
					
						
							|  |  |  |       this.getRecord(this.agentId) | 
					
						
							|  |  |  |       this.scrollToBottom(1000) | 
					
						
							|  |  |  |       await this.socketObj.init() | 
					
						
							|  |  |  |       this.$ClientData = new ClientData() | 
					
						
							|  |  |  |       this.$ClientData.init() | 
					
						
							|  |  |  |       this.$ClientData.setAttr({ socketObj: this.socketObj }) | 
					
						
							|  |  |  |       this.robotObj = this.socketObj.robotObj | 
					
						
							|  |  |  |       wx.hideLoading() | 
					
						
							|  |  |  |       this.sendMsg() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       wx.setNavigationBarTitle({ | 
					
						
							|  |  |  |         title: '数字领航员-' + this.robotObj.name | 
					
						
							|  |  |  |       }) | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     async renderTxt(e) { | 
					
						
							|  |  |  |       for (let index = 0; index < this.tmpMsg[e.request_id].length; index++) { | 
					
						
							|  |  |  |         const txt = this.tmpMsg[e.request_id][index]; | 
					
						
							|  |  |  |         const params = { | 
					
						
							|  |  |  |           content: txt, | 
					
						
							|  |  |  |           timestamp: e.timestamp | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         await this.audio(params) | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     sendMsg() { | 
					
						
							|  |  |  |       if (!this.msgList.length) { | 
					
						
							|  |  |  |         this.$ClientData.triggerSendMsg(this.socketObj.robotObj.firstWord, 'text', false); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     removeSpecificText(originalText, textToRemove) { | 
					
						
							|  |  |  |       // 使用字符串替换方法删除指定文本
 | 
					
						
							|  |  |  |       return originalText.replace(new RegExp(textToRemove, 'g'), ''); | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     // 复制
 | 
					
						
							|  |  |  |     copy(e) { | 
					
						
							|  |  |  |       wx.setClipboardData({ | 
					
						
							|  |  |  |         data: e, | 
					
						
							|  |  |  |         success: function () { | 
					
						
							|  |  |  |           wx.showToast({ title: '复制成功' }); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     async audio(n, status) { | 
					
						
							|  |  |  |       console.log('【audio------>】', n, status); | 
					
						
							|  |  |  |       this.reAudioType = !status | 
					
						
							|  |  |  |       this.audioArray = [] | 
					
						
							|  |  |  |       // this.audioArray = this.audioArray.slice(-10)
 | 
					
						
							|  |  |  |       const txt = this.stripHtmlTags(n.content) | 
					
						
							|  |  |  |       if (!txt) { return } | 
					
						
							|  |  |  |       this.audioCtx && this.audioCtx.destroy() | 
					
						
							|  |  |  |       this.audioCtx = null | 
					
						
							|  |  |  |       if (this.audioActive == n.timestamp) { | 
					
						
							|  |  |  |         this.audioActive = '' | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       this.player && this.player.destroy() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // this.audioOBJ.emit('send', txt)
 | 
					
						
							|  |  |  |       // return
 | 
					
						
							|  |  |  |       uni.request({ | 
					
						
							|  |  |  |         method: "post", | 
					
						
							|  |  |  |         data: { | 
					
						
							|  |  |  |           sessionId: this.audioOBJ.session_id, | 
					
						
							|  |  |  |           text: txt, | 
					
						
							|  |  |  |           voiceType: this.socketObj.robotObj.voiceType ?? 1002 | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         dataType: "json", | 
					
						
							|  |  |  |         url: `https://des.js-dyyj.com/xcx/api/voice/tts/flow`, | 
					
						
							|  |  |  |         success: (res) => { | 
					
						
							|  |  |  |           this.audioActive = n.timestamp | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         fail: (err) => { | 
					
						
							|  |  |  |           wx.showToast({ | 
					
						
							|  |  |  |             title: '识别失败', | 
					
						
							|  |  |  |             icon: 'error' | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         complete: () => { | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     audioText(text, cb) { | 
					
						
							|  |  |  |       return new Promise(async (resolve, reject) => { | 
					
						
							|  |  |  |         if (!this.audioStatus) { reject() } | 
					
						
							|  |  |  |         cb && cb(text) | 
					
						
							|  |  |  |         const url = await this.loadAudioUrl(text) | 
					
						
							|  |  |  |         if (!this.audioStatus) { reject() } | 
					
						
							|  |  |  |         console.log('开始播放文字', text) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         this.audioCtx = wx.createInnerAudioContext(); | 
					
						
							|  |  |  |         this.audioCtx.src = 'data:audio/wav;base64,' + url | 
					
						
							|  |  |  |         // this.audioCtx.src = 'data:audio/wav;base64,'+ url
 | 
					
						
							|  |  |  |         // console.log('App Launch', this.audioCtx.src)
 | 
					
						
							|  |  |  |         this.audioCtx.play() | 
					
						
							|  |  |  |         this.audioCtx.onStop(() => { | 
					
						
							|  |  |  |           console.log('语音播放停止', text) | 
					
						
							|  |  |  |           resolve() | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |         this.audioCtx.onEnded(() => { | 
					
						
							|  |  |  |           console.log('语音播放结束', text) | 
					
						
							|  |  |  |           resolve() | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |         this.audioCtx.onError((error) => { | 
					
						
							|  |  |  |           console.log('语音播放失败', text, error) | 
					
						
							|  |  |  |           resolve() | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     loadAudioUrl(text) { | 
					
						
							|  |  |  |       if (this.audioArray[text]) { | 
					
						
							|  |  |  |         return Promise.resolve(this.audioArray[text]) | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         console.log('-----开始请求语音合成-----', text) | 
					
						
							|  |  |  |         return new Promise((resolve, reject) => { | 
					
						
							|  |  |  |           uni.request({ | 
					
						
							|  |  |  |             method: "post", | 
					
						
							|  |  |  |             data: { | 
					
						
							|  |  |  |               text, | 
					
						
							|  |  |  |               voiceType: this.socketObj.robotObj.voiceType ?? 1002 | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             dataType: "json", | 
					
						
							|  |  |  |             url: `https://des.js-dyyj.com/xcx/api/voice/tts/new`, | 
					
						
							|  |  |  |             success: (res) => { | 
					
						
							|  |  |  |               console.log('-----请求语音合成回参-----', res) | 
					
						
							|  |  |  |               if (res.data.code == 200) { | 
					
						
							|  |  |  |                 console.log('-----请求语音合成成功-----', text) | 
					
						
							|  |  |  |                 this.audioArray[text] = res.data.data.audio | 
					
						
							|  |  |  |                 resolve(res.data.data.audio) | 
					
						
							|  |  |  |               } else { | 
					
						
							|  |  |  |                 console.log('-----请求语音合成失败-----', text) | 
					
						
							|  |  |  |                 wx.showToast({ title: '文字转化失败' }); | 
					
						
							|  |  |  |                 reject() | 
					
						
							|  |  |  |               } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             fail: (err) => { | 
					
						
							|  |  |  |               console.log("-------请求语音合成失败---->】", err); | 
					
						
							|  |  |  |               wx.showToast({ title: '文字转化失败' }); | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     stripHtmlTags(html) { | 
					
						
							|  |  |  |       return html.replace(/<\/?[^>]+(>\$)/g, '') | 
					
						
							|  |  |  |         .replace(/\u00a0/g, '') | 
					
						
							|  |  |  |         // .replace(/\s+/g, ' ')
 | 
					
						
							|  |  |  |         .replace(/\([^)]*\)/g, '') | 
					
						
							|  |  |  |         .replace(/\([^)]*\)/g, '') | 
					
						
							|  |  |  |         .replace(/\<br\/\>/g, '') | 
					
						
							|  |  |  |         .replace(/\<br\>/g, '') | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     getRecord(id) { | 
					
						
							|  |  |  |       // 获取聊天记录
 | 
					
						
							|  |  |  |       let { msgList } = getHistroyMsg(id) | 
					
						
							|  |  |  |       if (msgList && msgList.length) { | 
					
						
							|  |  |  |         this.msgList = msgList.slice(-10) | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         this.msgList = [] | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     changeVideoStatus() { | 
					
						
							|  |  |  |       this.videoStatus = !this.videoStatus | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     startVideo() { | 
					
						
							|  |  |  |       if (!this.recorderManager) { | 
					
						
							|  |  |  |         this.recorderManager = wx.getRecorderManager() | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       this.recorderManager.start({ | 
					
						
							|  |  |  |         duration: 10000, | 
					
						
							|  |  |  |         sampleRate: 44100, | 
					
						
							|  |  |  |         numberOfChannels: 1, | 
					
						
							|  |  |  |         encodeBitRate: 192000, | 
					
						
							|  |  |  |         format: 'aac', | 
					
						
							|  |  |  |         frameSize: 50 | 
					
						
							|  |  |  |       }) | 
					
						
							|  |  |  |       this.recorderManager.onStart(() => { | 
					
						
							|  |  |  |         console.log('开始录音') | 
					
						
							|  |  |  |         wx.showLoading({ | 
					
						
							|  |  |  |           title: '录音中', | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |       }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       this.recorderManager.onStop(async (res) => { | 
					
						
							|  |  |  |         wx.showLoading({ | 
					
						
							|  |  |  |           title: '识别中', | 
					
						
							|  |  |  |           mask: true | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |         uni.uploadFile({ | 
					
						
							|  |  |  |           url: 'https://des.js-dyyj.com/xcx/system/oss/upload', | 
					
						
							|  |  |  |           filePath: res.tempFilePath, | 
					
						
							|  |  |  |           name: 'file', | 
					
						
							|  |  |  |           success: async (res) => { | 
					
						
							|  |  |  |             let data = JSON.parse(res.data); | 
					
						
							|  |  |  |             console.log('上传成功', res, data); | 
					
						
							|  |  |  |             if (!this.asrStatus) { return } | 
					
						
							|  |  |  |             if (data.code == 200) { | 
					
						
							|  |  |  |               uni.request({ | 
					
						
							|  |  |  |                 method: "get", | 
					
						
							|  |  |  |                 dataType: "json", | 
					
						
							|  |  |  |                 url: `https://des.js-dyyj.com/xcx/api/voice/asr?audioFilePath=${encodeURI(data.data.url)}`, | 
					
						
							|  |  |  |                 success: (res) => { | 
					
						
							|  |  |  |                   console.log("【init msg-------res---->】", res); | 
					
						
							|  |  |  |                   if (!this.asrStatus) { return } | 
					
						
							|  |  |  |                   if (res.data.code == 200) { | 
					
						
							|  |  |  |                     wx.hideLoading() | 
					
						
							|  |  |  |                     if (res.data.msg) { | 
					
						
							|  |  |  |                       this.onSendQuestion(res.data.msg); | 
					
						
							|  |  |  |                       this.scrollToBottom() | 
					
						
							|  |  |  |                     } else { | 
					
						
							|  |  |  |                       wx.showToast({ | 
					
						
							|  |  |  |                         title: '没有听清,请再说一遍', | 
					
						
							|  |  |  |                         icon: 'none' | 
					
						
							|  |  |  |                       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                   } else { | 
					
						
							|  |  |  |                     wx.showToast({ | 
					
						
							|  |  |  |                       title: '识别失败', | 
					
						
							|  |  |  |                       icon: 'error' | 
					
						
							|  |  |  |                     }); | 
					
						
							|  |  |  |                   } | 
					
						
							|  |  |  |                 }, | 
					
						
							|  |  |  |                 fail: (err) => { | 
					
						
							|  |  |  |                   wx.showToast({ | 
					
						
							|  |  |  |                     title: '识别失败', | 
					
						
							|  |  |  |                     icon: 'error' | 
					
						
							|  |  |  |                   }); | 
					
						
							|  |  |  |                 }, | 
					
						
							|  |  |  |                 complete: () => { | 
					
						
							|  |  |  |                   this.delAudioFile(data.data.url) | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |               }); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           }, | 
					
						
							|  |  |  |           fail: (err) => { | 
					
						
							|  |  |  |             console.log('上传失败', err); | 
					
						
							|  |  |  |             wx.showToast({ | 
					
						
							|  |  |  |               title: '识别失败', | 
					
						
							|  |  |  |               icon: 'error' | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       }) | 
					
						
							|  |  |  |       this.recorderManager.onFrameRecorded((res) => { | 
					
						
							|  |  |  |         const { frameBuffer } = res | 
					
						
							|  |  |  |         console.log('frameBuffer', frameBuffer) | 
					
						
							|  |  |  |         // 实时处理音频帧数据
 | 
					
						
							|  |  |  |         this.getVideoText(frameBuffer) | 
					
						
							|  |  |  |       }) | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     delAudioFile(path) { | 
					
						
							|  |  |  |       if (path) { | 
					
						
							|  |  |  |         uni.request({ | 
					
						
							|  |  |  |           method: "get", | 
					
						
							|  |  |  |           dataType: "json", | 
					
						
							|  |  |  |           url: `https://des.js-dyyj.com/xcx/api/voice/deleteSingleFile?audioFilePath=${encodeURI(path)}`, | 
					
						
							|  |  |  |           success: (res) => { | 
					
						
							|  |  |  |             console.log("【删除文件msg-------res---->】", res); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           }, | 
					
						
							|  |  |  |           fail: (err) => { | 
					
						
							|  |  |  |             console.log("【init msg-------getDemoToken---->】", err); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           }, | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     getVideoText(frameBuffer) { | 
					
						
							|  |  |  |       const params = { | 
					
						
							|  |  |  |         Action: 'SentenceRecognition', | 
					
						
							|  |  |  |         Version: '2019-06-14', | 
					
						
							|  |  |  |         EngineModelType: '8k_zh', | 
					
						
							|  |  |  |         ChannelNum: 1, | 
					
						
							|  |  |  |         VoiceFormat: 'acc', | 
					
						
							|  |  |  |         ResTextFormat: 3, | 
					
						
							|  |  |  |         ResTextFormat: 1 | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     stopVideo() { | 
					
						
							|  |  |  |       console.log('结束录音') | 
					
						
							|  |  |  |       this.recorderManager && this.recorderManager.stop && this.recorderManager.stop() | 
					
						
							|  |  |  |       this.recorderManager = null | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     inputSend() { | 
					
						
							|  |  |  |       // const params = new FormData();
 | 
					
						
							|  |  |  |       // params.append('tts_text', '欢迎使用语音合成系统')
 | 
					
						
							|  |  |  |       // params.append('spk_id', '中文男')
 | 
					
						
							|  |  |  |       // params.append('speed', '1.0')
 | 
					
						
							|  |  |  |       // wx.uploadFile({
 | 
					
						
							|  |  |  |       //   url: 'http://110.42.225.206:8100/inference_sft_file',
 | 
					
						
							|  |  |  |       //   method: 'POST',
 | 
					
						
							|  |  |  |       //   // data: params,
 | 
					
						
							|  |  |  |       //   header: { 'content-type': 'multipart/form-data' },
 | 
					
						
							|  |  |  |       //   formData: {
 | 
					
						
							|  |  |  |       //     tts_text: '欢迎使用语音合成系统',
 | 
					
						
							|  |  |  |       //     spk_id: '中文男',
 | 
					
						
							|  |  |  |       //     speed: '1.0'
 | 
					
						
							|  |  |  |       //   },
 | 
					
						
							|  |  |  |       //   success: (res) => { 
 | 
					
						
							|  |  |  |       //     console.log(111111, res)
 | 
					
						
							|  |  |  |       //   },
 | 
					
						
							|  |  |  |       // })
 | 
					
						
							|  |  |  |       // return
 | 
					
						
							|  |  |  |       console.log('【inputSend------>】',); | 
					
						
							|  |  |  |       this.onSendQuestion(this.inputValue) | 
					
						
							|  |  |  |       this.scrollToBottom() | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     inputFocus() { | 
					
						
							|  |  |  |       console.log('inputFocus------>】',); | 
					
						
							|  |  |  |       this.scrollToBottom() | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     inputChange() { | 
					
						
							|  |  |  |       console.log('inputChange------>】',); | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     // *发送图片
 | 
					
						
							|  |  |  |     async tapChooseImage() { | 
					
						
							|  |  |  |       wx.chooseMedia({ | 
					
						
							|  |  |  |         count: 1, | 
					
						
							|  |  |  |         mediaType: ['image'], | 
					
						
							|  |  |  |         sourceType: ['album', 'camera'], | 
					
						
							|  |  |  |         success: (res) => { | 
					
						
							|  |  |  |           uni.uploadFile({ | 
					
						
							|  |  |  |             url: 'https://des.js-dyyj.com/xcx/system/oss/upload', | 
					
						
							|  |  |  |             filePath: res.tempFiles[0].tempFilePath, | 
					
						
							|  |  |  |             name: 'file', | 
					
						
							|  |  |  |             success: (res) => { | 
					
						
							|  |  |  |               console.log('上传成功', res); | 
					
						
							|  |  |  |               let data = JSON.parse(res.data); | 
					
						
							|  |  |  |               if (data.code == 200) { | 
					
						
							|  |  |  |                 const img = data.data.url | 
					
						
							|  |  |  |                 this.$ClientData.triggerSendMsg(img, 'img'); | 
					
						
							|  |  |  |                 const postData = { | 
					
						
							|  |  |  |                   content: img, | 
					
						
							|  |  |  |                   type: 'send', | 
					
						
							|  |  |  |                   contentType: 'img', | 
					
						
							|  |  |  |                   timestamp: +new Date(), | 
					
						
							|  |  |  |                   chatId: this.agentId | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 console.log('postData', postData) | 
					
						
							|  |  |  |                 this.msgList.push(postData) | 
					
						
							|  |  |  |                 // 加入恢复空信息
 | 
					
						
							|  |  |  |                 this.inputTmpReply() | 
					
						
							|  |  |  |                 this.scrollToBottom() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 this.disabledStatus = true | 
					
						
							|  |  |  |               } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             fail: (err) => { | 
					
						
							|  |  |  |               console.log('上传失败', err); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }) | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     onSendQuestion(e) { | 
					
						
							|  |  |  |       let self = this | 
					
						
							|  |  |  |       if (e === '') { | 
					
						
							|  |  |  |         return wx.showToast({ title: '不能发送空白消息', icon: 'none' }) | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       this.disabledStatus = true | 
					
						
							|  |  |  |       console.log('发送问题', e) | 
					
						
							|  |  |  |       this.$ClientData.triggerSendMsg(e, 'text'); | 
					
						
							|  |  |  |       const postData = { | 
					
						
							|  |  |  |         content: e, | 
					
						
							|  |  |  |         type: 'send', | 
					
						
							|  |  |  |         contentType: 'text', | 
					
						
							|  |  |  |         timestamp: +new Date(), | 
					
						
							|  |  |  |         chatId: this.agentId | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       self.msgList.push(postData) | 
					
						
							|  |  |  |       self.inputValue = '' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       this.inputTmpReply() | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     inputTmpReply() { | 
					
						
							|  |  |  |       // 加入恢复空信息
 | 
					
						
							|  |  |  |       const reData = { | 
					
						
							|  |  |  |         content: '', | 
					
						
							|  |  |  |         pending: true, | 
					
						
							|  |  |  |         type: 'reply', | 
					
						
							|  |  |  |         contentType: 'text', | 
					
						
							|  |  |  |         // timestamp: +new Date(),
 | 
					
						
							|  |  |  |         chatId: this.agentId, | 
					
						
							|  |  |  |         nickName: this.socketObj.robotObj.name, | 
					
						
							|  |  |  |         headImage: this.socketObj.robotObj.headImage, | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       this.msgList.push(reData) | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     // !滑倒最底部
 | 
					
						
							|  |  |  |     scrollToBottom(time = 300) { | 
					
						
							|  |  |  |       this.$nextTick(() => { | 
					
						
							|  |  |  |         setTimeout(() => { | 
					
						
							|  |  |  |           this.bottom = '' | 
					
						
							|  |  |  |           this.$nextTick(function () { | 
					
						
							|  |  |  |             this.bottom = 'bottom' | 
					
						
							|  |  |  |           }) | 
					
						
							|  |  |  |         }, time) | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | </script> | 
					
						
							|  |  |  | <style scoped lang="scss"> | 
					
						
							|  |  |  | .info { | 
					
						
							|  |  |  |   display: flex; | 
					
						
							|  |  |  |   align-items: center; | 
					
						
							|  |  |  |   flex-direction: column; | 
					
						
							|  |  |  |   padding-top: 100rpx; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   .head-img { | 
					
						
							|  |  |  |     width: 35vw; | 
					
						
							|  |  |  |     height: 35vw; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     .rot-head-img { | 
					
						
							|  |  |  |       width: 100%; | 
					
						
							|  |  |  |       height: 100%; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   .rot-title { | 
					
						
							|  |  |  |     margin-top: 40rpx; | 
					
						
							|  |  |  |     font-weight: bold; | 
					
						
							|  |  |  |     font-size: 26rpx; | 
					
						
							|  |  |  |     text-align: center; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | .chat-wrap__main { | 
					
						
							|  |  |  |   display: flex; | 
					
						
							|  |  |  |   flex-direction: column; | 
					
						
							|  |  |  |   width: 100%; | 
					
						
							|  |  |  |   height: 100%; | 
					
						
							|  |  |  |   min-height: 400px; | 
					
						
							|  |  |  |   overflow: hidden; | 
					
						
							|  |  |  |   background: #f8f8f8; | 
					
						
							|  |  |  |   border-radius: 12px; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   &-chat-content { | 
					
						
							|  |  |  |     flex: 1; | 
					
						
							|  |  |  |     display: flex; | 
					
						
							|  |  |  |     justify-content: flex-end; | 
					
						
							|  |  |  |     align-items: flex-end; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   &-content { | 
					
						
							|  |  |  |     height: calc(100% - 80px); | 
					
						
							|  |  |  |     display: flex; | 
					
						
							|  |  |  |     justify-content: flex-end; | 
					
						
							|  |  |  |     align-items: flex-end; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   &-footer { | 
					
						
							|  |  |  |     position: relative; | 
					
						
							|  |  |  |     z-index: 3; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | .chat-wrap__main-content { | 
					
						
							|  |  |  |   flex: 1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | .chat-wrap__main-footer { | 
					
						
							|  |  |  |   height: 140rpx; | 
					
						
							|  |  |  |   padding: 10rpx; | 
					
						
							|  |  |  |   padding-bottom: 10px; | 
					
						
							|  |  |  |   padding-bottom: constant(safe-area-inset-bottom); | 
					
						
							|  |  |  |   padding-bottom: env(safe-area-inset-bottom); | 
					
						
							|  |  |  |   position: relative; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   .disabled-loadding { | 
					
						
							|  |  |  |     position: absolute; | 
					
						
							|  |  |  |     top: 0; | 
					
						
							|  |  |  |     left: 0; | 
					
						
							|  |  |  |     bottom: 0; | 
					
						
							|  |  |  |     right: 0; | 
					
						
							|  |  |  |     background-color: rgba($color: #ffffff, $alpha: 0.4); | 
					
						
							|  |  |  |     z-index: 9; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | // 输入框
 | 
					
						
							|  |  |  | .chatinput-wrapper { | 
					
						
							|  |  |  |   width: 100%; | 
					
						
							|  |  |  |   // border-top: 1px solid #ddd;
 | 
					
						
							|  |  |  |   // border-bottom: 1px solid #ddd;
 | 
					
						
							|  |  |  |   .chatinput-content { | 
					
						
							|  |  |  |     height: 80rpx; | 
					
						
							|  |  |  |     padding: 20rpx; | 
					
						
							|  |  |  |     border-radius: 10rpx; | 
					
						
							|  |  |  |     display: flex; | 
					
						
							|  |  |  |     align-items: center; | 
					
						
							|  |  |  |     margin: 0 20rpx; | 
					
						
							|  |  |  |     background: #fff; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     .chatinput-input-wrap { | 
					
						
							|  |  |  |       flex: 1; | 
					
						
							|  |  |  |       height: 80rpx; | 
					
						
							|  |  |  |       box-sizing: border-box; | 
					
						
							|  |  |  |       display: flex; | 
					
						
							|  |  |  |       align-items: center; | 
					
						
							|  |  |  |       color: #9fa0a0; | 
					
						
							|  |  |  |       background: #fff; | 
					
						
							|  |  |  |       input { | 
					
						
							|  |  |  |         height: 76rpx; | 
					
						
							|  |  |  |         font-size: 16px; | 
					
						
							|  |  |  |         display: inline-block; | 
					
						
							|  |  |  |         width: 100%; | 
					
						
							|  |  |  |         margin: 10px; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     .chatinput-btn-wrap { | 
					
						
							|  |  |  |       width: 50px; | 
					
						
							|  |  |  |       display: flex; | 
					
						
							|  |  |  |       align-items: center; | 
					
						
							|  |  |  |       justify-content: center; | 
					
						
							|  |  |  |       image { | 
					
						
							|  |  |  |         width: 30px; | 
					
						
							|  |  |  |         height: 30px; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       .chatinput-send { | 
					
						
							|  |  |  |         height: 35px; | 
					
						
							|  |  |  |         width: 43px; | 
					
						
							|  |  |  |         border-radius: 4px; | 
					
						
							|  |  |  |         font-size: 12px; | 
					
						
							|  |  |  |         line-height: 35px; | 
					
						
							|  |  |  |         padding: 0; | 
					
						
							|  |  |  |         margin: 0 8px; | 
					
						
							|  |  |  |         color: #fff; | 
					
						
							|  |  |  |         background: #0097ff; | 
					
						
							|  |  |  |         &::after { | 
					
						
							|  |  |  |           border: none; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     image { | 
					
						
							|  |  |  |       width: 40rpx; | 
					
						
							|  |  |  |       height: 40rpx; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | .chat-wrapper { | 
					
						
							|  |  |  |   height: 100%; | 
					
						
							|  |  |  |   .chat-list { | 
					
						
							|  |  |  |     padding: 10px 0; | 
					
						
							|  |  |  |     .msg-container { | 
					
						
							|  |  |  |       display: flex; | 
					
						
							|  |  |  |       .ava { | 
					
						
							|  |  |  |         width: 35px; | 
					
						
							|  |  |  |         height: 35px; | 
					
						
							|  |  |  |         border-radius: 4px; | 
					
						
							|  |  |  |         margin: 0 10px; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       .msg-nickname { | 
					
						
							|  |  |  |         color: #999; | 
					
						
							|  |  |  |         line-height: 1; | 
					
						
							|  |  |  |         margin-bottom: 4px; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       .msg-content { | 
					
						
							|  |  |  |         box-sizing: border-box; | 
					
						
							|  |  |  |         word-wrap: break-word; | 
					
						
							|  |  |  |         max-width: 80vw; | 
					
						
							|  |  |  |         padding: 30rpx 30rpx 20rpx; | 
					
						
							|  |  |  |         border-radius: 10rpx; | 
					
						
							|  |  |  |         background: #fff; | 
					
						
							|  |  |  |         /* border: 1px solid #e7e7e7; */ | 
					
						
							|  |  |  |         max-width: "calc(100vw - 110px)"; | 
					
						
							|  |  |  |         color: #000; | 
					
						
							|  |  |  |         font-size: 28rpx; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         .msg-btns { | 
					
						
							|  |  |  |           border-top: 1px solid #e7e7e7; | 
					
						
							|  |  |  |           margin-top: 40rpx; | 
					
						
							|  |  |  |           padding-top: 20rpx; | 
					
						
							|  |  |  |           border-top: 1px dashed #000; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           .btn-img { | 
					
						
							|  |  |  |             margin-right: 10px; | 
					
						
							|  |  |  |             width: 40rpx; | 
					
						
							|  |  |  |             height: 40rpx; | 
					
						
							|  |  |  |             cursor: pointer; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       &.self { | 
					
						
							|  |  |  |         justify-content: flex-end; | 
					
						
							|  |  |  |         padding-right: 40rpx; | 
					
						
							|  |  |  |         .msg { | 
					
						
							|  |  |  |           display: flex; | 
					
						
							|  |  |  |           flex-direction: column; | 
					
						
							|  |  |  |           justify-content: flex-end; | 
					
						
							|  |  |  |           align-items: flex-end; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         .msg-nickname { | 
					
						
							|  |  |  |           text-align: right; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         .msg-content { | 
					
						
							|  |  |  |           width: auto; | 
					
						
							|  |  |  |           color: #000; | 
					
						
							|  |  |  |           background-color: #acf8f8; | 
					
						
							|  |  |  |           /* border-color:#acf8f8; */ | 
					
						
							|  |  |  |           padding: 30rpx; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       &.other { | 
					
						
							|  |  |  |         padding-left: 40rpx; | 
					
						
							|  |  |  |         .msg { | 
					
						
							|  |  |  |           display: flex; | 
					
						
							|  |  |  |           flex-direction: column; | 
					
						
							|  |  |  |           justify-content: flex-end; | 
					
						
							|  |  |  |           align-items: flex-start; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       .msg-content.msg-content-img { | 
					
						
							|  |  |  |         background: none; | 
					
						
							|  |  |  |         border: none; | 
					
						
							|  |  |  |         padding: 0; | 
					
						
							|  |  |  |         image { | 
					
						
							|  |  |  |           max-width: 120px; | 
					
						
							|  |  |  |           border-radius: 2px; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | </style> |