|
|
|
@ -6,17 +6,19 @@ |
|
|
|
|
<view class="list_item" :class="item.userType" v-for="(item,index) in list" :key="index"> |
|
|
|
|
<view v-if="item.userType=='self'"> |
|
|
|
|
<view class="text-name row flex-align-center"> |
|
|
|
|
<image class="img" mode="widthFix" |
|
|
|
|
<image class="img" |
|
|
|
|
src="https://eluyou.ailuquan.cn/upload/image/2024/mapIcon/daolan/ai-self.png"></image> |
|
|
|
|
<text>E鹿小助手</text> |
|
|
|
|
</view> |
|
|
|
|
<zero-markdown-view :markdown="item.content"></zero-markdown-view> |
|
|
|
|
<zero-markdown-view :markdown="item.content" v-if="item.content"></zero-markdown-view> |
|
|
|
|
<view class="loading" v-else><text class="loading-dot"></text><text |
|
|
|
|
class="loading-dot"></text><text class="loading-dot"></text></view> |
|
|
|
|
</view> |
|
|
|
|
<view class="row flex-align-center" style="justify-content: flex-end;" |
|
|
|
|
v-if="item.userType=='friend'"> |
|
|
|
|
<view class="text-box">{{item.content}}</view> |
|
|
|
|
<view class="text-name row flex-align-center"> |
|
|
|
|
<image class="img" mode="widthFix" |
|
|
|
|
<image class="img" |
|
|
|
|
src="https://eluyou.ailuquan.cn/upload/image/2024/mapIcon/daolan/ai-friend.png"></image> |
|
|
|
|
</view> |
|
|
|
|
</view> |
|
|
|
@ -35,7 +37,7 @@ |
|
|
|
|
<image src="https://eluyou.ailuquan.cn/upload/image/2024/mapIcon/daolan/icon-text.png" mode="widthFix" |
|
|
|
|
class="left-icon" @click="messageType='text'"> |
|
|
|
|
</image> |
|
|
|
|
<text class="voice-crl" @touchstart="touchstart" |
|
|
|
|
<text class="voice-crl" @click="clickSprink()" @touchstart="touchstart" |
|
|
|
|
@touchend="touchend">{{ recordStart ? '松开 发送' : '按住 说话' }}</text> |
|
|
|
|
</block> |
|
|
|
|
</view> |
|
|
|
@ -46,7 +48,6 @@ |
|
|
|
|
</view> |
|
|
|
|
</view> |
|
|
|
|
|
|
|
|
|
<f-login></f-login> |
|
|
|
|
</view> |
|
|
|
|
</template> |
|
|
|
|
|
|
|
|
@ -58,12 +59,16 @@ |
|
|
|
|
mapMutations |
|
|
|
|
} from "vuex"; |
|
|
|
|
export default { |
|
|
|
|
components:{ZeroMarkdownView}, |
|
|
|
|
components: { |
|
|
|
|
ZeroMarkdownView |
|
|
|
|
}, |
|
|
|
|
computed: { |
|
|
|
|
...mapState(["PrimaryColor", "userInfo"]) |
|
|
|
|
}, |
|
|
|
|
data() { |
|
|
|
|
return { |
|
|
|
|
//公共路径 |
|
|
|
|
rootPath: this.$config.ROOTPATH, |
|
|
|
|
content: '', |
|
|
|
|
list: [{ |
|
|
|
|
content: "Hi,你好,E鹿小助手很高兴为您提供咨询服务。", |
|
|
|
@ -90,7 +95,10 @@ |
|
|
|
|
clientTime: 10000, // 客户端计时间隔 |
|
|
|
|
serverTimer: null, // 服务端计时 |
|
|
|
|
serverTime: 30000, // 服务端计时间隔 |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
//是否长按事件 |
|
|
|
|
islongPress: false, |
|
|
|
|
timer: null, //长按计时器 |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
onUnload() { |
|
|
|
@ -98,10 +106,11 @@ |
|
|
|
|
}, |
|
|
|
|
onShow() { |
|
|
|
|
this.getUserInfo() |
|
|
|
|
this.connectWs() |
|
|
|
|
}, |
|
|
|
|
methods: { |
|
|
|
|
// 用户信息 |
|
|
|
|
getUserInfo() { |
|
|
|
|
getUserInfo() { |
|
|
|
|
this.querUserInfo = { |
|
|
|
|
"message": "", |
|
|
|
|
"dialogId": "", |
|
|
|
@ -138,7 +147,6 @@ |
|
|
|
|
$this.heartBeat(); |
|
|
|
|
// 处理消息 |
|
|
|
|
let data = JSON.parse(res.data); |
|
|
|
|
console.log("xinxi", data.content) |
|
|
|
|
$this.handlerMessage(JSON.parse(data.content)); |
|
|
|
|
}); |
|
|
|
|
// ws关闭 |
|
|
|
@ -151,7 +159,7 @@ |
|
|
|
|
this.wsInfo.ws.onError((err) => { |
|
|
|
|
$this.wsInfo.alive = false; |
|
|
|
|
$this.reConnect(); |
|
|
|
|
console.log("ws连接错误:", res); |
|
|
|
|
console.log("ws连接错误:", err); |
|
|
|
|
}); |
|
|
|
|
}, |
|
|
|
|
// 心跳检测 |
|
|
|
@ -176,7 +184,7 @@ |
|
|
|
|
}, $this.wsInfo.clientTime); |
|
|
|
|
}, |
|
|
|
|
//发送消息 |
|
|
|
|
handlerMessage(data) { |
|
|
|
|
handlerMessage(data) { |
|
|
|
|
if (data.context !== null) { |
|
|
|
|
this.list[this.list.length - 1].content += data.context |
|
|
|
|
} |
|
|
|
@ -209,33 +217,29 @@ |
|
|
|
|
// 发送消息 |
|
|
|
|
sendMsg() { |
|
|
|
|
const $this = this |
|
|
|
|
this.judgeLogin((resa) => { |
|
|
|
|
this.getUserInfo() |
|
|
|
|
this.connectWs() |
|
|
|
|
this.list.push({ |
|
|
|
|
content: this.content, |
|
|
|
|
userType: 'friend', |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
if (this.querUserInfo.identity == "") { |
|
|
|
|
this.querUserInfo.identity = this.generateRandomString(8); |
|
|
|
|
} |
|
|
|
|
this.getUserInfo() |
|
|
|
|
this.list.push({ |
|
|
|
|
content: this.content, |
|
|
|
|
userType: 'friend', |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
this.querUserInfo.message = this.content |
|
|
|
|
this.content = '' |
|
|
|
|
this.$Request.post(this.$config.aiSendMsg, $this.querUserInfo, "json", null, false, true) |
|
|
|
|
.then(res => { |
|
|
|
|
this.inputDisabled = true |
|
|
|
|
setTimeout(() => { |
|
|
|
|
$this.list.push({ |
|
|
|
|
content: "", |
|
|
|
|
userType: 'self', |
|
|
|
|
}) |
|
|
|
|
$this.scrollToBottom() |
|
|
|
|
}, 500) |
|
|
|
|
}); |
|
|
|
|
if (this.querUserInfo.identity == "") { |
|
|
|
|
this.querUserInfo.identity = this.generateRandomString(8); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
}); |
|
|
|
|
this.querUserInfo.message = this.content |
|
|
|
|
this.content = '' |
|
|
|
|
this.$Request.post(this.$config.aiSendMsg, $this.querUserInfo, "json", null, false, true) |
|
|
|
|
.then(res => { |
|
|
|
|
this.inputDisabled = true |
|
|
|
|
setTimeout(() => { |
|
|
|
|
$this.list.push({ |
|
|
|
|
content: "", |
|
|
|
|
userType: 'self', |
|
|
|
|
}) |
|
|
|
|
$this.scrollToBottom() |
|
|
|
|
}, 500) |
|
|
|
|
}); |
|
|
|
|
}, |
|
|
|
|
//屏幕滚动 |
|
|
|
|
scrollToBottom() { |
|
|
|
@ -270,63 +274,100 @@ |
|
|
|
|
} |
|
|
|
|
}) |
|
|
|
|
}, |
|
|
|
|
//开启录音 |
|
|
|
|
touchstart() { |
|
|
|
|
//开始录音 |
|
|
|
|
const _permission = 'scope.record' |
|
|
|
|
uni.getSetting({ |
|
|
|
|
success: (res) => { |
|
|
|
|
// 判断是否有相关权限属性 |
|
|
|
|
if (res.authSetting.hasOwnProperty(_permission)) { |
|
|
|
|
// 属性存在,且为false,用户拒绝过权限 |
|
|
|
|
if (!res.authSetting[_permission]) { |
|
|
|
|
this.authTips() |
|
|
|
|
} else { |
|
|
|
|
// 已授权 |
|
|
|
|
this._recordAuth = true |
|
|
|
|
// 开始录音 |
|
|
|
|
recorderManager.start({ |
|
|
|
|
format: 'pcm', |
|
|
|
|
}) |
|
|
|
|
recorderManager.onStart(() => { |
|
|
|
|
this.recordStart = true |
|
|
|
|
}) |
|
|
|
|
//长按事件 |
|
|
|
|
longpress() { |
|
|
|
|
const $this = this |
|
|
|
|
this.islongPress = true; |
|
|
|
|
if (this.islongPress == true) { |
|
|
|
|
//开始录音 |
|
|
|
|
const _permission = 'scope.record' |
|
|
|
|
uni.getSetting({ |
|
|
|
|
success: (res) => { |
|
|
|
|
if (res.authSetting.hasOwnProperty('scope.record')) { |
|
|
|
|
|
|
|
|
|
if (!res.authSetting[_permission]) { |
|
|
|
|
$this.authTips() |
|
|
|
|
} else { |
|
|
|
|
// 已授权 |
|
|
|
|
$this._recordAuth = true |
|
|
|
|
// 开始录音 |
|
|
|
|
recorderManager.start({ |
|
|
|
|
format: 'pcm', |
|
|
|
|
}) |
|
|
|
|
recorderManager.onStart(() => { |
|
|
|
|
$this.recordStart = true |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
// 错误回调 |
|
|
|
|
recorderManager.onError((res) => { |
|
|
|
|
console.log('recorder error', res) |
|
|
|
|
uni.showToast({ |
|
|
|
|
icon: 'none', |
|
|
|
|
title: '系统出错,请重试' |
|
|
|
|
// 错误回调 |
|
|
|
|
recorderManager.onError((res) => { |
|
|
|
|
console.log('recorder error', res) |
|
|
|
|
uni.showToast({ |
|
|
|
|
icon: 'none', |
|
|
|
|
title: '系统出错,请重试' |
|
|
|
|
}) |
|
|
|
|
$this.recordStart = false |
|
|
|
|
}) |
|
|
|
|
this.recordStart = false |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
// 属性不存在,需要授权 |
|
|
|
|
console.log('属性不存在,需要授权') |
|
|
|
|
uni.authorize({ |
|
|
|
|
scope: 'scope.record', |
|
|
|
|
success: () => { |
|
|
|
|
// 授权成功 |
|
|
|
|
$this._recordAuth = true |
|
|
|
|
console.log('授权成功') |
|
|
|
|
}, |
|
|
|
|
fail: (res) => { |
|
|
|
|
console.log('// 未授权隐私协议', res.errno) |
|
|
|
|
/** |
|
|
|
|
* 104 未授权隐私协议 |
|
|
|
|
* 用户可能拒绝官方隐私授权弹窗,为了避免过度弹窗打扰用户,开发者再次调用隐私相关接口时, |
|
|
|
|
* 若距上次用户拒绝不足10秒,将不再触发弹窗,直接给到开发者用户拒绝隐私授权弹窗的报错 |
|
|
|
|
*/ |
|
|
|
|
if (res.errno == 104) { |
|
|
|
|
uni.showModal({ |
|
|
|
|
title: '温馨提示', |
|
|
|
|
content: '您拒绝了隐私协议,请稍后再试', |
|
|
|
|
confirmText: '知道了', |
|
|
|
|
showCancel: false, |
|
|
|
|
success: () => {} |
|
|
|
|
}) |
|
|
|
|
} else { |
|
|
|
|
// 用户拒绝授权 |
|
|
|
|
console.log('// 用户拒绝授权') |
|
|
|
|
$this.authTips() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
// 属性不存在,需要授权 |
|
|
|
|
uni.authorize({ |
|
|
|
|
scope: _permission, |
|
|
|
|
success: () => { |
|
|
|
|
// 授权成功 |
|
|
|
|
this._recordAuth = true |
|
|
|
|
}, |
|
|
|
|
fail: (res) => { |
|
|
|
|
/** |
|
|
|
|
* 104 未授权隐私协议 |
|
|
|
|
* 用户可能拒绝官方隐私授权弹窗,为了避免过度弹窗打扰用户,开发者再次调用隐私相关接口时, |
|
|
|
|
* 若距上次用户拒绝不足10秒,将不再触发弹窗,直接给到开发者用户拒绝隐私授权弹窗的报错 |
|
|
|
|
*/ |
|
|
|
|
if (res.errno == 104) { |
|
|
|
|
uni.showModal({ |
|
|
|
|
title: '温馨提示', |
|
|
|
|
content: '您拒绝了隐私协议,请稍后再试', |
|
|
|
|
confirmText: '知道了', |
|
|
|
|
} |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
//开启麦克风权限 |
|
|
|
|
authTips() { |
|
|
|
|
const $this = this |
|
|
|
|
uni.showModal({ |
|
|
|
|
title: '提示', |
|
|
|
|
content: '您拒绝了麦克风权限,将导致功能不能正常使用,去设置权限?', |
|
|
|
|
confirmText: '去设置', |
|
|
|
|
cancelText: '取消', |
|
|
|
|
success: (res) => { |
|
|
|
|
if (res.confirm) { |
|
|
|
|
uni.openSetting({ |
|
|
|
|
success: (res) => { |
|
|
|
|
if (res.authSetting['scope.record']) { |
|
|
|
|
console.log("已授权麦克风"); |
|
|
|
|
$this._recordAuth = true |
|
|
|
|
} else { |
|
|
|
|
// 未授权 |
|
|
|
|
wx.showModal({ |
|
|
|
|
title: '提示', |
|
|
|
|
content: '您未授权麦克风,功能将无法使用', |
|
|
|
|
showCancel: false, |
|
|
|
|
success: () => {} |
|
|
|
|
confirmText: '知道了' |
|
|
|
|
}) |
|
|
|
|
} else { |
|
|
|
|
// 用户拒绝授权 |
|
|
|
|
this.authTips() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}) |
|
|
|
@ -334,9 +375,22 @@ |
|
|
|
|
} |
|
|
|
|
}) |
|
|
|
|
}, |
|
|
|
|
//开启录音 |
|
|
|
|
touchstart() { |
|
|
|
|
const $this = this |
|
|
|
|
this.timer = setTimeout(() => { |
|
|
|
|
$this.longpress(); |
|
|
|
|
}, 200) |
|
|
|
|
}, |
|
|
|
|
//停止录音 |
|
|
|
|
touchend() { |
|
|
|
|
const $this = this |
|
|
|
|
//延时执行为了防止 click() 还未判断 islongPress 的值就被置为 fasle |
|
|
|
|
clearTimeout(this.timer); |
|
|
|
|
setTimeout(() => { |
|
|
|
|
this.islongPress = false |
|
|
|
|
}, 200) |
|
|
|
|
|
|
|
|
|
if (!this._recordAuth || !this.recordStart) return |
|
|
|
|
//停止录音 |
|
|
|
|
recorderManager.stop(); |
|
|
|
@ -345,9 +399,10 @@ |
|
|
|
|
duration, |
|
|
|
|
tempFilePath |
|
|
|
|
} = res |
|
|
|
|
this.recordStart = false |
|
|
|
|
$this.recordStart = false |
|
|
|
|
|
|
|
|
|
wx.uploadFile({ |
|
|
|
|
url: 'http://192.168.130.157:48080/app-api/wechatshop/toolIdentify/identifySpeech', |
|
|
|
|
url: $this.rootPath + '/app-api/wechatshop/toolIdentify/identifySpeech', |
|
|
|
|
filePath: tempFilePath, |
|
|
|
|
name: 'file', |
|
|
|
|
formData: { |
|
|
|
@ -355,13 +410,19 @@ |
|
|
|
|
}, |
|
|
|
|
success: function(res) { |
|
|
|
|
let data = JSON.parse(res.data) |
|
|
|
|
$this.list.push({ |
|
|
|
|
content: data.data, |
|
|
|
|
audioSrc: tempFilePath, |
|
|
|
|
userType: 'self', |
|
|
|
|
messageType: 'voice' |
|
|
|
|
}) |
|
|
|
|
$this.scrollToBottom() |
|
|
|
|
console.log("dsfaD", data) |
|
|
|
|
if (data.data) { |
|
|
|
|
$this.content = data.data |
|
|
|
|
$this.sendMsg() |
|
|
|
|
} else { |
|
|
|
|
wx.showModal({ |
|
|
|
|
title: '提示', |
|
|
|
|
content: "没听清,请您再说一遍", |
|
|
|
|
showCancel: false, |
|
|
|
|
success: function(res) {} |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
}, |
|
|
|
|
fail: function(res) { |
|
|
|
|
UTIL.log(res); |
|
|
|
@ -376,6 +437,20 @@ |
|
|
|
|
}); |
|
|
|
|
}) |
|
|
|
|
}, |
|
|
|
|
//点击事件 |
|
|
|
|
clickSprink() { |
|
|
|
|
// 非长按 |
|
|
|
|
if (this.islongPress == false) { |
|
|
|
|
wx.showModal({ |
|
|
|
|
title: '提示', |
|
|
|
|
content: "说话时间太短", |
|
|
|
|
showCancel: false, |
|
|
|
|
success: function(res) {} |
|
|
|
|
}); |
|
|
|
|
} else if (this.islongPress == true) { |
|
|
|
|
console.log("长按事件"); |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
//获取当前对话 key |
|
|
|
|
generateRandomString(length) { |
|
|
|
|
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; |
|
|
|
@ -401,6 +476,7 @@ |
|
|
|
|
/* #endif */ |
|
|
|
|
box-sizing: border-box; |
|
|
|
|
top: 0; |
|
|
|
|
padding-bottom: 60rpx; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
.v-pages.Ai { |
|
|
|
@ -408,7 +484,7 @@ |
|
|
|
|
// background-repeat: no-repeat; |
|
|
|
|
// background-position: top; |
|
|
|
|
// background-size: cover; |
|
|
|
|
min-height: 50vh; |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
.v-ai-list { |
|
|
|
@ -423,6 +499,7 @@ |
|
|
|
|
|
|
|
|
|
.img { |
|
|
|
|
width: 80rpx; |
|
|
|
|
height: 80rpx; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -556,4 +633,25 @@ |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
.loading { |
|
|
|
|
height: 40rpx; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
.loading-dot { |
|
|
|
|
display: inline-block; |
|
|
|
|
width: 4px; |
|
|
|
|
height: 4px; |
|
|
|
|
border-radius: 4px; |
|
|
|
|
background-color: #000; |
|
|
|
|
animation: loading 1s infinite; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
.loading-dot:nth-child(2) { |
|
|
|
|
animation-delay: 0.2s; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
.loading-dot:nth-child(3) { |
|
|
|
|
animation-delay: 0.4s; |
|
|
|
|
} |
|
|
|
|
</style> |