You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1046 lines
30 KiB
1046 lines
30 KiB
<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="chatinput-wrapper">
|
|
<view class="chatinput-content">
|
|
<view class="input-content">
|
|
<view class="disabled-loadding" v-if="disabledStatus"></view>
|
|
<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>
|
|
<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="disabledStatus">
|
|
<image src="./../../static/imgs/icon-stop.png" class="chatinput-img" @click="stopMessage"></image>
|
|
</template>
|
|
<template v-else>
|
|
<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>
|
|
|
|
</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,
|
|
|
|
stopStatus: 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()
|
|
this.stopStatus = false
|
|
this.currentRecordId = ''
|
|
},
|
|
onHide() {
|
|
console.log('【hide message connect type------>】',);
|
|
},
|
|
onShow() {
|
|
this.scrollToBottom()
|
|
},
|
|
methods: {
|
|
stopMessage(){
|
|
// 暂停播放
|
|
console.log('暂停语音',);
|
|
this.stopStatus = true
|
|
// 取消对话框回复
|
|
this.stopMSN()
|
|
// 暂停语音播放
|
|
if (this.player) {
|
|
this.player.destroy()
|
|
}
|
|
|
|
// 清楚当前如果是pending的信息
|
|
if (this.msgList && this.msgList.length) {
|
|
const last = this.msgList[this.msgList.length - 1]
|
|
console.log('last', last);
|
|
if (last.pending) {
|
|
this.msgList.pop()
|
|
}
|
|
}
|
|
|
|
this.audioActive = ''
|
|
this.disabledStatus = false
|
|
|
|
},
|
|
stopMSN () {
|
|
if (this.socketObj && this.socketObj.socket) {
|
|
const options = { data: "42" + JSON.stringify(["stop_generation", {
|
|
payload: {
|
|
record_id: this.currentRecordId
|
|
}
|
|
}]) }
|
|
this.socketObj.send(options)
|
|
}
|
|
},
|
|
/* 4. 滚动日志 */
|
|
appendLog(str) {
|
|
console.log(1111, str)
|
|
},
|
|
async init() {
|
|
this.player = new PCMPlayer({
|
|
sampleRate: 16000,
|
|
onPlay: (obj) => {
|
|
console.log('播放文字', obj)
|
|
// 暂停
|
|
if (this.stopStatus) {
|
|
return
|
|
}
|
|
|
|
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 (this.stopStatus) {
|
|
return
|
|
}
|
|
|
|
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(111111, e.record_id, e.request_id + '回复内容', e.content)
|
|
// 记录当前记录id
|
|
self.currentRecordId = e.record_id
|
|
// 暂停
|
|
if (this.stopStatus) {
|
|
this.stopMSN()
|
|
return
|
|
}
|
|
|
|
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.stopStatus = false
|
|
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.dayunyuanjian.cn/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.dayunyuanjian.cn/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.dayunyuanjian.cn/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.dayunyuanjian.cn/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.dayunyuanjian.cn/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.dayunyuanjian.cn/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) {
|
|
this.stopStatus = false
|
|
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;
|
|
|
|
.input-content {
|
|
flex: 1;
|
|
display: flex;
|
|
position: relative;
|
|
}
|
|
|
|
.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>
|