JLBA202505130001_关于吉林银行新建AI智能培训系统的需求_feat: 陪练语音按钮
This commit is contained in:
+1
-1
@@ -5,7 +5,7 @@ ENV = 'development'
|
||||
#VITE_APP_BASE_API_Url = 'https://aits.jlbank.com.cn:7001'
|
||||
|
||||
|
||||
VITE_APP_BASE_API_Url = 'https://aitstest.jlbank.com.cn:7003'
|
||||
VITE_APP_BASE_API_Url = 'https://aitstest.jlbank.com.cn:7001'
|
||||
|
||||
|
||||
# DEV
|
||||
|
||||
@@ -46,4 +46,15 @@
|
||||
height: 50rpx;
|
||||
}
|
||||
}
|
||||
|
||||
&.blue{
|
||||
background-color: #06f;
|
||||
&.inputMode {
|
||||
border: 2rpx solid #dedede00;
|
||||
}
|
||||
|
||||
&.action {
|
||||
background-color: #0066ff99;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<view>
|
||||
<view class="talk-btns-components" :class="{ action: isRecording, inputMode: !isInputVisible }">
|
||||
<view :class="['talk-btns-components', props.theme, { action: isRecording, inputMode: !isInputVisible }]">
|
||||
<view class="touch-btn" @click="disabled_click">
|
||||
<button
|
||||
v-if="isInputVisible"
|
||||
@@ -11,7 +11,9 @@
|
||||
@touchcancel="stopRecord()"
|
||||
:plain="true"
|
||||
>
|
||||
<slot>
|
||||
<text class="voice-text">{{ isRecording ? '松开发送' : '按住说话' }}</text>
|
||||
</slot>
|
||||
</button>
|
||||
<input
|
||||
v-if="!isVoiceOnlyMode && !isInputVisible"
|
||||
@@ -180,6 +182,10 @@
|
||||
/* '01': '不限制回答方式', '02': '仅文字回答', '03': '仅语音回答'*/
|
||||
return ['01', '02', '03'].includes(value);
|
||||
}
|
||||
},
|
||||
theme: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
});
|
||||
// 兼容以前的按钮参数
|
||||
|
||||
@@ -51,6 +51,15 @@
|
||||
import {
|
||||
getPhoneEnvBool
|
||||
} from '@/common/common.js';
|
||||
import {
|
||||
downloadFile,
|
||||
downloadVoiceFile
|
||||
} from '@/api/common.js'
|
||||
import {
|
||||
useStorageStore
|
||||
} from '@/store/storage.js';
|
||||
|
||||
const storageStore = useStorageStore();
|
||||
const props = defineProps({
|
||||
dataInfo: {
|
||||
default: () => ({}),
|
||||
@@ -120,14 +129,37 @@
|
||||
}, 300)
|
||||
return
|
||||
} else {
|
||||
talkUserVoiceObj.value.src = ''
|
||||
if (unref(dataInfo).talkingVoice) {
|
||||
setTimeout(() => {
|
||||
talkUserVoiceObj.value.src = itemVoice.value
|
||||
talkUserVoiceObj.value.play()
|
||||
isPlaying.value = true
|
||||
}, 100)
|
||||
voiceLoading.value = true;
|
||||
let _id = unref(dataInfo)?.fileId
|
||||
storageStore.getStorageById('audio', _id, (url) => {
|
||||
if (url) {
|
||||
voiceLoading.value = false;
|
||||
itemVoice.value = url;
|
||||
talkVoiceObj.value.src = url;
|
||||
if (voiceIsPlaying.value) return
|
||||
if (unref(dataInfo)?.id !== props.activeVoiceId) return
|
||||
talkVoiceObj.value.play();
|
||||
} else {
|
||||
downloadFile(_id)
|
||||
.then((res) => {
|
||||
if (res?.data) {
|
||||
common.msg(res.data.message);
|
||||
return;
|
||||
}
|
||||
voiceLoading.value = false;
|
||||
itemVoice.value = res.tempFilePath;
|
||||
talkVoiceObj.value.src = res.tempFilePath;
|
||||
if (voiceIsPlaying.value) return
|
||||
if (unref(dataInfo)?.id !== props.activeVoiceId) return
|
||||
talkVoiceObj.value.play();
|
||||
storageStore.setStorage('audio', _id, itemVoice.value);
|
||||
})
|
||||
.catch((err) => {
|
||||
voiceLoading.value = false;
|
||||
emit('changePlay', '');
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
const IsAndEnv = getPhoneEnvBool('AND');
|
||||
|
||||
+185
-39
@@ -11,7 +11,9 @@
|
||||
</view>
|
||||
<view class="top-desc">内容由AI生成</view>
|
||||
</view>
|
||||
<scroll-view :class="[' body-content index-dialog-body', detailInfo.traInterTyp === '01' ? 'talking-type1' : '']" :scroll-y="true" refresher-default-style="none" :scroll-top="scrollTop" :show-scrollbar="false" :scroll-with-animation="false">
|
||||
<scroll-view :class="[' body-content index-dialog-body', detailInfo.traInterTyp === '01' ? 'talking-type1' : '']"
|
||||
:scroll-y="true" refresher-default-style="none" :scroll-top="scrollTop" :show-scrollbar="false"
|
||||
:scroll-with-animation="false">
|
||||
<view :class="['body-content-role', showRoleInfo ? '' : 'hide-info']">
|
||||
<view :class="['role-title']">
|
||||
<ImagePreview class="role-title-avatar" :src="detailInfo.ossAddr" :isCache="true"></ImagePreview>
|
||||
@@ -21,8 +23,10 @@
|
||||
<text class="card-title-name">{{ detailInfo.chaName }}</text>
|
||||
<text class="card-title-age">{{ detailInfo.chaAge }}岁</text>
|
||||
<text class="card-title-img">
|
||||
<image class="card-title-icon" v-if="detailInfo.gender === 'm' || detailInfo.gender === '01'" src="@/static/images/me/man.png"></image>
|
||||
<image class="card-title-icon" v-if="detailInfo.gender === 'f' || detailInfo.gender === '02'" src="@/static/images/me/wuman.png"></image>
|
||||
<image class="card-title-icon" v-if="detailInfo.gender === 'm' || detailInfo.gender === '01'"
|
||||
src="@/static/images/me/man.png"></image>
|
||||
<image class="card-title-icon" v-if="detailInfo.gender === 'f' || detailInfo.gender === '02'"
|
||||
src="@/static/images/me/wuman.png"></image>
|
||||
</text>
|
||||
</view>
|
||||
<view class="role-desc"> 预计时长: {{ detailInfo.traStartLimit }} ~ {{ detailInfo.traEndLimit }} 分钟 </view>
|
||||
@@ -58,29 +62,40 @@
|
||||
<view class="tip-text">-请点击下方按钮开始{{ detailInfo.traInterTypDesc }}</view>
|
||||
</view>
|
||||
<view class="talking-box" v-else>
|
||||
<TalkingItem
|
||||
v-for="(item, index) in talkingList"
|
||||
:talkingType="item.type"
|
||||
:talkingInfo="item"
|
||||
:roleInfo="detailInfo"
|
||||
:traId="traId"
|
||||
:execId="execId"
|
||||
:isExam="talkingType === '02'"
|
||||
:isLast="index + 1 === talkingList.length"
|
||||
:isAnswering="!answerIsOver"
|
||||
:activeVoiceId="activeVoiceTalkingItemId"
|
||||
@handleEvents="talkItemEvents"></TalkingItem>
|
||||
<TalkingItem v-for="(item, index) in talkingList" :talkingType="item.type" :talkingInfo="item"
|
||||
:roleInfo="detailInfo" :traId="traId" :execId="execId" :isExam="talkingType === '02'"
|
||||
:isLast="index + 1 === talkingList.length" :isAnswering="!answerIsOver"
|
||||
:activeVoiceId="activeVoiceTalkingItemId" @handleEvents="talkItemEvents"></TalkingItem>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="over-btn-box" v-if="detailInfo.traInterTyp === '01'">
|
||||
<view class="over-btn" @click="overTalking">结束陪练</view>
|
||||
</view>
|
||||
<TouchBtn class="body-bottom" @uploadVoice="uploadRecordNow" @startRecord="changePlayItemId('')" formatType="wav" :isLoading="!answerIsOver" :isDisabled="false" v-if="detailInfo.traInterTyp === '01'" />
|
||||
<!-- <TouchBtn
|
||||
class="body-bottom"
|
||||
@uploadVoice="uploadRecordNow"
|
||||
@startRecord="changePlayItemId('')"
|
||||
formatType="wav"
|
||||
:isLoading="!answerIsOver"
|
||||
:isDisabled="false"
|
||||
v-if="detailInfo.traInterTyp === '01'" /> -->
|
||||
|
||||
<touch-btn class="body-bottom" @submitVoice="submitVoice" @sendLoading="sendLoading" @submitAnswer="submitAnswer"
|
||||
@startRecord="changePlayItemId('')" :isLoading="!answerIsOver" :isDisabled="false" theme="blue"
|
||||
answerMethod="03" v-if="detailInfo.traInterTyp === '01'" >
|
||||
|
||||
<view class="center-text">
|
||||
<image class="type-icon" src="@/static/images/sparring/talk-icon.png"></image>
|
||||
{{'语音对话'}}
|
||||
</view>
|
||||
</touch-btn>
|
||||
<view class="body-bottom" v-else>
|
||||
<view class="body-bottom-btn" @click="beforeToTalking">
|
||||
<view class="center-text">
|
||||
<image class="type-icon" src="@/static/images/sparring/video-icon.png" v-if="detailInfo.traInterTyp === '03'"></image>
|
||||
<image class="type-icon" src="@/static/images/sparring/phone-icon.png" v-else-if="detailInfo.traInterTyp === '02'"></image>
|
||||
<image class="type-icon" src="@/static/images/sparring/video-icon.png"
|
||||
v-if="detailInfo.traInterTyp === '03'"></image>
|
||||
<image class="type-icon" src="@/static/images/sparring/phone-icon.png"
|
||||
v-else-if="detailInfo.traInterTyp === '02'"></image>
|
||||
<image v-else class="type-icon" src="@/static/images/sparring/talk-icon.png"></image>
|
||||
{{ detailInfo.traInterTypDesc }}
|
||||
</view>
|
||||
@@ -93,27 +108,62 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, unref, onMounted, computed, nextTick } from 'vue'
|
||||
import { onShow, onHide, onLoad, onUnload, onLaunch, onBackPress } from '@dcloudio/uni-app'
|
||||
import {
|
||||
ref,
|
||||
unref,
|
||||
onMounted,
|
||||
computed,
|
||||
nextTick
|
||||
} from 'vue'
|
||||
import {
|
||||
onShow,
|
||||
onHide,
|
||||
onLoad,
|
||||
onUnload,
|
||||
onLaunch,
|
||||
onBackPress
|
||||
} from '@dcloudio/uni-app'
|
||||
import common from '@/common/common'
|
||||
import { getPhoneEnvBool } from '@/common/common'
|
||||
import TouchBtn from './components/touchBtn.vue'
|
||||
import {
|
||||
getPhoneEnvBool
|
||||
} from '@/common/common'
|
||||
// import TouchBtn from './components/touchBtn.vue'
|
||||
import ImagePreview from '@/components/image-preview/image-preview.vue'
|
||||
import { commonUploadVoiceFile } from '@/api/common.js'
|
||||
import { queryTraPartnerCharacterInfoById, partnerChatReportTrigger, checkTraPartnerInfoById } from '@/api/sparring.js'
|
||||
import { useSocketStore } from '@/store/socket.js'
|
||||
import { useStorageStore } from '@/store/storage.js'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import {
|
||||
commonUploadVoiceFile
|
||||
} from '@/api/common.js'
|
||||
import {
|
||||
queryTraPartnerCharacterInfoById,
|
||||
partnerChatReportTrigger,
|
||||
checkTraPartnerInfoById
|
||||
} from '@/api/sparring.js'
|
||||
import {
|
||||
useSocketStore
|
||||
} from '@/store/socket.js'
|
||||
import {
|
||||
useStorageStore
|
||||
} from '@/store/storage.js'
|
||||
import {
|
||||
storeToRefs
|
||||
} from 'pinia'
|
||||
import TalkingItem from './components/talking-item.vue'
|
||||
import badge from '@/components/points-and-badge/badge'
|
||||
import points from '@/components/points-and-badge/points'
|
||||
import usePointsAndBadge from '@/components/points-and-badge/usePointsAndBadge'
|
||||
|
||||
import { initRecord } from '@/composables/useExamSubject.js'
|
||||
import {
|
||||
initRecord
|
||||
} from '@/composables/useExamSubject.js'
|
||||
import permissionApi from '@/common/permission'
|
||||
const { pointAndBadgesRun, badgeRef, pointsRef } = usePointsAndBadge()
|
||||
const {
|
||||
pointAndBadgesRun,
|
||||
badgeRef,
|
||||
pointsRef
|
||||
} = usePointsAndBadge()
|
||||
const socketStore = useSocketStore()
|
||||
const { SocketObj } = storeToRefs(socketStore)
|
||||
const {
|
||||
SocketObj
|
||||
} = storeToRefs(socketStore)
|
||||
|
||||
const traId = ref('')
|
||||
const chaId = ref('')
|
||||
@@ -162,12 +212,69 @@ const scrollToBottomNow = (time = 100) => {
|
||||
}, time)
|
||||
})
|
||||
}
|
||||
|
||||
// 新发送语音
|
||||
const submitVoice = ({
|
||||
ossKey,
|
||||
text
|
||||
}) => {
|
||||
console.log('新发送语音', ossKey, text);
|
||||
if (!ossKey) {
|
||||
uni.showToast({
|
||||
title: '系统异常,请联系管理员',
|
||||
icon: 'none',
|
||||
duration: 1000
|
||||
});
|
||||
} else if (text) {
|
||||
if (!execId.value) {
|
||||
common.show('提示', '网络连接失败,请重新加载', false).then(res => {
|
||||
if (res) {
|
||||
common.redirectTo('/pages/sparring/tip?traId=' + traId.value + '&chaId=' + chaId.value + '&type=' +
|
||||
talkingType.value + '&isPreview=' + (isPreview.value ? '1' : ''))
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
askData.value = {
|
||||
execId: execId.value,
|
||||
intrctWay: '02', //01-文本;02-语音;03-图片
|
||||
intrctContent: text,
|
||||
fileId: ossKey,
|
||||
ossFileAddr: `/traoss/tras3/show/${ossKey}`,
|
||||
talkingVoice: '',
|
||||
voiceTime: 0
|
||||
}
|
||||
sendMessage({
|
||||
commond: 'ASK',
|
||||
body: askData.value
|
||||
})
|
||||
} else {
|
||||
talkingList.value.pop();
|
||||
uni.showToast({
|
||||
title: '未识别到文字',
|
||||
icon: 'none',
|
||||
duration: 1000
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 新发送语音前loading动画
|
||||
const sendLoading = () => {
|
||||
talkingList.value.push({
|
||||
type: 'question',
|
||||
talkingText: '',
|
||||
isLoading: true
|
||||
});
|
||||
scrollToBottomNow();
|
||||
}
|
||||
|
||||
const uploadRecordNow = (voicePath, voiceTime) => {
|
||||
console.log(voicePath, voiceTime)
|
||||
if (!execId.value) {
|
||||
common.show('提示', '网络连接失败,请重新加载', false).then(res => {
|
||||
if (res) {
|
||||
common.redirectTo('/pages/sparring/tip?traId=' + traId.value + '&chaId=' + chaId.value + '&type=' + talkingType.value + '&isPreview=' + (isPreview.value ? '1' : ''))
|
||||
common.redirectTo('/pages/sparring/tip?traId=' + traId.value + '&chaId=' + chaId.value + '&type=' +
|
||||
talkingType.value + '&isPreview=' + (isPreview.value ? '1' : ''))
|
||||
}
|
||||
})
|
||||
return
|
||||
@@ -262,7 +369,8 @@ const toTalking = async () => {
|
||||
} else if (detailInfo.value.traInterTyp === '03') {
|
||||
_gotUrl = '/pages/sparring/talk' // 没改好,暂时跳到一个页
|
||||
}
|
||||
common.redirectTo(_gotUrl + '?traId=' + traId.value + '&chaId=' + chaId.value + '&type=' + talkingType.value + '&traInterTyp=' + detailInfo.value.traInterTyp + '&isPreview=' + (isPreview.value ? '1' : '0'))
|
||||
common.redirectTo(_gotUrl + '?traId=' + traId.value + '&chaId=' + chaId.value + '&type=' + talkingType.value +
|
||||
'&traInterTyp=' + detailInfo.value.traInterTyp + '&isPreview=' + (isPreview.value ? '1' : '0'))
|
||||
} catch (err) {
|
||||
console.error('申请权限失败')
|
||||
}
|
||||
@@ -273,7 +381,12 @@ const changePlayItemId = (id = '') => {
|
||||
activeVoiceTalkingItemId.value = id
|
||||
}
|
||||
|
||||
const talkItemEvents = ({ type, data, id, info = {} }) => {
|
||||
const talkItemEvents = ({
|
||||
type,
|
||||
data,
|
||||
id,
|
||||
info = {}
|
||||
}) => {
|
||||
switch (type) {
|
||||
case 'changePlay':
|
||||
activeVoiceTalkingItemId.value = id
|
||||
@@ -324,7 +437,8 @@ const initsocketTask = () => {
|
||||
reconcatNum.value += 1
|
||||
// common.redirectTo(`/pages/index/dialog`);
|
||||
socketStore?.closeSocket()
|
||||
common.redirectTo('/pages/sparring/result?isOver=1&traId=' + traId.value + '&execId=' + execId.value + '&type=' + talkingType.value + '&isPreview=' + (isPreview.value ? '1' : ''))
|
||||
common.redirectTo('/pages/sparring/result?isOver=1&traId=' + traId.value + '&execId=' + execId
|
||||
.value + '&type=' + talkingType.value + '&isPreview=' + (isPreview.value ? '1' : ''))
|
||||
})
|
||||
})
|
||||
.finally(() => {
|
||||
@@ -332,7 +446,11 @@ const initsocketTask = () => {
|
||||
})
|
||||
})
|
||||
unref(SocketObj).on('message', res => {
|
||||
const { rtnCode, body, commond } = JSON.parse(res.data)
|
||||
const {
|
||||
rtnCode,
|
||||
body,
|
||||
commond
|
||||
} = JSON.parse(res.data)
|
||||
let _id = new Date().getTime() + 'id'
|
||||
if (rtnCode === '0000') {
|
||||
if (commond === 'ASK-OPEN') {
|
||||
@@ -451,7 +569,8 @@ const initsocketTask = () => {
|
||||
console.log(body)
|
||||
console.log('陪练已结束,去结果页面!')
|
||||
socketStore?.closeSocket()
|
||||
common.redirectTo('/pages/sparring/result?isOver=1&traId=' + traId.value + '&execId=' + execId.value + '&type=' + talkingType.value + '&isPreview=' + (isPreview.value ? '1' : ''))
|
||||
common.redirectTo('/pages/sparring/result?isOver=1&traId=' + traId.value + '&execId=' + execId.value +
|
||||
'&type=' + talkingType.value + '&isPreview=' + (isPreview.value ? '1' : ''))
|
||||
return
|
||||
}
|
||||
if (commond === 'ASK-PONG') {
|
||||
@@ -486,7 +605,8 @@ const overTalking = () => {
|
||||
if (!execId.value) {
|
||||
common.show('提示', '网络连接失败,请重新加载', false).then(res => {
|
||||
if (res) {
|
||||
common.redirectTo('/pages/sparring/tip?traId=' + traId.value + '&chaId=' + chaId.value + '&type=' + talkingType.value + '&isPreview=' + (isPreview.value ? '1' : ''))
|
||||
common.redirectTo('/pages/sparring/tip?traId=' + traId.value + '&chaId=' + chaId.value + '&type=' +
|
||||
talkingType.value + '&isPreview=' + (isPreview.value ? '1' : ''))
|
||||
}
|
||||
})
|
||||
return
|
||||
@@ -507,7 +627,8 @@ const overNow = (cb = () => {}) => {
|
||||
})
|
||||
socketStore?.closeSocket()
|
||||
changePlayItemId('')
|
||||
common.redirectTo('/pages/sparring/result?isOver=1&traId=' + traId.value + '&execId=' + execId.value + '&type=' + talkingType.value + '&isPreview=' + (isPreview.value ? '1' : ''))
|
||||
common.redirectTo('/pages/sparring/result?isOver=1&traId=' + traId.value + '&execId=' + execId.value + '&type=' +
|
||||
talkingType.value + '&isPreview=' + (isPreview.value ? '1' : ''))
|
||||
|
||||
// partnerChatReportTrigger({
|
||||
// execId: execId.value,
|
||||
@@ -764,6 +885,7 @@ onBackPress(res => {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-evenly;
|
||||
|
||||
.role-name {
|
||||
width: 100%;
|
||||
|
||||
@@ -887,6 +1009,7 @@ onBackPress(res => {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.talking-desc {
|
||||
// padding-top: 24rpx;
|
||||
padding: 24rpx 20rpx 0;
|
||||
@@ -894,6 +1017,7 @@ onBackPress(res => {
|
||||
font-size: 24rpx;
|
||||
// text-align: center;
|
||||
color: #999;
|
||||
|
||||
&.second {
|
||||
padding-top: 0;
|
||||
}
|
||||
@@ -933,7 +1057,7 @@ onBackPress(res => {
|
||||
.body-bottom {
|
||||
height: 180rpx;
|
||||
background-color: #fff;
|
||||
padding: 36rpx 23rpx 0;
|
||||
padding: 36rpx 10rpx 0;
|
||||
position: fixed;
|
||||
width: 100vw;
|
||||
bottom: 0;
|
||||
@@ -970,4 +1094,26 @@ onBackPress(res => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.body-bottom{
|
||||
.center-text {
|
||||
height: 35rpx;
|
||||
line-height: 35rpx;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
color:#fff;
|
||||
}
|
||||
|
||||
.type-icon {
|
||||
width: 35rpx;
|
||||
height: 35rpx;
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user