414 lines
9.2 KiB
Vue
414 lines
9.2 KiB
Vue
<template>
|
||
<view class="answer-content" v-if="props.isLoading">
|
||
<view class="talking-cont">
|
||
<CommonLoading color="#06f" />
|
||
</view>
|
||
</view>
|
||
<view class="answer-content" v-else>
|
||
<view :class="['talking-gif', !voiceLoading&&isPlaying ? 'is-play' : '']">
|
||
</view>
|
||
<view class="talking-cont">
|
||
<MarkdownPreview :mdString="mdText" />
|
||
</view>
|
||
<view class="talking-link">
|
||
<view class="link-cont-desc">语音转文字的耗时:{{ dataInfo.asrTime }}ms</view>
|
||
<!-- <view class="link-cont-desc">请求的文字:{{ dataInfo.reqContent }}</view> -->
|
||
<view class="link-cont-desc">请求内容的字符长度:{{ dataInfo.reqContSize }}</view>
|
||
<!-- <view class="link-cont-desc">AI问答返回内容:{{ dataInfo.aiContent }}</view> -->
|
||
<view class="link-cont-desc">AI问答返回内容字符的长度:{{ dataInfo.aiContentSize }}</view>
|
||
<view class="link-cont-desc">AI问答总耗时:{{ dataInfo.aiAskTime }}ms</view>
|
||
<view class="link-cont-desc">文字转语音消耗时长:{{ dataInfo.transVoice }}ms</view>
|
||
<!-- <view class="link-cont-desc">oss 文件id:{{ dataInfo.fileId }}</view> -->
|
||
<view class="link-cont-desc">上传OSS文件服务消耗时长:{{ dataInfo.ossUpTime }}ms</view>
|
||
<view class="link-cont-desc">AI回答语音长度时长:{{ dataInfo.playVoiceTime }}s</view>
|
||
<!-- <view class="link-cont-desc">本次回答参考资料来源于网络</view> -->
|
||
</view>
|
||
<view class="btns-cont">
|
||
<view class="replay-btn" >
|
||
<image class="icon" src="@/static/images/course/play.png" style="width: 100%; height: 100%"
|
||
v-show="!isPlaying"
|
||
@click.stop="playVoice()" ></image>
|
||
<image class="icon"src="@/static/images/course/pause.png"
|
||
v-show="isPlaying"
|
||
style="width: 100%; height: 100%" @click.stop="pauseVoice"></image>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { onMounted, computed, ref, watch, toRefs } from 'vue'
|
||
import { onUnload } from '@dcloudio/uni-app';
|
||
import MarkdownPreview from '@/components/markdown-preview/markdown-preview.vue'
|
||
import common from '@/common/common'
|
||
import {
|
||
downloadVoiceFile,
|
||
downloadFile
|
||
} from '@/api/common.js';
|
||
|
||
const emit = defineEmits(['handleEvents', 'changePlay'])
|
||
const props = defineProps({
|
||
dataInfo: {
|
||
default: () => ({}),
|
||
type: Object
|
||
},
|
||
isLoading: {
|
||
default: false,
|
||
type: Boolean
|
||
},
|
||
isAnswering: {
|
||
default: false,
|
||
type: Boolean
|
||
},
|
||
activeVoiceId:{
|
||
default:'',
|
||
type: String
|
||
},
|
||
isLast: {
|
||
default: false,
|
||
type: Boolean
|
||
}
|
||
})
|
||
const { activeVoiceId, dataInfo } = toRefs(props)
|
||
|
||
watch(
|
||
() => props.dataInfo,
|
||
() => {},
|
||
{
|
||
deep: true,
|
||
immediate: true
|
||
}
|
||
)
|
||
watch(
|
||
() => props.isLoading,
|
||
(val) => {
|
||
if(!val){
|
||
setTimeout(()=> {
|
||
playVoice()
|
||
},500)
|
||
}
|
||
},
|
||
{
|
||
deep: true,
|
||
immediate: true
|
||
}
|
||
)
|
||
const isPlaying = ref(false)
|
||
watch(
|
||
() => props.activeVoiceId,
|
||
(val) => {
|
||
if(val === props.dataInfo.id){
|
||
isPlaying.value = true
|
||
}else{
|
||
isPlaying.value = false
|
||
}
|
||
},
|
||
{
|
||
deep: true,
|
||
immediate: true
|
||
}
|
||
)
|
||
const mdText = computed(() => {
|
||
let _text = (props.dataInfo?.talkingText|| '').replace(/\\n/g, `\n`);
|
||
return _text
|
||
})
|
||
const fileList = computed(() => props.dataInfo?.talkingFiles || [])
|
||
const crsList = computed(() => props.dataInfo?.talkingCrsList || [])
|
||
const activeNames = ref([1, 2])
|
||
|
||
const talkVoiceObj = ref(null)
|
||
const voiceLoading = ref(false)
|
||
|
||
const voiceTime = ref('')
|
||
const itemVoice = ref('')
|
||
const initVoice = () => {
|
||
talkVoiceObj.value = uni.createInnerAudioContext()
|
||
talkVoiceObj.value.onEnded(()=>{
|
||
emit('changePlay', '')
|
||
talkVoiceObj.value.src = ''
|
||
isPlaying.value = false
|
||
})
|
||
talkVoiceObj.value.onError(()=>{
|
||
talkVoiceObj.value.src = ''
|
||
isPlaying.value = false
|
||
})
|
||
}
|
||
const setVoiceTime = (sum) => {
|
||
sum += 1
|
||
if(Math.ceil(talkVoiceObj.value.duration)>0){
|
||
voiceTime.value = (Math.ceil(talkVoiceObj.value.duration) || 1 ) + 's'
|
||
}else{
|
||
if(sum === 20) {
|
||
voiceTime.value = 0 + 's'
|
||
return
|
||
}
|
||
setTimeout(()=>{
|
||
setVoiceTime(sum)
|
||
},200)
|
||
}
|
||
}
|
||
const playVoice = (readType = 'pgth') => {
|
||
let _content = mdText.value;
|
||
if (!_content) {
|
||
common.msg('没有可以播放的文字信息!');
|
||
return;
|
||
}
|
||
isPlaying.value = true
|
||
voiceLoading.value = true;
|
||
setTimeout(() => {
|
||
|
||
|
||
downloadFile(props.dataInfo?.talkingVoiceId)
|
||
.then((res) => {
|
||
if (res?.data) {
|
||
common.msg(res.data.message);
|
||
return;
|
||
}
|
||
voiceLoading.value = false;
|
||
itemVoice.value = res.tempFilePath;
|
||
talkVoiceObj.value.src = res.tempFilePath;
|
||
talkVoiceObj.value.play();
|
||
})
|
||
.catch((err) => {
|
||
voiceLoading.value = false;
|
||
});
|
||
}, 100);
|
||
};
|
||
|
||
const pauseVoice = () => {
|
||
if(!talkVoiceObj.value) return
|
||
talkVoiceObj.value.pause()
|
||
isPlaying.value = false
|
||
emit('changePlay', '')
|
||
}
|
||
onMounted(()=>{
|
||
initVoice()
|
||
})
|
||
onUnload(()=>{
|
||
pauseVoice()
|
||
})
|
||
watch(() => props.dataInfo.talkingVoice,(val)=>{
|
||
if(val){
|
||
initVoice()
|
||
}
|
||
},{
|
||
deep: true,
|
||
immediate: true
|
||
})
|
||
const changeActive = val => {
|
||
if (activeNames.value.indexOf(val) >= 0) {
|
||
activeNames.value = activeNames.value.filter(t => t !== val)
|
||
} else {
|
||
activeNames.value.push(val)
|
||
}
|
||
}
|
||
const reAnswer = () => {
|
||
emit('handleEvents', 'reAnswer')
|
||
}
|
||
const stopAnswer = () => {
|
||
emit('handleEvents', 'stop')
|
||
}
|
||
const zanAnswer = () => {
|
||
emit('handleEvents', 'great')
|
||
}
|
||
const feedbackAnswer = () => {
|
||
emit('handleEvents', 'feedback')
|
||
}
|
||
const previewFile = item => {
|
||
emit('handleEvents', 'preview', item)
|
||
}
|
||
const previewCrs = item => {
|
||
common.navigateTo('/pages/courseDetail/index?crsId=' + item.crsId)
|
||
}
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
.answer-content {
|
||
background-color: #f5f5f5;
|
||
padding: 20rpx;
|
||
border-radius: 15rpx;
|
||
padding-bottom: 10rpx;
|
||
position: relative;
|
||
overflow: hidden;
|
||
float: left;
|
||
width: 100%;
|
||
min-height: 80rpx;
|
||
|
||
.talking-gif{
|
||
width: 200rpx;
|
||
height: 38rpx;
|
||
margin-bottom: 20rpx;
|
||
background: url(@/static/images/course/voice-gray.png) no-repeat;
|
||
background-size: contain;
|
||
padding-left: 168rpx;
|
||
&.is-play{
|
||
background: url(@/static/images/course/voice-gif.gif) no-repeat;
|
||
background-size: contain;
|
||
}
|
||
}
|
||
.talking-cont {
|
||
padding-bottom: 20rpx;
|
||
|
||
}
|
||
|
||
.btns-cont {
|
||
overflow: hidden;
|
||
border-top: 3rpx solid #eee;
|
||
padding-bottom: 10rpx;
|
||
|
||
.replay-btn {
|
||
float: left;
|
||
width: 46rpx;
|
||
height: 46rpx;
|
||
overflow: hidden;
|
||
margin-right: 20rpx;
|
||
margin-top: 20rpx;
|
||
}
|
||
|
||
.play-btn {
|
||
float: left;
|
||
width: 46rpx;
|
||
height: 46rpx;
|
||
overflow: hidden;
|
||
margin-right: 20rpx;
|
||
margin-top: 20rpx;
|
||
}
|
||
|
||
.next-learn-btn {
|
||
float: left;
|
||
padding: 0 60rpx 0 20rpx;
|
||
height: 62rpx;
|
||
line-height: 62rpx;
|
||
font-size: 30rpx;
|
||
background-color: #06f;
|
||
color: #fff;
|
||
border-radius: 8rpx;
|
||
margin-top: 16rpx;
|
||
margin-right: 16rpx;
|
||
position: relative;
|
||
|
||
&.replay-study-btn {
|
||
float: right;
|
||
}
|
||
|
||
&.restart {
|
||
background-color: #eaf2ff;
|
||
color: #06f;
|
||
}
|
||
|
||
.icon-iamge {
|
||
width: 25rpx;
|
||
height: 25rpx;
|
||
position: absolute;
|
||
right: 20rpx;
|
||
top: 50%;
|
||
margin-top: -12rpx;
|
||
}
|
||
}
|
||
|
||
.replay-learn-btn {
|
||
float: left;
|
||
padding: 0 60rpx 0 20rpx;
|
||
height: 62rpx;
|
||
line-height: 62rpx;
|
||
font-size: 30rpx;
|
||
color: #06f;
|
||
background-color: #eaf2ff;
|
||
border-radius: 8rpx;
|
||
margin-top: 16rpx;
|
||
margin-right: 16rpx;
|
||
position: relative;
|
||
|
||
.icon-iamge {
|
||
width: 25rpx;
|
||
height: 25rpx;
|
||
position: absolute;
|
||
right: 20rpx;
|
||
top: 50%;
|
||
margin-top: -12rpx;
|
||
}
|
||
}
|
||
|
||
.next-btn {
|
||
float: right;
|
||
padding: 0 60rpx 0 20rpx;
|
||
height: 62rpx;
|
||
line-height: 62rpx;
|
||
font-size: 30rpx;
|
||
background-color: #06f;
|
||
color: #fff;
|
||
border-radius: 8rpx;
|
||
margin-top: 16rpx;
|
||
position: relative;
|
||
|
||
.icon-iamge {
|
||
width: 25rpx;
|
||
height: 25rpx;
|
||
position: absolute;
|
||
right: 20rpx;
|
||
top: 50%;
|
||
margin-top: -12rpx;
|
||
}
|
||
}
|
||
}
|
||
.link-cont-desc {
|
||
margin-bottom: 12rpx;
|
||
font-size: 24rpx;
|
||
color: #999;
|
||
}
|
||
.link-cont {
|
||
margin-bottom: 12rpx;
|
||
font-size: 28rpx;
|
||
.link-info {
|
||
height: 50rpx;
|
||
line-height: 50rpx;
|
||
overflow: hidden;
|
||
margin-bottom: 12rpx;
|
||
|
||
.info-text {
|
||
float: left;
|
||
padding-right: 20rpx;
|
||
}
|
||
|
||
&::after {
|
||
transition: all 0.3s ease-in-out;
|
||
content: '';
|
||
display: block;
|
||
float: left;
|
||
position: relative;
|
||
top: 12rpx;
|
||
width: 12rpx;
|
||
height: 12rpx;
|
||
border-right: 4rpx solid #333;
|
||
border-bottom: 4rpx solid #333;
|
||
transform: rotate(135deg);
|
||
}
|
||
}
|
||
|
||
&.is-active {
|
||
.link-info {
|
||
&::after {
|
||
top: 18rpx;
|
||
transform: rotate(45deg);
|
||
}
|
||
}
|
||
|
||
.link-items {
|
||
max-height: 600rpx;
|
||
}
|
||
}
|
||
|
||
.link-items {
|
||
max-height: 0rpx;
|
||
overflow: hidden;
|
||
transition: all 0.3s ease-in-out;
|
||
|
||
.link-item {
|
||
line-height: 48rpx;
|
||
color: #06f;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</style>
|