1280 lines
33 KiB
Vue
1280 lines
33 KiB
Vue
<!-- type:
|
||
0,纯文本信息不翻译
|
||
1,语音回答 ,
|
||
2 文本回答
|
||
3 段落信息
|
||
4 问题信息
|
||
5 统计信息
|
||
6 发送loading状态
|
||
7 教师反馈文本不翻译
|
||
8 问题反馈信息
|
||
9 已学完当前知识点反馈信息
|
||
-->
|
||
<template>
|
||
<view :class="['ai-answer', props.class]" :id="props.id" v-if="[3, 4, 5, 8, 9].indexOf(type) >= 0">
|
||
<!-- 头像信息 -->
|
||
<view class="user-info">
|
||
<view class="avatar-box">
|
||
<ImagePreview class="img-box" :imgId="teacherInfo?.imgAddr"></ImagePreview>
|
||
</view>
|
||
<view class="ai-name">
|
||
{{ teacherInfo?.teacherName }}
|
||
<view class="ai-name-desc">{{ teacherInfo?.teacherDesc }}</view>
|
||
</view>
|
||
</view>
|
||
<!-- 对话信息 -->
|
||
<view :class="['talking-info', [4].indexOf(type) >= 0 ? 'talking-info-less' : '']">
|
||
<!-- 语音加载状态 -->
|
||
<view :class="['talking-gif', !voiceLoading && activeVoiceId === dataInfo.id ? 'is-play' : '']"
|
||
v-if="!([2, 5, 8, 9].indexOf(type) >= 0)">
|
||
<CommonLoading color="#06f" v-show="voiceLoading" />
|
||
</view>
|
||
<view class="talking-cont">
|
||
<!-- 加载状态 -->
|
||
<CommonLoading color="#06f" v-show="dataInfo.isLoading" />
|
||
<!-- 参考答案 -->
|
||
<view class="talking-text" v-if="
|
||
false
|
||
//type === 5
|
||
&& !dataInfo.isLoading" style="overflow: hidden">
|
||
参考答案:{{ dataInfo.itemAnswer }}
|
||
</view>
|
||
<!-- 维度信息预览 -->
|
||
<view v-if="
|
||
false
|
||
//type === 5
|
||
&& !dataInfo.isLoading" style="padding-top: 16rpx">
|
||
<ChartFour v-if="dataInfo.evalDimens.length === 4" :dataList="dataInfo.evalDimens"
|
||
:score="dataInfo.asrGrade" />
|
||
<ChartThree v-if="dataInfo.evalDimens.length === 3" :dataList="dataInfo.evalDimens"
|
||
:score="dataInfo.asrGrade" />
|
||
<ChartFive v-if="dataInfo.evalDimens.length === 5" :dataList="dataInfo.evalDimens"
|
||
:score="dataInfo.asrGrade" />
|
||
</view>
|
||
<!-- 题干/段落内容 -->
|
||
<text class="talking-text" v-if="[3,
|
||
// 5,
|
||
6, 9].indexOf(type) >= 0 && !dataInfo.isLoading">
|
||
{{ type === 5 ? '试题解析:' : '' }}{{ showContent }}
|
||
</text>
|
||
<!-- <view class="type-tag" v-if="[4].indexOf(type) >= 0 && dataInfo.qnsTyp === 'ES'">问答题</view>
|
||
<text class="talking-text" v-if="[4].indexOf(type) >= 0 && dataInfo.qnsTyp === 'ES'">
|
||
{{ showContent }}
|
||
</text> -->
|
||
<!-- 试题问题 -->
|
||
<Subject v-if="[4, 5, 8].indexOf(type) >= 0 && !dataInfo.isLoading" :isPreview="!isLast" :item="subjectInfo"
|
||
:mode="mode" :clearResult="type === 4" @subject_click="subject_click" />
|
||
<!-- 试题问题反馈 -->
|
||
<!-- <RadioSubjectRequest v-if="[8].indexOf(type) >= 0 && dataInfo.qnsTyp !== 'ES'" :item="dataInfo" :mode="mode" @subject_click="subject_click" /> -->
|
||
<!-- 段落图片视频预览 -->
|
||
<view v-if="type === 3" style="overflow: hidden;padding-top: 10rpx;">
|
||
<ImagePreview :class="['cont-img-box',imgAddrs.length === 1?'cont-video-box':imgAddrs.length === 2?'cont-img-box2':'']" v-for="item in imgAddrs "
|
||
:imgId="item" :previewDetail="false" @click="previewImage(item, imgAddrs)"></ImagePreview>
|
||
<VideoPreview class="cont-img-box cont-video-box" v-if="(dataInfo.vidoAddrs || []).length > 0"
|
||
:fileId="dataInfo.vidoAddrs[0].vidAddr"
|
||
:picId="dataInfo.vidoAddrs[0].vidImgAddr" @click="previewVideo(dataInfo.vidoAddrs[0])">
|
||
</VideoPreview>
|
||
<view class="file-items" v-if="(dataInfo.fileData || []).length > 0">
|
||
<view class="file-item" v-for="item in dataInfo.fileData" @click="previewFile(item)">
|
||
{{item.fileName.replace(/.docx|.doc|.txt/g, '.pdf')}}
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<!-- 举例预览 -->
|
||
<view class="talking-text" v-if="type === 3 && dataInfo.exampie" style="overflow: hidden">
|
||
举例:{{ dataInfo.exampie }}
|
||
</view>
|
||
</view>
|
||
<view class="btns-cont" v-show="!dataInfo.isLoading">
|
||
<!-- 重新播放 -->
|
||
<view class="replay-btn" @click.stop="restartPlay" v-if="!([9].indexOf(type) >= 0)">
|
||
<image class="icon" src="@/static/images/course/replay.png" style="width: 100%; height: 100%"></image>
|
||
</view>
|
||
<!-- 播放/暂停 -->
|
||
<view class="play-btn" v-if="!([9].indexOf(type) >= 0)">
|
||
<image class="icon" src="@/static/images/course/play.png" style="width: 100%; height: 100%"
|
||
@click.stop="playVoice()" v-show="!isPlaying"></image>
|
||
<image class="icon" v-show="isPlaying" src="@/static/images/course/pause.png"
|
||
style="width: 100%; height: 100%" @click.stop="pauseVoice"></image>
|
||
</view>
|
||
<view class="next-learn-btn" v-if="showNextBtn && [5, 8, 9].indexOf(type) >= 0 && isLast"
|
||
@click.stop="goOnRestartPrgh">
|
||
重新学
|
||
<image class="icon icon-iamge" src="@/static/images/course/refresh-white.png"></image>
|
||
</view>
|
||
<view class="next-learn-btn restart" v-if="showNextBtn && [5, 8].indexOf(type) >= 0 && isLast"
|
||
@click.stop="goOnRestart">
|
||
重新答
|
||
<image class="icon icon-iamge" src="@/static/images/course/restart.png"></image>
|
||
</view>
|
||
<view class="replay-study-btn next-learn-btn" v-if="showGoOnStudyBtn" @click.stop="goOnGetQues">
|
||
继续学
|
||
<image class="icon icon-iamge" src="@/static/images/course/goon.png"></image>
|
||
</view>
|
||
<view class="next-btn" @click.stop="goOnGetQues" v-if="showGoonBtn">
|
||
继续
|
||
<image class="icon icon-iamge" src="@/static/images/course/goon.png"></image>
|
||
</view>
|
||
</view>
|
||
<!-- 简答鼓励信息 -->
|
||
<!-- <view class="talking-cont last-cont" v-if=" [5].indexOf(type) >= 0 && !dataInfo.isLoading">
|
||
<view class="talking-text">
|
||
{{ changeBr(dataInfo.suggestContent || '--') }}
|
||
</view>
|
||
</view> -->
|
||
</view>
|
||
</view>
|
||
<!-- ai回答 -->
|
||
<view :class="['ai-answer', props.class]" :id="props.id" v-if="[7].indexOf(type) >= 0">
|
||
<view class="user-info">
|
||
<view class="avatar-box">
|
||
<ImagePreview class="img-box" :imgId="teacherInfo?.imgAddr"></ImagePreview>
|
||
</view>
|
||
<view class="ai-name">
|
||
{{ teacherInfo?.teacherName }}
|
||
<view class="ai-name-desc">{{ teacherInfo?.teacherDesc }}</view>
|
||
</view>
|
||
</view>
|
||
<view :class="['talking-info', [4, 7].indexOf(type) >= 0 ? 'talking-info-less' : '']">
|
||
<view class="talking-cont">
|
||
<view class="talking-text">
|
||
{{ showContent }}
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<!-- 用户对话 -->
|
||
<view :class="['user-answer', props.class]" :id="props.id" v-else-if="[1, 2].indexOf(type) >= 0">
|
||
<view class="user-info">
|
||
<view class="avatar-box">
|
||
<image v-if="userInfo.imagePath" class="img-box" :src="userInfo.imagePath" style="width: 100%; height: 100%">
|
||
</image>
|
||
<ImagePreview v-else-if="userInfo.imageAddr" class="img-box" :src="userInfo.imageAddr"
|
||
style="width: 100%; height: 100%"></ImagePreview>
|
||
<image v-else class="img-box" src="/static/images/me/default_user_avatar.png" style="width: 100%; height: 100%">
|
||
</image>
|
||
</view>
|
||
<view class="ai-name">
|
||
{{ userInfo.userName }}
|
||
</view>
|
||
</view>
|
||
|
||
<view class="talking-info">
|
||
<view :class="['talking-gif', !voiceLoading && activeVoiceId === dataInfo.id ? 'is-play' : '']"
|
||
v-if="[1].indexOf(type) >= 0">
|
||
<CommonLoading color="#fff" v-show="voiceLoading || dataInfo.isLoading" />
|
||
</view>
|
||
<view class="right-time">{{ voiceTime }}</view>
|
||
<view class="talking-cont" v-show="!dataInfo.isLoading">
|
||
<text :class="['talking-text translate-text loading']" v-show="!translateLoading">
|
||
{{ showContent }}
|
||
</text>
|
||
<view class="talking-text translate-text" v-if="translateLoading">
|
||
<CommonLoading color="#fff" />
|
||
</view>
|
||
</view>
|
||
<view class="btns-cont" v-show="!dataInfo.isLoading">
|
||
<!-- <view class="replay-btn">
|
||
<image class="icon" src="@/static/images/course/replay.png" style="width: 100%;height: 100%;"></image>
|
||
</view> -->
|
||
<view class="play-btn" v-if="[1].indexOf(type) >= 0">
|
||
<image class="icon" @click.stop="playQnsVoice('qns', dataInfo)" v-show="!isPlaying"
|
||
src="@/static/images/course/play-blue.png" style="width: 100%; height: 100%"></image>
|
||
<image class="icon" v-show="isPlaying" @click.stop="pauseVoice" src="@/static/images/course/stop-blue.png"
|
||
style="width: 100%; height: 100%"></image>
|
||
</view>
|
||
<view class="icon text-btn" v-if="type === 1" @click.stop="translateVoice(dataInfo)">文</view>
|
||
<view class="icon text-right-btn" @click.stop="finishQuestion(dataInfo)" v-show="isLast">完成</view>
|
||
<view class="icon text-right-btn" @click.stop="withdraw" v-show="showWithdraw">
|
||
撤回
|
||
<!-- v-show="showWithdraw" -->
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view :class="['user-answer', props.class]" :id="props.id" v-else-if="[0, 6].indexOf(type) >= 0">
|
||
<view class="user-info">
|
||
<view class="avatar-box">
|
||
<image v-if="userInfo.imagePath" class="img-box" :src="userInfo.imagePath" style="width: 100%; height: 100%">
|
||
</image>
|
||
<ImagePreview v-else-if="userInfo.imageAddr" class="img-box" :src="userInfo.imageAddr"
|
||
style="width: 100%; height: 100%"></ImagePreview>
|
||
<image v-else class="img-box" src="/static/images/me/default_user_avatar.png" style="width: 100%; height: 100%">
|
||
</image>
|
||
</view>
|
||
<view class="ai-name">
|
||
{{ userInfo.userName }}
|
||
</view>
|
||
</view>
|
||
<view class="talking-info-less">
|
||
<text class="talking-text" v-if="type === 0">
|
||
{{ showContent }}
|
||
</text>
|
||
<view class="talking-text loading" v-if="type === 6">
|
||
<CommonLoading color="#fff" />
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view v-else></view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import {
|
||
watch,
|
||
unref,
|
||
ref,
|
||
toRefs,
|
||
computed,
|
||
nextTick,
|
||
onMounted
|
||
} from 'vue';
|
||
import ImagePreview from '@/components/image-preview/image-preview.vue';
|
||
import VideoPreview from '@/components/image-preview/video-preview.vue';
|
||
import {
|
||
downloadVoiceFile,
|
||
downloadVoiceParams,
|
||
uploadVoiceFile,
|
||
getAudioByText
|
||
} from '@/api/common.js';
|
||
import {
|
||
studyParagraphOverApi,
|
||
getAskAboutRawApi
|
||
} from '@/api/study.js';
|
||
import ChartFour from './chartFour.vue';
|
||
import ChartThree from './chartThree.vue';
|
||
import ChartFive from './chartFive.vue';
|
||
import common from '@/common/common';
|
||
import {
|
||
getUserInfo
|
||
} from '@/common/common';
|
||
import {
|
||
useStorageStore
|
||
} from '@/store/storage.js';
|
||
import {
|
||
onUnload,
|
||
onHide
|
||
} from '@dcloudio/uni-app';
|
||
|
||
import Subject from '@/components/examination/subject.vue';
|
||
// import RadioSubjectRequest from '@/pages/wrong_question_record/components/radio-subject.vue'
|
||
import {
|
||
previewFileFnc
|
||
} from '@/common/common'
|
||
|
||
|
||
const storageStore = useStorageStore();
|
||
const audioCacheObj = computed(() => storageStore.audioObj);
|
||
const isGettingId = computed(() => storageStore.getGettingVoiceId);
|
||
const props = defineProps({
|
||
type: {
|
||
default: 0, // 1: userAnswer 0: AIAnswer
|
||
type: [Number, String]
|
||
},
|
||
dataInfo: {
|
||
default: () => ({}),
|
||
type: Object
|
||
},
|
||
teacherInfo: {
|
||
default: () => ({}),
|
||
type: Object
|
||
},
|
||
activeVoiceId: {
|
||
default: '',
|
||
type: String
|
||
},
|
||
answerMethod: {
|
||
default: '01',
|
||
type: String
|
||
},
|
||
isLast: {
|
||
default: false,
|
||
type: Boolean
|
||
},
|
||
isPlaying: {
|
||
default: false,
|
||
type: Boolean
|
||
},
|
||
voiceRate: {
|
||
default: '1.0',
|
||
type: [Number, String]
|
||
},
|
||
isTransform: {
|
||
default: false,
|
||
type: Boolean
|
||
},
|
||
lastQuestionInfo: {
|
||
default: () => ({}),
|
||
type: Object
|
||
},
|
||
class: {
|
||
default: '',
|
||
type: String
|
||
},
|
||
id: {
|
||
default: '',
|
||
type: String
|
||
}
|
||
});
|
||
|
||
const {
|
||
type,
|
||
dataInfo,
|
||
activeVoiceId,
|
||
isPlaying,
|
||
isLast,
|
||
lastQuestionInfo,
|
||
teacherInfo
|
||
} = toRefs(props);
|
||
const emit = defineEmits(['changePlay', 'nextQuestion', 'withdraw', 'finishQuestion', 'answerQuestion']);
|
||
// 声音播放初始化
|
||
const talkVoiceObj = ref(uni.createInnerAudioContext());
|
||
talkVoiceObj.value.playbackRate = Number(props.voiceRate);
|
||
const userInfo = computed(() => getUserInfo());
|
||
const itemVoice = ref('');
|
||
const subjectInfo = computed(() => {
|
||
return {
|
||
...dataInfo.value,
|
||
...(dataInfo.value?.traQnsInfoVo || {}),
|
||
my_answer: dataInfo.value?.traQnsInfoVo?.anserResult || '',
|
||
answerMethod: props.answerMethod,
|
||
...(props.type === 5?{
|
||
"qnsTyp": "ES",
|
||
my_answer: dataInfo.value,
|
||
itemVos: [{
|
||
...dataInfo.value,
|
||
"qnsTyp": "ES",
|
||
"qnsId": dataInfo.value.qnsId,
|
||
"itemAnswer": dataInfo.value.itemAnswer,
|
||
"relKeyword": dataInfo.value.relKeyword,
|
||
anserResult:dataInfo.value?.traQnsInfoVo?.anserResult || '',
|
||
"evalSettingVos": dataInfo.value?.essayEvalVo,
|
||
}]
|
||
}:{})
|
||
|
||
};
|
||
});
|
||
const mode = computed(()=>{
|
||
return props.type == 5 ? 'study_answer' : props.type == 8 ? 'study_result' : 'study'
|
||
}); // 考试 examination 练习practice 学习study 学习结果study_answer
|
||
|
||
const imgAddrs = computed(()=>{
|
||
return (dataInfo.value.imageAddrs || [])
|
||
})
|
||
|
||
const previewFile = info => {
|
||
previewFileFnc({
|
||
fileId: info.fileId,
|
||
getFileSrc: '/traapp/traFilePreview/',
|
||
fileName: info.fileName + '.pdf'
|
||
})
|
||
}
|
||
const showWithdraw = computed(() => {
|
||
let _isMe = unref(lastQuestionInfo).qstLogId === unref(dataInfo).qstLogId;
|
||
let _isQuestion = unref(lastQuestionInfo).isQuestion;
|
||
return _isMe && _isQuestion;
|
||
});
|
||
|
||
const voiceLoading = ref(false);
|
||
watch(
|
||
() => props.teacherInfo,
|
||
(val) => {
|
||
itemVoice.value = '';
|
||
talkVoiceObj.value.src = '';
|
||
}, {
|
||
deep: true,
|
||
immediate: true
|
||
}
|
||
);
|
||
watch(
|
||
() => props.isPlaying,
|
||
(val) => {
|
||
if (!val) {
|
||
talkVoiceObj.value?.pause();
|
||
}
|
||
}, {
|
||
deep: true,
|
||
immediate: true
|
||
}
|
||
);
|
||
watch(
|
||
() => props.voiceRate,
|
||
(val) => {
|
||
talkVoiceObj.value.playbackRate = Number(val);
|
||
}, {
|
||
deep: true,
|
||
immediate: true
|
||
}
|
||
);
|
||
watch(
|
||
() => props.lastTalkingInfo,
|
||
() => {}, {
|
||
deep: true,
|
||
immediate: true
|
||
}
|
||
);
|
||
watch(
|
||
() => props.dataInfo,
|
||
() => {}, {
|
||
deep: true,
|
||
immediate: true
|
||
}
|
||
);
|
||
const showGoOnStudyBtn = computed(() => {
|
||
if (!(showNextBtn.value && isLast.value)) {
|
||
return false;
|
||
}
|
||
if (type.value === 5) {
|
||
if(dataInfo.value.stdyStat !== 'Y' && dataInfo.value.isLastQns === 'Y'){
|
||
return false
|
||
}else{
|
||
return true;
|
||
}
|
||
}
|
||
if (type.value === 8) {
|
||
if(dataInfo.value.stdyStat !== 'Y' && dataInfo.value.isLastQns === 'Y'){
|
||
return false
|
||
}else{
|
||
return !!dataInfo.value.traQnsInfoVo?.judgeResult;
|
||
}
|
||
}
|
||
if (type.value === 9) {
|
||
return dataInfo.value.stdyStat === 'Y';
|
||
}
|
||
return false;
|
||
});
|
||
const transformContent = ref('');
|
||
const showContentBase = computed(() => {
|
||
if (props.isTransform) {
|
||
return transformContent.value;
|
||
} else {
|
||
if (props.type === 1) return unref(dataInfo)?.chatContent || unref(dataInfo)?.talkingContent;
|
||
else if (props.type === 2) return unref(dataInfo)?.chatContent || unref(dataInfo)?.talkingContent;
|
||
else if (props.type === 3) return unref(dataInfo)?.chatContent || unref(dataInfo)?.pgrphContent;
|
||
else if (props.type === 4) return unref(dataInfo)?.chatContent || unref(dataInfo)?.qnsContent;
|
||
else if (props.type === 5) return unref(dataInfo)?.chatContent || unref(dataInfo)?.analyContent;
|
||
else if (props.type === 8) return unref(subjectInfo)?.chatContent || unref(subjectInfo)?.qnsContent;
|
||
else if (props.type === 9) return unref(dataInfo)?.chatContent || unref(dataInfo)?.analyContent;
|
||
else if (props.type === 0) return unref(dataInfo)?.chatContent || unref(dataInfo)?.talkingContent;
|
||
else return unref(dataInfo)?.chatContent || unref(dataInfo)?.talkingContent;
|
||
}
|
||
});
|
||
const showContent = computed(() => {
|
||
let _str = showContentBase.value || '';
|
||
return _str.replace(/\/n/g, '\n');
|
||
});
|
||
|
||
const changeBr = (str = '') => {
|
||
return str.replace(/\/n/g, '\n');
|
||
};
|
||
|
||
const previewVideo = (obj)=>{
|
||
console.log(obj)
|
||
storageStore.setShowPreviewDetail({
|
||
status: true,
|
||
fileType: 'video',
|
||
fileId: '',
|
||
fileSrc: obj.vidAddr
|
||
})
|
||
}
|
||
const previewImage = (obj, arr=[])=>{
|
||
storageStore.setShowPreviewDetail({
|
||
status: true,
|
||
fileType: arr.length>0?'imageList':'image',
|
||
fileId: '',
|
||
fileSrc: obj,
|
||
fileSrcList: arr
|
||
})
|
||
}
|
||
|
||
|
||
const voiceTime = ref('');
|
||
const itemInfo = ref({});
|
||
|
||
onMounted(() => {
|
||
initVoice();
|
||
});
|
||
onUnload(() => {
|
||
// pauseVoice();
|
||
console.log('onUnload')
|
||
talkVoiceObj.value?.destroy()
|
||
if(downloadFnc.value && downloadFnc.value.abord) downloadFnc.value?.abord()
|
||
downloadFnc.value = null
|
||
});
|
||
onHide(() => {
|
||
console.log('onHide')
|
||
voiceLoading.value = false;
|
||
if(downloadFnc.value && downloadFnc.value.abord) downloadFnc.value?.abord()
|
||
downloadFnc.value = null
|
||
});
|
||
let prevTime = -1;
|
||
let pauseTime = -1;
|
||
const startPlay = ref(false)
|
||
const voiceIsPlaying = ref(false)
|
||
talkVoiceObj.value.onEnded(() => {
|
||
console.log('onEnded');
|
||
voiceIsPlaying.value = false
|
||
if(props.activeVoiceId){
|
||
emit('changePlay', '');
|
||
}
|
||
prevTime = -1;
|
||
pauseTime = prevTime
|
||
talkVoiceObj.value.src = '';
|
||
});
|
||
talkVoiceObj.value.onStop(() => {
|
||
console.log('onStop');
|
||
voiceLoading.value = false;
|
||
voiceIsPlaying.value = false
|
||
if(props.activeVoiceId){
|
||
emit('changePlay', '');
|
||
}
|
||
prevTime = -1;
|
||
pauseTime = prevTime
|
||
talkVoiceObj.value.src = '';
|
||
});
|
||
talkVoiceObj.value.onPause(() => {
|
||
voiceIsPlaying.value = false
|
||
voiceLoading.value = false;
|
||
prevTime = talkVoiceObj.value.currentTime;
|
||
pauseTime = prevTime
|
||
if(downloadFnc.value && downloadFnc.value.abord) downloadFnc.value?.abord()
|
||
downloadFnc.value = null
|
||
});
|
||
talkVoiceObj.value.onPlay(() => {
|
||
console.log('onPlay');
|
||
prevTime = talkVoiceObj.value.currentTime;
|
||
pauseTime = prevTime
|
||
startPlay.value = true
|
||
voiceIsPlaying.value = true
|
||
setTimeout(()=> {
|
||
startPlay.value= false
|
||
},300)
|
||
emit('changePlay', unref(dataInfo)?.id);
|
||
});
|
||
|
||
talkVoiceObj.value.onTimeUpdate(() => {
|
||
if (activeVoiceId.value === unref(dataInfo)?.id) {
|
||
setTimeout(() => {
|
||
if (talkVoiceObj.value.currentTime !== prevTime) {
|
||
if (talkVoiceObj.value.currentTime > 0) {
|
||
prevTime = talkVoiceObj.value.currentTime;
|
||
}
|
||
} else {
|
||
if (props.isPlaying && (talkVoiceObj.value.currentTime > 0) && props.activeVoiceId && !startPlay.value) {
|
||
console.log(pauseTime , prevTime)
|
||
emit('changePlay', '');
|
||
}
|
||
}
|
||
}, 100);
|
||
}
|
||
});
|
||
|
||
talkVoiceObj.value.onError(() => {
|
||
// console.log('onError');
|
||
// if(!talkVoiceObj.value.src) return
|
||
// talkVoiceObj.value.src = '';
|
||
// emit('changePlay', '');
|
||
});
|
||
const pauseVoice = () => {
|
||
voiceLoading.value = false;
|
||
if (!talkVoiceObj.value) return;
|
||
talkVoiceObj.value.pause();
|
||
emit('changePlay', '');
|
||
};
|
||
// 重播
|
||
const restartPlay = () => {
|
||
if (itemVoice.value) {
|
||
emit('changePlay', '');
|
||
emit('changePlay', unref(dataInfo)?.id);
|
||
setTimeout(() => {
|
||
talkVoiceObj.value.src = itemVoice.value;
|
||
talkVoiceObj.value.seek(0);
|
||
talkVoiceObj.value.play();
|
||
}, 100);
|
||
} else {
|
||
playVoice();
|
||
}
|
||
};
|
||
const downloadFnc = ref(null)
|
||
const playVoice = (readType = 'pgth') => {
|
||
console.log('playVoice11111111')
|
||
if(voiceIsPlaying.value) return
|
||
let _content = showContent.value;
|
||
if (!_content) {
|
||
common.msg('没有可以播放的文字信息!');
|
||
return;
|
||
}
|
||
setTimeout(() => {
|
||
emit('changePlay', unref(dataInfo)?.id);
|
||
// readType 阅读内容类型pgrh段落/qns问题
|
||
if (talkVoiceObj.value.src && talkVoiceObj.value.src === itemVoice.value) {
|
||
if(voiceIsPlaying.value) return
|
||
talkVoiceObj.value.play();
|
||
return;
|
||
}
|
||
talkVoiceObj.value.src = '';
|
||
if (itemVoice.value) {
|
||
setTimeout(() => {
|
||
if(voiceIsPlaying.value) return
|
||
talkVoiceObj.value.src = itemVoice.value;
|
||
talkVoiceObj.value.play();
|
||
}, 100);
|
||
} else {
|
||
voiceLoading.value = true;
|
||
let _type =
|
||
props.type == 2 ?
|
||
'ans' :
|
||
props.type == 3 ?
|
||
'pgrh' :
|
||
props.type == 4 ?
|
||
'qns' :
|
||
props.type == 5 ?
|
||
'analysis' :
|
||
props.type == 8 ?
|
||
'stemAnalysis' :
|
||
'';
|
||
let _readId =
|
||
props.type == 2 ?
|
||
unref(dataInfo)?.intrctId :
|
||
props.type == 3 ?
|
||
unref(dataInfo)?.pgrphId :
|
||
props.type == 4 ?
|
||
unref(dataInfo)?.qnsId :
|
||
props.type == 5 ?
|
||
unref(dataInfo)?.qnsId :
|
||
props.type == 8 ?
|
||
unref(dataInfo)?.qnsId :
|
||
'';
|
||
let _params = {
|
||
crsId: unref(dataInfo)?.crsId || '',
|
||
pgrphId: unref(dataInfo)?.pgrphId || '',
|
||
qnsId: unref(dataInfo)?.qnsId || '',
|
||
readType: _type, // 2 文本回答 3, 段落信息 4 问题信息
|
||
teachNo: unref(teacherInfo)?.teacherNo, // 必填
|
||
readId: _readId
|
||
// content: _content
|
||
};
|
||
|
||
let _url =
|
||
'/trastudy/traStdyInfo/readContent?' +
|
||
Object.keys(_params)
|
||
.map((t) => {
|
||
return encodeURIComponent(t) + '=' + encodeURIComponent(_params[t]);
|
||
})
|
||
.join('&');
|
||
|
||
storageStore.getStorageById('audio', _url + _params.teachNo, (url) => {
|
||
if (url) {
|
||
voiceLoading.value = false;
|
||
itemVoice.value = url;
|
||
if(voiceIsPlaying.value) return
|
||
talkVoiceObj.value.src = url;
|
||
talkVoiceObj.value.play();
|
||
} else {
|
||
storageStore.setGettingVoiceId(unref(dataInfo)?.id);
|
||
let _paramsObj = downloadVoiceParams(_url)
|
||
downloadFnc.value = uni.downloadFile({
|
||
..._paramsObj,
|
||
success(res) {
|
||
if (res?.data) {
|
||
common.msg(res.data.message);
|
||
return;
|
||
}
|
||
voiceLoading.value = false;
|
||
itemVoice.value = res.tempFilePath;
|
||
if(voiceIsPlaying.value) return
|
||
talkVoiceObj.value.src = res.tempFilePath;
|
||
// 判斷是否在當前頁面
|
||
if(unref(isGettingId) === unref(dataInfo)?.id){
|
||
talkVoiceObj.value.play();
|
||
storageStore.setStorage('audio', _url + _params.teachNo, itemVoice.value);
|
||
}
|
||
},
|
||
complete() {
|
||
voiceLoading.value = false;
|
||
downloadFnc.value = null
|
||
},
|
||
});
|
||
// downloadVoiceFile(_url)
|
||
// .then((res) => {
|
||
// if (res?.data) {
|
||
// common.msg(res.data.message);
|
||
// return;
|
||
// }
|
||
// voiceLoading.value = false;
|
||
// if()
|
||
// itemVoice.value = res.tempFilePath;
|
||
// talkVoiceObj.value.src = res.tempFilePath;
|
||
// // 判斷是否在當前頁面
|
||
// if(unref(isGettingId) === unref(dataInfo)?.id){
|
||
// talkVoiceObj.value.play();
|
||
// storageStore.setStorage('audio', _url + _params.teachNo, itemVoice.value);
|
||
// }
|
||
// })
|
||
// .catch((err) => {
|
||
// voiceLoading.value = false;
|
||
// });
|
||
}
|
||
});
|
||
|
||
}
|
||
}, 100);
|
||
};
|
||
const saveFile = (buffer, filePath, cb = () => {}) => {
|
||
let fileManager = uni.getFileSystemManager();
|
||
fileManager.writeFile({
|
||
filePath: filePath,
|
||
data: buffer,
|
||
encoding: 'binary',
|
||
success: cb,
|
||
file: () => {
|
||
voiceLoading.value = false;
|
||
}
|
||
});
|
||
};
|
||
const initVoice = (readType = 'qns', item, isplay = false) => {
|
||
if (unref(dataInfo).talkingVoice) {
|
||
itemVoice.value = unref(dataInfo).talkingVoice;
|
||
talkVoiceObj.value.src = itemVoice.value;
|
||
setTimeout(() => {
|
||
setVoiceTime(0);
|
||
}, 100);
|
||
}
|
||
// else{
|
||
// setTimeout(()=>{
|
||
// initVoice(readType, item, isplay)
|
||
// },200)
|
||
// }
|
||
};
|
||
const setVoiceTime = (sum) => {
|
||
plus.io.getAudioInfo({
|
||
filePath: unref(dataInfo).talkingVoice,
|
||
success:(res)=>{
|
||
voiceTime.value = (Math.floor(res.duration) || 1) + 's'
|
||
},
|
||
fail:(res)=>{
|
||
voiceTime.value = (unref(dataInfo).voiceTime || 1) + 's'
|
||
}
|
||
})
|
||
}
|
||
// const setVoiceTime = (sum) => {
|
||
// sum += 1;
|
||
// if (talkVoiceObj.value.duration) {
|
||
// voiceTime.value = (Math.ceil(talkVoiceObj.value.duration) || 0) + 's';
|
||
// } else {
|
||
// if (sum === 100) {
|
||
// voiceTime.value = (unref(dataInfo).voiceTime || 0) + 's';
|
||
// return;
|
||
// }
|
||
// setTimeout(() => {
|
||
// setVoiceTime(sum);
|
||
// }, 300);
|
||
// }
|
||
// };
|
||
const playQnsVoice = (readType = 'qns', item) => {
|
||
if (type.value === 2) {
|
||
// 文本转语音
|
||
playVoice(readType);
|
||
return;
|
||
}
|
||
// readType 阅读内容类型pgrh段落/qns问题
|
||
emit('changePlay', unref(dataInfo)?.id);
|
||
setTimeout(() => {
|
||
itemVoice.value = item.talkingVoice;
|
||
talkVoiceObj.value.src = item.talkingVoice;
|
||
talkVoiceObj.value.play();
|
||
}, 100);
|
||
};
|
||
const translateLoading = ref(false);
|
||
// 翻译
|
||
const translateVoice = (item) => {
|
||
if (!item.talkingVoice) return;
|
||
translateLoading.value = true;
|
||
uploadVoiceFile(item.talkingVoice, {})
|
||
.then((res) => {
|
||
let _data = JSON.parse(res.data);
|
||
item.talkingContent = _data.body.text;
|
||
translateLoading.value = false;
|
||
})
|
||
.catch(() => {
|
||
translateLoading.value = false;
|
||
});
|
||
};
|
||
const showNextBtn = ref(true);
|
||
const showGoonBtn = computed(() => {
|
||
return showNextBtn.value && [3, 6].indexOf(props.type) >= 0 && isLast.value; // && !isPlaying.value
|
||
});
|
||
const isClick = ref(false);
|
||
const goOnGetQues = () => {
|
||
if (isClick.value) return;
|
||
clickNow();
|
||
if ([5, 8].indexOf(props.type) >= 0) {
|
||
// 完成问题
|
||
if(dataInfo.value.stdyStat === 'Y' && dataInfo.value.isLastQns === 'Y'){
|
||
emit('nextQuestion', unref(dataInfo), 'overNextGraph');
|
||
}else{
|
||
emit('nextQuestion', unref(dataInfo));
|
||
}
|
||
} else if ([3].indexOf(props.type) >= 0) {
|
||
// 完成段落
|
||
if(unref(dataInfo).hasQns !=='Y'){
|
||
emit('nextQuestion', unref(dataInfo), 'overNextGraph');
|
||
}else{
|
||
emit('nextQuestion', unref(dataInfo), 'overGraph');
|
||
}
|
||
} else if ([9].indexOf(props.type) >= 0) {
|
||
// 下一段落
|
||
emit('nextQuestion', unref(dataInfo), 'nextGraph');
|
||
}
|
||
};
|
||
const clickNow = () => {
|
||
isClick.value = true;
|
||
setTimeout(() => {
|
||
isClick.value = false;
|
||
}, 800);
|
||
};
|
||
const goOnRestart = () => {
|
||
if (isClick.value) return;
|
||
clickNow();
|
||
emit('nextQuestion', unref(dataInfo), 'reStart');
|
||
};
|
||
const goOnRestartPrgh = () => {
|
||
if (isClick.value) return;
|
||
clickNow();
|
||
emit('nextQuestion', unref(dataInfo), 'reStartPrgh');
|
||
};
|
||
// 撤回
|
||
const withdraw = () => {
|
||
if (isClick.value) return;
|
||
clickNow();
|
||
emit('withdraw', unref(dataInfo));
|
||
};
|
||
// 完成
|
||
const finishQuestion = (data) => {
|
||
if (isClick.value) return;
|
||
clickNow();
|
||
emit('finishQuestion', data);
|
||
};
|
||
// 答题 选择
|
||
const subject_click = (type, data) => {
|
||
if (['answer', 'choose'].includes(type)) {
|
||
emit('answerQuestion', {
|
||
...data,
|
||
...unref(dataInfo)
|
||
});
|
||
}
|
||
};
|
||
</script>
|
||
|
||
<style lang="scss">
|
||
.ai-answer {
|
||
width: 100%;
|
||
overflow: hidden;
|
||
margin-bottom: 20rpx;
|
||
|
||
.user-info {
|
||
overflow: hidden;
|
||
|
||
.avatar-box {
|
||
float: left;
|
||
width: 76rpx;
|
||
height: 76rpx;
|
||
border: 3rpx solid #fff;
|
||
background-color: #f1f1f1;
|
||
overflow: hidden;
|
||
border-radius: 10rpx;
|
||
margin-right: 16rpx;
|
||
margin-bottom: 16rpx;
|
||
|
||
.img-box {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
}
|
||
|
||
.ai-name {
|
||
float: left;
|
||
color: #666;
|
||
font-size: 25rpx;
|
||
line-height: 35rpx;
|
||
|
||
.ai-name-desc {
|
||
color: #999;
|
||
margin-left: 16rpx;
|
||
display: inline-block;
|
||
}
|
||
}
|
||
}
|
||
|
||
.talking-info {
|
||
padding: 20rpx;
|
||
border-radius: 15rpx;
|
||
background-color: #fff;
|
||
overflow: hidden;
|
||
padding-bottom: 30rpx;
|
||
width: 100%;
|
||
|
||
&.talking-info-less {
|
||
float: left;
|
||
min-width: 400rpx;
|
||
padding-bottom: 20rpx;
|
||
}
|
||
|
||
.talking-gif {
|
||
width: 200rpx;
|
||
height: 38rpx;
|
||
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 {
|
||
margin: 16rpx 0;
|
||
overflow: hidden;
|
||
min-height: 48rpx;
|
||
|
||
&.last-cont {
|
||
margin-bottom: 0;
|
||
|
||
.talking-text {
|
||
margin-bottom: 0;
|
||
}
|
||
}
|
||
|
||
.type-tag {
|
||
width: 108rpx;
|
||
height: 46rpx;
|
||
background: #267eff;
|
||
border-radius: 8rpx;
|
||
font-family: PingFangSC;
|
||
font-weight: 500;
|
||
font-size: 25rpx;
|
||
color: #ffffff;
|
||
text-align: center;
|
||
line-height: 46rpx;
|
||
margin-bottom: 10rpx;
|
||
}
|
||
|
||
.talking-text {
|
||
font-size: 28rpx;
|
||
line-height: 48rpx;
|
||
min-height: 48rpx;
|
||
margin-bottom: 20rpx;
|
||
word-break: break-all;
|
||
white-space: pre-line;
|
||
|
||
&.loading {
|
||
// min-height: 36px;
|
||
}
|
||
}
|
||
|
||
.file-items{
|
||
float: left;
|
||
width: 100%;
|
||
padding-top: 12rpx;
|
||
.file-item {
|
||
line-height: 36rpx;
|
||
font-size: 28rpx;
|
||
margin-bottom: 8rpx;
|
||
color: #06f;
|
||
}
|
||
}
|
||
|
||
.cont-img-box {
|
||
float: left;
|
||
width: calc(33% - 13.34rpx);
|
||
height: 130rpx;
|
||
border-radius: 15rpx;
|
||
overflow: hidden;
|
||
background-color: #fafafa;
|
||
margin-bottom: 20rpx;
|
||
margin-right: 20rpx;
|
||
|
||
&.cont-video-box{
|
||
width: 433rpx;
|
||
height: 269rpx;
|
||
margin-right: 0rpx;
|
||
}
|
||
&:nth-child(3n){
|
||
margin-right: 0;
|
||
}
|
||
&.cont-img-box2{
|
||
width: calc(50% - 10rpx);
|
||
height: 200rpx;
|
||
&:nth-child(2n){
|
||
margin-right: 0rpx;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.btns-cont {
|
||
overflow: hidden;
|
||
border-top: 3rpx solid #eee;
|
||
|
||
.replay-btn {
|
||
float: left;
|
||
width: 46rpx;
|
||
height: 46rpx;
|
||
overflow: hidden;
|
||
margin-right: 16rpx;
|
||
margin-top: 25rpx;
|
||
}
|
||
|
||
.play-btn {
|
||
float: left;
|
||
width: 46rpx;
|
||
height: 46rpx;
|
||
overflow: hidden;
|
||
margin-right: 16rpx;
|
||
margin-top: 25rpx;
|
||
}
|
||
|
||
.next-learn-btn {
|
||
float: left;
|
||
padding: 0 60rpx 0 15rpx;
|
||
height: 62rpx;
|
||
line-height: 62rpx;
|
||
font-size: 28rpx;
|
||
background-color: #06f;
|
||
color: #fff;
|
||
border-radius: 8rpx;
|
||
margin-top: 16rpx;
|
||
margin-right: 16rpx;
|
||
position: relative;
|
||
|
||
&.replay-study-btn {
|
||
float: right;
|
||
margin-right: 0;
|
||
}
|
||
|
||
&.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;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.user-answer {
|
||
overflow: hidden;
|
||
width: 100%;
|
||
margin-bottom: 20rpx;
|
||
|
||
.user-info {
|
||
overflow: hidden;
|
||
|
||
.avatar-box {
|
||
float: right;
|
||
width: 76rpx;
|
||
height: 76rpx;
|
||
border: 3rpx solid #fff;
|
||
background-color: #f1f1f1;
|
||
overflow: hidden;
|
||
border-radius: 10rpx;
|
||
margin-left: 16rpx;
|
||
margin-bottom: 16rpx;
|
||
|
||
.img-box {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
}
|
||
|
||
.ai-name {
|
||
float: right;
|
||
color: #666;
|
||
font-size: 25rpx;
|
||
line-height: 35rpx;
|
||
}
|
||
}
|
||
|
||
.talking-info-less {
|
||
padding: 20rpx;
|
||
float: right;
|
||
border-radius: 15rpx;
|
||
background-color: #06f;
|
||
color: #fff;
|
||
}
|
||
|
||
.talking-info {
|
||
padding: 20rpx;
|
||
border-radius: 15rpx;
|
||
background-color: #06f;
|
||
padding-bottom: 30rpx;
|
||
position: relative;
|
||
color: #fff;
|
||
|
||
.talking-gif {
|
||
width: 200rpx;
|
||
height: 38rpx;
|
||
background: url(@/static/images/course/voice-gray.png) no-repeat;
|
||
background-size: contain;
|
||
padding-left: 168rpx;
|
||
|
||
&.is-play {
|
||
background: url(@/static/images/course/voice-white.gif) no-repeat;
|
||
background-size: contain;
|
||
}
|
||
}
|
||
|
||
.right-time {
|
||
position: absolute;
|
||
right: 20rpx;
|
||
top: 20rpx;
|
||
font-size: 28rpx;
|
||
height: 38rpx;
|
||
line-height: 38rpx;
|
||
}
|
||
|
||
.talking-cont {
|
||
margin-top: 16rpx;
|
||
overflow: hidden;
|
||
|
||
.talking-text {
|
||
font-size: 28rpx;
|
||
line-height: 48rpx;
|
||
margin-bottom: 20rpx;
|
||
word-break: break-all;
|
||
white-space: pre-line;
|
||
overflow: hidden;
|
||
|
||
&.translate-text {
|
||
min-height: 48rpx;
|
||
}
|
||
}
|
||
}
|
||
|
||
.btns-cont {
|
||
overflow: hidden;
|
||
border-top: 3rpx solid #eee;
|
||
|
||
.replay-btn {
|
||
float: left;
|
||
width: 46rpx;
|
||
height: 46rpx;
|
||
overflow: hidden;
|
||
margin-right: 16rpx;
|
||
margin-top: 25rpx;
|
||
}
|
||
|
||
.play-btn {
|
||
float: left;
|
||
width: 46rpx;
|
||
height: 46rpx;
|
||
overflow: hidden;
|
||
margin-right: 16rpx;
|
||
margin-top: 25rpx;
|
||
}
|
||
|
||
.text-btn {
|
||
float: left;
|
||
width: 46rpx;
|
||
height: 46rpx;
|
||
overflow: hidden;
|
||
line-height: 46rpx;
|
||
text-align: center;
|
||
color: #fff;
|
||
margin-right: 16rpx;
|
||
margin-top: 25rpx;
|
||
background-color: #67a4ff;
|
||
font-size: 24rpx;
|
||
font-weight: bold;
|
||
border-radius: 20rpx;
|
||
}
|
||
|
||
.text-right-btn {
|
||
float: right;
|
||
padding: 0 10rpx;
|
||
height: 46rpx;
|
||
overflow: hidden;
|
||
line-height: 46rpx;
|
||
text-align: center;
|
||
margin-left: 16rpx;
|
||
margin-top: 25rpx;
|
||
}
|
||
|
||
.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;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</style> |