Files
tra-app/src/pages/course/components/talkingItem.vue
T

1280 lines
33 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!-- 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>