1302 lines
33 KiB
Vue
1302 lines
33 KiB
Vue
<template>
|
||
<view class="course-study max_page">
|
||
<view class="top-bg"></view>
|
||
<view class="bot-bg"></view>
|
||
<view class="course-study-body" v-show="step === 1">
|
||
<view class="seek-setting">
|
||
<view class="inline-block" @click="showChangeSeekModelNow">{{chooseRate}}倍</view>
|
||
<!-- <image src="@/static/images/course/setting.png" alt="" class="image-icon" @click.stop="showChangeBgModelNow"></image> -->
|
||
</view>
|
||
<view class="body-title">
|
||
<uv-icon class="arrow-icon" name="arrow-left" @click="backRouter()"/>
|
||
语言选择,开启学习之旅
|
||
</view>
|
||
<view class="voice-list">
|
||
<uv-radio-group v-model="choiceTeacher">
|
||
<view class="voice-item" v-for="item in teacherList" :key="item.teacherNo" @click="choiceTeacher = item.teacherNo">
|
||
<view class="avatar-box">
|
||
<ImagePreview class="img-box" :src="item.imgAddr" :isCache="true" :width="120" :height="160"></ImagePreview>
|
||
</view>
|
||
<view class="right-cont">
|
||
<view class="right-name">{{item.teacherName}}</view>
|
||
<view class="right-desc">描述:{{item.voiceDesc || '--'}}</view>
|
||
<view class="audio-box">
|
||
<view :class="['talking-gif', activeVoiceTeachNo === item.teacherNo && !loadingTeacherVoice ? 'is-play':'']"></view>
|
||
<CommonLoading color="#06f" v-show="activeVoiceTeachNo === item.teacherNo && loadingTeacherVoice" style="float: left;"/>
|
||
<view class="play-btn" @click="playTeacherVoice(item)">
|
||
<image class="icon" v-if="activeVoiceTeachNo !== item.teacherNo" src="@/static/images/course/play-blue.png" style="width: 100%;height: 100%;"></image>
|
||
<image class="icon" v-else src="@/static/images/course/stop-blue.png" style="width: 100%;height: 100%;"></image>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<uv-radio :name="item.teacherNo" class="radio-box" @click.stop/>
|
||
</view>
|
||
</uv-radio-group>
|
||
<view class="start-btns" @click="nextStep">
|
||
开启学习之旅
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="course-study-body" v-show="step === 2">
|
||
<view class="seek-setting">
|
||
<view class="inline-block" @click="showChangeSeekModelNow">{{chooseRate}}倍</view>
|
||
<image src="@/static/images/course/setting.png" alt="" class="image-icon" @click.stop="showChangeBgModelNow"></image>
|
||
</view>
|
||
<view class="body-title" @click="scrollToBottomNow">
|
||
<uv-icon class="arrow-icon" name="arrow-left" @click="backRouter()"/>
|
||
课程学习
|
||
</view>
|
||
<view class="study-num">
|
||
<view class="color-blue">{{ showOrder }}</view> / {{ pgrphNum }}
|
||
</view>
|
||
<scroll-view
|
||
class="talking-list"
|
||
:scroll-y="true"
|
||
:scroll-top="scrollTop"
|
||
:show-scrollbar="false"
|
||
:scroll-with-animation="true"
|
||
>
|
||
<!-- <view class="talking-list-cont" id="scroll-box" ref="scrollBoxRef"> -->
|
||
<TalkingItem
|
||
class="talking-item"
|
||
v-for="item,index in talkingList"
|
||
:key="'item' + item.id"
|
||
:type="item.type"
|
||
:dataInfo="item"
|
||
:activeVoiceId="activeVoiceTalkingItemId"
|
||
:isPlaying="activeVoiceTalkingItemId === item.id"
|
||
:voiceRate="chooseRate"
|
||
:lastQuestionInfo="lastTalkingInfo"
|
||
:teacherInfo="choiceTeacherInfo"
|
||
@changePlay="changePlayItemId"
|
||
@nextQuestion="nextQuestionSuccess"
|
||
@withdraw="withdrawNow"
|
||
@finishQuestion="finishQuestionNow"
|
||
:id="'item' + item.id"
|
||
:isLast="talkingList.length === (index+1)"
|
||
></TalkingItem>
|
||
<!-- <video v-if="videoUrlShow" :src="videoUrlShow" controls></video> -->
|
||
<!-- </view> -->
|
||
</scroll-view>
|
||
<TouchBtn
|
||
class="talk-btns"
|
||
@uploadVoice="uploadRecordNow"
|
||
@submitAnswer="submitAnswerNow"
|
||
@startRecord="changePlayItemId('')"
|
||
:isLoading="isUploadingVoice"
|
||
:onlyVoice="answerOnlyVoice"
|
||
:canAnswer="testAnswer"
|
||
:canAnswerText="'每个问题最多连续发送五次答案!'"
|
||
:isDisabled="((lastTalkingInfo.type||0) !== 4)"
|
||
/>
|
||
</view>
|
||
<uv-overlay :show="showSeekModel" opacity=".6" @click="showSeekModel = false">
|
||
<view class="overlay-box">
|
||
<view class="white-bg-box">
|
||
<view class="box-title">倍速
|
||
</view>
|
||
<view class="box-btns">
|
||
<view
|
||
v-for="item in rateList"
|
||
:class="{'is-active':item === chooseRate}"
|
||
@click="chooseRateNow(item)"
|
||
>{{item}}</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</uv-overlay>
|
||
<uv-overlay :show="showBgModel" opacity=".6" @click="showBgModel = false">
|
||
<view class="overlay-box">
|
||
<view class="white-bg-box-setting white-bg-box">
|
||
<view class="box-title">设置
|
||
</view>
|
||
<view class="box-items" @click.stop>
|
||
<view class="box-item">
|
||
<view class="box-item-title">音色</view>
|
||
<uv-radio-group v-model="choiceTeacher" class="box-item-cont">
|
||
<view class="overlay-voice-item" v-for="item in teacherList" :key="item.teacherNo" @click="changeTeacherVoiceNow(item)">
|
||
<view class="overlay-avatar-box">
|
||
<ImagePreview class="overlay-img-box" :src="item.imgAddr" :isCache="true" :width="120" :height="160"></ImagePreview>
|
||
</view>
|
||
<view class="overlay-teacher-name">{{item.teacherName}}</view>
|
||
<uv-radio :name="item.teacherNo" class="overlay-radio-box" @click.stop/>
|
||
</view>
|
||
</uv-radio-group>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</uv-overlay>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { reactive, ref, computed, unref, onMounted, onUnmounted, getCurrentInstance, nextTick } from 'vue';
|
||
import { onLoad,onShow, onUnload,onHide } from '@dcloudio/uni-app';
|
||
import {
|
||
previewFile,
|
||
downloadFile,
|
||
commonUploadVoiceFile,
|
||
downloadVoiceFile,
|
||
uploadVoiceFile,
|
||
} from "@/api/common.js"
|
||
import { getToken } from '@/common/common.js'
|
||
import { get_base_url, goto_login_fun } from '@/api/request'
|
||
import {
|
||
getCourseStudyInfoApi,
|
||
afreshStudyCourseApi,
|
||
queryQuestionEvalateApi,
|
||
textChartApi,
|
||
setStudyTeacherApi
|
||
} from "@/api/study.js"
|
||
import common from '@/common/common';
|
||
import TalkingItem from '@/pages/course/components/talkingItem.vue'
|
||
import ImagePreview from '@/components/image-preview/image-preview.vue'
|
||
import TouchBtn from '@/pages/course/components/touchBtn.vue'
|
||
|
||
import { useSocketStore } from "@/store/socket.js"
|
||
import { useStorageStore } from "@/store/storage.js"
|
||
import { storeToRefs } from 'pinia'
|
||
|
||
const socketStore = useSocketStore()
|
||
const storageStore = useStorageStore()
|
||
const { SocketObj } = storeToRefs(socketStore)
|
||
const audioCacheObj = computed(() => storageStore.audioObj)
|
||
|
||
const { proxy } = getCurrentInstance()
|
||
const crsNum = ref('');
|
||
const crsId = ref('');
|
||
const step = ref(0)
|
||
const teacherList = ref([])
|
||
const choiceTeacher = ref('')
|
||
const videoUrlShow = ref('')
|
||
const talkingList = ref([])
|
||
const activeVoiceTalkingItemId = ref('')
|
||
const answerValue = ref('')
|
||
const inputRef = ref('')
|
||
const flagVal = ref('1')
|
||
const isUploadingVoice = ref(false)
|
||
// 初始化数据
|
||
const getData = async (flag) => {
|
||
try{
|
||
const { body } = await getCourseStudyInfoApi({crsId: crsId.value})
|
||
teacherList.value = body.teacheList.map(t=>({
|
||
...t,
|
||
voicePath: '',
|
||
voiceObj: null
|
||
}))
|
||
if(body.stdyProgressFlag === 'N'){
|
||
step.value = 1
|
||
}else{
|
||
choiceTeacher.value = body.choiceTeacher
|
||
if(startPgrphId.value){
|
||
sendMessage({
|
||
"commond" : 'STDY',
|
||
"body" : {
|
||
crsId: crsId.value,
|
||
teacherNo: choiceTeacher.value
|
||
}
|
||
})
|
||
return
|
||
}
|
||
common.show('提示', '系统检测到您已存在学习记录,要继续学习吗?',true, '重新学习','继续学习').then((res) => {
|
||
if(res) {
|
||
// 继续学习 , 获取学习历史接口
|
||
// 滚动到最下面
|
||
scrollToBottomNow()
|
||
sendMessage({
|
||
"commond" : 'STDY',
|
||
"body" : {
|
||
crsId: crsId.value,
|
||
teacherNo: choiceTeacher.value
|
||
}
|
||
})
|
||
step.value = 2
|
||
}else{
|
||
// 重新学习
|
||
afreshStudyCourseApi({
|
||
crsId: crsId.value,
|
||
}).then(res=>{
|
||
step.value = 1
|
||
teacherList.value = body.teacheList.map(t=>({
|
||
...t,
|
||
voicePath: '',
|
||
voiceObj: null
|
||
}))
|
||
}).catch(t=>{
|
||
step.value = 1
|
||
teacherList.value = body.teacheList.map(t=>({
|
||
...t,
|
||
voicePath: '',
|
||
voiceObj: null
|
||
}))
|
||
})
|
||
}
|
||
}).finally(() => {})
|
||
}
|
||
}catch{
|
||
|
||
}finally{
|
||
watchFlag.value +=1
|
||
}
|
||
};
|
||
|
||
const showChangeSeekModelNow = () => {
|
||
// changePlayItemId()
|
||
showSeekModel.value = true
|
||
}
|
||
const showBgModel = ref(false)
|
||
const showChangeBgModelNow = () => {
|
||
changePlayItemId()
|
||
showBgModel.value = true
|
||
}
|
||
// 关闭播放状态
|
||
const changePlayItemId = (id = '') => {
|
||
activeVoiceTalkingItemId.value = id
|
||
// if(activeVoiceTalkingItemId.value === id){
|
||
// activeVoiceTalkingItemId.value = ''
|
||
// }else{
|
||
// activeVoiceTalkingItemId.value = id
|
||
// }
|
||
}
|
||
// 选择的教师对象信息
|
||
const choiceTeacherInfo = computed(()=>{
|
||
return teacherList.value.find(t=> t.teacherNo === choiceTeacher.value)
|
||
})
|
||
|
||
const changeTeacherVoiceNow = async (item) => {
|
||
if(item.teacherNo === choiceTeacher.value) return
|
||
try{
|
||
const res = await setStudyTeacherApi({
|
||
stdyId: stdyId.value,
|
||
teacherNo: item.teacherNo
|
||
})
|
||
console.log(res)
|
||
if(res.rtnCode === '0000'){
|
||
choiceTeacher.value = item.teacherNo
|
||
showBgModel.value = false
|
||
}
|
||
}catch{} finally{}
|
||
}
|
||
|
||
// 选择教师语音对象
|
||
const activeVoiceTeachNo = ref('')
|
||
let teacherVoiceObj = uni.createInnerAudioContext()
|
||
|
||
teacherVoiceObj.onEnded(()=>{
|
||
activeVoiceTeachNo.value = ''
|
||
teacherVoiceObj.src = ''
|
||
})
|
||
const loadingTeacherVoice = ref(false)
|
||
const playTeacherVoice = (item) =>{
|
||
teacherVoiceObj.stop()
|
||
teacherVoiceObj.src = ''
|
||
let _isStop = item.teacherNo === activeVoiceTeachNo.value
|
||
activeVoiceTeachNo.value = _isStop ? '' : item.teacherNo
|
||
if(_isStop) return
|
||
if(item.voicePath){
|
||
teacherVoiceObj.src = item.voicePath
|
||
teacherVoiceObj.play()
|
||
}else{
|
||
loadingTeacherVoice.value = true
|
||
storageStore.getStorageById('audio', item.teacherNo, (url)=>{
|
||
if(url){
|
||
teacherVoiceObj.src = url
|
||
teacherVoiceObj.play()
|
||
loadingTeacherVoice.value = false
|
||
}else{
|
||
downloadVoiceFile('/trastudy/traStdyInfo/trialListening?teachNo='+item.teacherNo).then(res=>{
|
||
loadingTeacherVoice.value = false
|
||
item.voicePath = res.tempFilePath
|
||
teacherVoiceObj.src = item.voicePath
|
||
teacherVoiceObj.play()
|
||
storageStore.setStorage('audio', item.teacherNo, item.voicePath)
|
||
})
|
||
}
|
||
})
|
||
}
|
||
}
|
||
var socketTask = null
|
||
const nextStep = () => {
|
||
if(choiceTeacher.value){
|
||
teacherVoiceObj.destroy()
|
||
sendMessage({
|
||
"commond" : 'STDY',
|
||
"body" : {
|
||
crsId: crsId.value,
|
||
teacherNo: choiceTeacher.value
|
||
}
|
||
})
|
||
step.value = 2
|
||
}else{
|
||
uni.showToast({
|
||
title: '请选择一位教师!',
|
||
icon: "none",
|
||
duration:1000
|
||
});
|
||
}
|
||
}
|
||
|
||
const pgrphNum = ref(1)
|
||
const showOrder = ref(0)
|
||
const isLastPgrph = ref(false)
|
||
const backRouter = ()=>{
|
||
// common.redirectTo(`/pages/courseDetail/index?crsId=${crsId.value}&crsNum=${crsNum.value}&from=study`)
|
||
common.navigateBack()
|
||
}
|
||
|
||
const answerOnlyVoice = computed(()=>{
|
||
return lastTalkingInfo.value.answerWay === 'V'
|
||
})
|
||
|
||
// 获取段落
|
||
const getGraphInfoUnStudy = (flag = 0) => {
|
||
console.log(isLastPgrph.value)
|
||
let _id = new Date().getTime() + 'id'
|
||
let _data = {
|
||
crsId: crsId.value,
|
||
stdyId: unref(stdyId),
|
||
stdyBatch: unref(stdyBatch)
|
||
}
|
||
if(flag === 1 && !!startPgrphId.value){
|
||
_data.pgrphId = startPgrphId.value
|
||
}
|
||
textChartApi({
|
||
processType: 'PRGH-STDY',
|
||
requestBody: JSON.stringify(_data)
|
||
}).then(res=>{
|
||
let _body = res.body|| {}
|
||
if(!_body.chatBody) {
|
||
return
|
||
}
|
||
if(Number(_body.bodyTyp) === 7){
|
||
common.show('恭喜你已学完全部知识点', '有什么心得跟我们分享吗?',true, '取消','去评论').then((res) => {
|
||
if(res) {
|
||
// 去评论
|
||
common.redirectTo(`/pages/courseDetail/submit?crsId=${crsId.value}&crsNum=${crsNum.value}&from=study&imgId=${imageId.value}`)
|
||
}else{
|
||
// 取消
|
||
common.redirectTo(`/pages/courseDetail/index?crsId=${crsId.value}&crsNum=${crsNum.value}&from=study`)
|
||
}
|
||
}).finally(() => {})
|
||
return
|
||
}
|
||
lastTalkingInfo.value = {
|
||
crsId: crsId.value,
|
||
qstLogId: _body.chatBody,
|
||
pgrphId: _body.pgrphId,
|
||
logId:_body.chatBody.logId || '',
|
||
chatContent: _body.chatContent,
|
||
userInfo: {...choiceTeacherInfo.value},
|
||
..._body.chatBody,
|
||
type: Number(_body.bodyTyp) || 0,
|
||
id: _id
|
||
}
|
||
isLastPgrph.value = _body.chatBody.haveNext !== 'Y'
|
||
talkingList.value.push(lastTalkingInfo.value)
|
||
pgrphNum.value = _body.chatBody.pgrphNum
|
||
showOrder.value = _body.chatBody.showOrder
|
||
logId.value = _body.chatBody.logId || ''
|
||
scrollToBottomNow()
|
||
})
|
||
// common.msg('您已学完当前课程的全部内容!')
|
||
}
|
||
const lastTalkingInfo = ref({})
|
||
const qstLogId = ref('')
|
||
// 获取问题
|
||
const getQuestionInfoUnStudy = (pgrphId) => {
|
||
textChartApi({
|
||
processType: 'QSN-ANSER',
|
||
requestBody: JSON.stringify({
|
||
crsId: crsId.value,
|
||
stdyId: unref(stdyId),
|
||
stdyBatch: unref(stdyBatch),
|
||
logId: unref(logId),
|
||
pgrphId
|
||
})
|
||
})
|
||
.then(res=>{
|
||
let _id = new Date().getTime() + 'id'
|
||
let _body = res.body || {}
|
||
console.log(_body)
|
||
if(Number(_body.bodyTyp) === 7){
|
||
common.show('恭喜你已学完全部知识点', '有什么心得跟我们分享吗?',true, '取消','去评论').then((res) => {
|
||
if(res) {
|
||
// 去评论
|
||
common.redirectTo(`/pages/courseDetail/submit?crsId=${crsId.value}&crsNum=${crsNum.value}&from=study&imgId=${imageId.value}`)
|
||
}else{
|
||
// 取消
|
||
common.redirectTo(`/pages/courseDetail/index?crsId=${crsId.value}&crsNum=${crsNum.value}&from=study`)
|
||
}
|
||
}).finally(() => {})
|
||
return
|
||
}
|
||
if(_body.chatBody && _body.chatBody?.stdyStat !== 'N'){
|
||
let _id = new Date().getTime() + 'id'
|
||
lastTalkingInfo.value = {
|
||
crsId: crsId.value,
|
||
stdyId: unref(stdyId),
|
||
stdyBatch: unref(stdyBatch),
|
||
logId: unref(logId),
|
||
pgrphId,
|
||
chatContent: _body.chatContent||'',
|
||
userInfo: {...choiceTeacherInfo.value},
|
||
...(_body.chatBody||{}),
|
||
type: 4,
|
||
userInfo: {...choiceTeacherInfo.value},
|
||
id: _id,
|
||
isQuestion: true
|
||
}
|
||
talkingList.value.push(JSON.parse(JSON.stringify(lastTalkingInfo.value)))
|
||
qstLogId.value = _body.chatBody?.qstLogId
|
||
scrollToBottomNow()
|
||
}else{
|
||
getGraphInfoUnStudy()
|
||
}
|
||
})
|
||
}
|
||
|
||
// 继续 执行方法
|
||
const nextQuestionSuccess = (info, type='next') => {
|
||
let _id = new Date().getTime() + 'id'
|
||
if(type === 'next'){
|
||
talkingList.value.push({
|
||
crsId: info.crsId,
|
||
pgrphId: info.pgrphId,
|
||
talkingContent: '继续',
|
||
type: 0,
|
||
id: _id
|
||
})
|
||
scrollToBottomNow(0)
|
||
getQuestionInfoUnStudy(info.pgrphId)
|
||
}else if( type === 'overGraph'){
|
||
textChartApi({
|
||
processType: 'PRGH-OVER',
|
||
requestBody: JSON.stringify({
|
||
pgrphId: info?.pgrphId,
|
||
logId: info?.logId,
|
||
})
|
||
}).then(res=>{
|
||
talkingList.value.push({
|
||
crsId: info.crsId,
|
||
pgrphId: info.pgrphId,
|
||
talkingContent: '继续',
|
||
type: 0,
|
||
id: _id
|
||
})
|
||
scrollToBottomNow(0)
|
||
getQuestionInfoUnStudy(info.pgrphId)
|
||
})
|
||
}else if(type === 'reStart'){
|
||
talkingList.value.push({
|
||
crsId: info.crsId,
|
||
pgrphId: info.pgrphId,
|
||
talkingContent: '重新答',
|
||
type: 0,
|
||
id: _id
|
||
})
|
||
// 重新回答
|
||
textChartApi({
|
||
processType: 'NEW-ANSER',//NEW-KNOW
|
||
requestBody: info.qstLogId
|
||
}).then(res=>{
|
||
let _body = res.body|| {}
|
||
if(!_body.chatBody) {
|
||
return
|
||
}
|
||
lastTalkingInfo.value = {
|
||
crsId: info.crsId,
|
||
qstLogId: _body.chatBody,
|
||
pgrphId: info.pgrphId,
|
||
chatContent: _body.chatContent,
|
||
userInfo: {...choiceTeacherInfo.value},
|
||
..._body.chatBody,
|
||
type: Number(_body.bodyTyp) || 0,
|
||
id: _id,
|
||
isQuestion: true
|
||
}
|
||
talkingList.value.push(lastTalkingInfo.value)
|
||
qstLogId.value = _body.chatBody.qstLogId
|
||
scrollToBottomNow()
|
||
})
|
||
}else if (type === 'reStartPrgh'){
|
||
talkingList.value.push({
|
||
crsId: info.crsId,
|
||
pgrphId: info.pgrphId,
|
||
talkingContent: '重新学',
|
||
type: 0,
|
||
id: _id
|
||
})
|
||
textChartApi({
|
||
processType: 'NEW-KNOW',
|
||
requestBody: JSON.stringify({
|
||
logId: info.logId,
|
||
pgrphId: info.pgrphId
|
||
})
|
||
}).then(res=>{
|
||
let _body = res.body|| {}
|
||
if(!_body.chatBody) {
|
||
return
|
||
}
|
||
lastTalkingInfo.value = {
|
||
crsId: info.crsId,
|
||
qstLogId: _body.chatBody,
|
||
pgrphId: info.pgrphId,
|
||
chatContent: _body.chatContent,
|
||
userInfo: {...choiceTeacherInfo.value},
|
||
..._body.chatBody,
|
||
type: Number(_body.bodyTyp) || 0,
|
||
id: _id,
|
||
isQuestion: true
|
||
}
|
||
talkingList.value.push(lastTalkingInfo.value)
|
||
logId.value = _body.chatBody.logId
|
||
qstLogId.value = _body.chatBody.qstLogId || qstLogId.value
|
||
scrollToBottomNow()
|
||
})
|
||
}
|
||
}
|
||
const scrollBoxRef = ref()
|
||
const scrollTop = ref(99999)
|
||
const scrollToBottomNow = (time = 500) => {
|
||
nextTick(()=>{
|
||
setTimeout(() => {
|
||
scrollTop.value += 1
|
||
scrollTop.value += 1
|
||
let _arr = talkingList.value || []
|
||
if(_arr.length && _arr.length > 0){
|
||
let _last = talkingList.value[talkingList.value.length - 1]
|
||
uni.pageScrollTo({
|
||
selector: '#item' + _last.id,
|
||
duration: 300
|
||
})
|
||
}
|
||
},time)
|
||
})
|
||
}
|
||
|
||
const reconnectTimer = null // 重连timer
|
||
const isManualClose = ref(false) // 是否手动关闭
|
||
|
||
const stdyId = ref('')
|
||
const stdyBatch = ref('')
|
||
const logId = ref('')
|
||
// ws 初始化逻辑
|
||
const initsocketTask = () => {
|
||
socketStore.creatSocket()
|
||
// unref(SocketObj).on('open', (res)=>{
|
||
// if(startPgrphId.value){
|
||
// sendMessage({
|
||
// "commond" : 'STDY',
|
||
// "body" : {
|
||
// crsId: crsId.value,
|
||
// teacherNo: choiceTeacher.value
|
||
// }
|
||
// })
|
||
// step.value = 2
|
||
// }
|
||
// })
|
||
unref(SocketObj).on('message', (res)=>{
|
||
const { rtnCode, body, commond } = JSON.parse(res.data)
|
||
if(rtnCode === '0000'){
|
||
if(commond === 'STDYBTH' && !stdyId.value){
|
||
stdyId.value = body.stdyId || ''
|
||
stdyBatch.value = body.stdyBatch || ''
|
||
logId.value = body.logId || ''
|
||
getGraphInfoUnStudy(1)
|
||
step.value = 2
|
||
}
|
||
}else{
|
||
// if(['0005', 'QQ0005'].includes(res.rtnCode)) {
|
||
// goto_login_fun();
|
||
// }
|
||
}
|
||
})
|
||
}
|
||
const reconnect = ()=> {
|
||
initsocketTask()
|
||
}
|
||
const sendMessage = (data) => {
|
||
unref(SocketObj).send(data)
|
||
}
|
||
// ws 结束
|
||
const startPgrphId = ref('')
|
||
const imageId = ref('')
|
||
onLoad((params) => {
|
||
watchFlag.value = 1
|
||
crsId.value = params.crsId||'';
|
||
crsNum.value = params.crsNum||'';
|
||
imageId.value = params.imgId || ''
|
||
startPgrphId.value = params.pgrphId||'';
|
||
console.log('onLoad')
|
||
});
|
||
const showTalkingModel = ref(false)
|
||
// 录音
|
||
onMounted(()=>{
|
||
initsocketTask()
|
||
getData('1');
|
||
})
|
||
onHide(()=>{
|
||
if(watchFlag.value === 1) return
|
||
changePlayItemId('')
|
||
socketStore.closeSocket()
|
||
// common.show('提示信息', '检测到您已离开,即将退出学习页面。',false, '','确认').then((res) => {
|
||
// common.redirectTo(`/pages/courseDetail/index?crsId=${crsId.value}&crsNum=${crsNum.value}&from=study`)
|
||
// }).finally(() => {})
|
||
})
|
||
onShow(()=>{
|
||
if(watchFlag.value === 1) return
|
||
// getData('2');
|
||
initsocketTask()
|
||
})
|
||
const watchFlag = ref(1)
|
||
onUnload(()=>{
|
||
changePlayItemId()
|
||
teacherVoiceObj?.stop?.()
|
||
teacherVoiceObj.src = ''
|
||
})
|
||
const testAnswer = computed(()=>{
|
||
if(talkingList.value.length < 5) return true
|
||
let _arr = talkingList.value.slice(-5)
|
||
let _num = _arr.map(t=>[1,2,6].indexOf(Number(t.type))>=0).filter(t => !!t)
|
||
return _num.length < 5
|
||
})
|
||
const uploadRecordNow = (voicePath) => {
|
||
if(!testAnswer.value){
|
||
common.msg('每个问题最多连续发送五次答案!')
|
||
return
|
||
}
|
||
isUploadingVoice.value = true
|
||
let _data = {
|
||
qnsId: unref(lastTalkingInfo).qnsId,// 问题ID 必输项:true 类型:String
|
||
pgrphId: unref(lastTalkingInfo).pgrphId,// 段落ID 必输项:true 类型:String
|
||
qstLogId: unref(qstLogId),// 段落ID 必输项:true 类型:String
|
||
crsId: crsId.value,// 课程ID 必输项:true 类型:String
|
||
stdyBatch: stdyBatch.value,// 学习批次 必输项:true 类型:String
|
||
ansterContent: '',// 回答文本内容 必输项:false 类型:String
|
||
}
|
||
let _id = new Date().getTime() + 'id'
|
||
//#ifndef APP-PLUS
|
||
// h5 假数据
|
||
talkingList.value.push({
|
||
crsId: crsId.value,
|
||
pgrphId: unref(lastTalkingInfo).pgrphId,
|
||
qnsId: unref(lastTalkingInfo).qnsId,
|
||
talkingContent: '',
|
||
talkingVoice: voicePath,
|
||
type: 1,
|
||
userInfo: {...choiceTeacherInfo.value},
|
||
id: _id
|
||
})
|
||
setIsUploadingVoice()
|
||
scrollToBottomNow()
|
||
// #endif
|
||
//#ifdef APP-PLUS
|
||
talkingList.value.push({
|
||
crsId: crsId.value,
|
||
pgrphId: unref(lastTalkingInfo).pgrphId,
|
||
qnsId: unref(lastTalkingInfo).qnsId,
|
||
talkingContent: '',
|
||
talkingVoice: voicePath,
|
||
intrctId: '',
|
||
isLoading: true,
|
||
type: 1,
|
||
userInfo: {...choiceTeacherInfo.value},
|
||
id: _id
|
||
})
|
||
scrollToBottomNow()
|
||
commonUploadVoiceFile(voicePath, '/trastudy/traStdychat/voiceChart', {
|
||
processType: 'ANSER-VOICE',
|
||
requestBody: JSON.stringify(_data)
|
||
}).then(res=>{
|
||
let _res = JSON.parse(res.data)
|
||
if(_res.rtnCode === '0000'){
|
||
talkingList.value = talkingList.value.map(t=>{
|
||
if(t.id === _id){
|
||
return {
|
||
crsId: crsId.value,
|
||
pgrphId: _data.pgrphId,
|
||
qstLogId: _data.qstLogId,
|
||
qnsId: _data.qnsId,
|
||
talkingContent: '',
|
||
talkingVoice: voicePath,
|
||
intrctId: _res.body.chatBody,
|
||
type: 1,
|
||
userInfo: {...choiceTeacherInfo.value},
|
||
id: _id
|
||
}
|
||
}else{
|
||
return t
|
||
}
|
||
})
|
||
scrollToBottomNow()
|
||
}else{}
|
||
setIsUploadingVoice()
|
||
}).catch(err=>{
|
||
setIsUploadingVoice()
|
||
talkingList.value = talkingList.value.filter(t=>t.id !== _id)
|
||
uni.showToast({
|
||
title: '系统异常,请联系管理员',
|
||
icon: "none",
|
||
duration:1000
|
||
});
|
||
})
|
||
// #endif
|
||
}
|
||
const setIsUploadingVoice = (flag = false) => {
|
||
if(flag){
|
||
isUploadingVoice.value = true
|
||
}else{
|
||
setTimeout(()=>{
|
||
isUploadingVoice.value = false
|
||
},100)
|
||
}
|
||
}
|
||
// 发送文本
|
||
const submitAnswerNow = (val, cb) => {
|
||
if(!testAnswer.value){
|
||
common.msg('每个问题最多连续发送五次答案!')
|
||
return
|
||
}
|
||
isUploadingVoice.value = true
|
||
let _data = {
|
||
qnsId: unref(lastTalkingInfo).qnsId,// 问题ID 必输项:true 类型:String
|
||
pgrphId: unref(lastTalkingInfo).pgrphId,// 段落ID 必输项:true 类型:String
|
||
qstLogId: unref(qstLogId),// 段落ID 必输项:true 类型:String
|
||
crsId: crsId.value,// 课程ID 必输项:true 类型:String
|
||
stdyBatch: stdyBatch.value,// 学习批次 必输项:true 类型:String
|
||
ansterContent: val // 回答文本内容 必输项:false 类型:String
|
||
}
|
||
textChartApi({
|
||
processType: 'ANSER-TEXT',
|
||
requestBody: JSON.stringify(_data)
|
||
})
|
||
.then(res=>{
|
||
setIsUploadingVoice()
|
||
let _id = new Date().getTime() + 'id'
|
||
let _body = res.body|| {}
|
||
if(!_body.chatBody) {
|
||
return
|
||
}
|
||
talkingList.value.push({
|
||
..._data,
|
||
chatContent: _body.chatContent,
|
||
userInfo: {...choiceTeacherInfo.value},
|
||
intrctId: _body.chatBody ||'',
|
||
talkingContent: val,
|
||
talkingVoice: '',
|
||
type: 2,
|
||
userInfo: {...choiceTeacherInfo.value},
|
||
qstLogId: qstLogId.value,
|
||
id: _id
|
||
})
|
||
scrollToBottomNow()
|
||
cb && cb()
|
||
}).catch(()=>{
|
||
setIsUploadingVoice()
|
||
})
|
||
}
|
||
// 撤回
|
||
const withdrawNow = (data) => {
|
||
textChartApi({
|
||
processType: 'ANSER-WITHDRAW',
|
||
requestBody: JSON.stringify({
|
||
intrctId: data.intrctId
|
||
})
|
||
})
|
||
.then(res=> {
|
||
// 删除撤回的数据item
|
||
common.msg('撤回成功!')
|
||
talkingList.value = talkingList.value.filter(t => t.intrctId !== data.intrctId)
|
||
})
|
||
}
|
||
const loadingQstLogId = ref('')
|
||
// 完成
|
||
const finishQuestionNow = (data) => {
|
||
textChartApi({
|
||
processType: 'ANSER-OVER',
|
||
requestBody: JSON.stringify({
|
||
qstLogId: data.qstLogId
|
||
})
|
||
})
|
||
.then(res=> {
|
||
// common.msg('完成成功,开始获取评分维度数据!')
|
||
let _state = res.body.chatBody.processStat
|
||
if(_state === 'ER'){
|
||
common.msg('提交答案失败!')
|
||
return
|
||
}
|
||
let _id1 = new Date().getTime() + '3d'
|
||
talkingList.value.push({
|
||
id: _id1,
|
||
talkingContent: '完成',
|
||
userInfo: {...choiceTeacherInfo.value},
|
||
type: 0
|
||
})
|
||
loadingQstLogId.value = res.body.chatBody.qstLogId
|
||
qstLogId.value = loadingQstLogId.value
|
||
let _id = new Date().getTime() + 'id'
|
||
lastTalkingInfo.value = {
|
||
isLoading: true,
|
||
id: _id,
|
||
userInfo: {...choiceTeacherInfo.value},
|
||
type: 5
|
||
}
|
||
talkingList.value.push(lastTalkingInfo.value)
|
||
scrollToBottomNow(0)
|
||
getQuestionEvalateRequest(_id, data, 1)
|
||
})
|
||
}
|
||
// 查询问题回答评分
|
||
const getQuestionEvalateRequest = (id, data, num) => {
|
||
if(loadingQstLogId.value){
|
||
textChartApi({
|
||
processType: 'ANSER-RESULT',
|
||
requestBody: JSON.stringify({
|
||
qstLogId:loadingQstLogId.value
|
||
})
|
||
})
|
||
.then(res=>{
|
||
let _body = res.body|| {}
|
||
if(!_body.chatBody) {
|
||
return
|
||
}
|
||
if(_body.chatBody && _body.chatBody.evalOver === 'Y'){
|
||
talkingList.value = talkingList.value.map(t=>{
|
||
if(t.id === id){
|
||
lastTalkingInfo.value = {
|
||
..._body.chatBody,
|
||
pgrphId: data.pgrphId,
|
||
qstLogId: loadingQstLogId.value,
|
||
id,
|
||
userInfo: {...choiceTeacherInfo.value},
|
||
type: 5
|
||
}
|
||
return lastTalkingInfo.value
|
||
}else{
|
||
return t
|
||
}
|
||
})
|
||
scrollToBottomNow()
|
||
}else{
|
||
// 1分钟之后取消获取报错
|
||
if(num === 30){
|
||
common.msg('获取评分数据失败,请重新提交获取!')
|
||
talkingList.value.pop()//删除loading会话
|
||
talkingList.value.pop()//删除完成会话
|
||
return
|
||
}
|
||
setTimeout(()=>{
|
||
getQuestionEvalateRequest(id, data, num+1)
|
||
}, 2 * 1000)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
const showSeekModel = ref(false)
|
||
const rateList = ref(['0.5', '0.8', '1.0', '1.5', '2.0'])
|
||
const chooseRate = ref('1.0')
|
||
const chooseRateNow = (item) => {
|
||
chooseRate.value = item
|
||
showSeekModel.value = false
|
||
teacherVoiceObj.playbackRate = Number(item)
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss">
|
||
.course-study{
|
||
position: relative;
|
||
display: flex;
|
||
flex-direction: column;
|
||
.top-bg{
|
||
background: linear-gradient(16deg,rgba(234,246,255,0) 0%,#c3e6ff 100%);
|
||
height: 480rpx;
|
||
width: 750rpx;
|
||
}
|
||
.bot-bg{
|
||
// background-color: #F6F8FF;
|
||
background: linear-gradient(136deg,rgba(234,246,255,0) 0%,#c3e6ff 100%);
|
||
width: 750rpx;
|
||
flex: 1;
|
||
}
|
||
.seek-setting{
|
||
position: absolute;
|
||
z-index: 3;
|
||
right: 20rpx;
|
||
top: 68rpx;
|
||
font-size: 26rpx;
|
||
width: 140rpx;
|
||
height: 52rpx;
|
||
padding: 10rpx 0;
|
||
overflow: hidden;
|
||
line-height: 32rpx;
|
||
text-align: right;
|
||
padding-right: 48rpx;
|
||
|
||
.image-icon{
|
||
width: 30rpx;
|
||
height: 32rpx;
|
||
position: absolute;
|
||
right: 0;
|
||
top: 10rpx;
|
||
}
|
||
}
|
||
.course-study-body{
|
||
position: absolute;
|
||
overflow: hidden;
|
||
left:0;
|
||
top: 0;
|
||
height: 100%;
|
||
width: 100%;
|
||
padding:0 0rpx;
|
||
box-sizing: border-box;
|
||
display: flex;
|
||
flex-direction: column;
|
||
.body-title{
|
||
|
||
height: 150rpx;
|
||
text-align: center;
|
||
font-size: 33rpx;
|
||
padding-top: 70rpx;
|
||
line-height: 46rpx;
|
||
box-sizing: border-box;
|
||
font-weight: 500;
|
||
color:#000;
|
||
position: relative;
|
||
// margin-bottom: 23rpx;
|
||
.arrow-icon{
|
||
position: absolute;
|
||
left:21rpx;
|
||
top:65rpx;
|
||
color: #000;
|
||
padding: 15rpx;
|
||
// background-color: red;
|
||
}
|
||
}
|
||
.study-num{
|
||
height: 36rpx;
|
||
line-height: 36rpx;
|
||
text-align: center;
|
||
padding-top: 30rpx;
|
||
box-sizing: content-box;
|
||
background: url(@/static/images/course/study-bg.png) no-repeat;
|
||
border-radius: 30rpx 30rpx 0 0;
|
||
.color-blue{
|
||
color: #06f;
|
||
display: inline;
|
||
}
|
||
}
|
||
.talking-list{
|
||
flex:1;
|
||
overflow-y: auto;
|
||
padding: 10rpx 30rpx;
|
||
background: url(@/static/images/course/study-bg.png) no-repeat;
|
||
background-size: cover;
|
||
box-sizing: border-box;
|
||
width: 100%;
|
||
.talking-list-cont{
|
||
padding: 0;
|
||
}
|
||
.talking-item{
|
||
margin-bottom: 46rpx;
|
||
}
|
||
}
|
||
.talk-btns{
|
||
height: 150rpx;
|
||
background-color: #fff;
|
||
padding: 25rpx 30rpx 0;
|
||
position: relative;
|
||
.touch-btn{
|
||
height: 92rpx;
|
||
box-shadow: 0 8rpx 20rpx 0 rgba(0,0,0,.06);
|
||
text-align: center;
|
||
line-height: 92rpx;
|
||
color: #000;
|
||
font-weight: 400;
|
||
position: relative;
|
||
.input-box{
|
||
width: 100%;
|
||
height: 92rpx;
|
||
line-height: 92rpx;
|
||
text-align: left;
|
||
box-sizing: border-box;
|
||
padding:0 40rpx 0 40rpx;
|
||
::v-deep .uv-input__content{
|
||
padding-right: 50rpx;
|
||
}
|
||
}
|
||
}
|
||
.talk-btn-box{
|
||
position: absolute;
|
||
height: 100%;
|
||
width: 120rpx;
|
||
right: 0;
|
||
top:0;
|
||
.talk-btn-icon{
|
||
width: 40rpx;
|
||
height: 40rpx;
|
||
position: absolute;
|
||
right: 60rpx;
|
||
top: 50%;
|
||
margin-top: -20rpx;
|
||
}
|
||
}
|
||
}
|
||
.voice-list{
|
||
flex:1;
|
||
overflow-y: auto;
|
||
padding: 10rpx 40rpx 30rpx;
|
||
.voice-item{
|
||
min-height: 246rpx;
|
||
width: 100%;
|
||
overflow: hidden;
|
||
background-color: #fff;
|
||
border-radius: 16rpx;
|
||
display: flex;
|
||
padding: 25rpx;
|
||
position: relative;
|
||
&:not(:last-child){
|
||
margin-bottom: 30rpx;
|
||
}
|
||
.avatar-box{
|
||
width: 160rpx;
|
||
height: 207rpx;
|
||
margin-right: 20rpx;
|
||
border-radius: 16rpx;
|
||
overflow: hidden;
|
||
background-color: #ccc;
|
||
.img-box{
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
}
|
||
.right-cont{
|
||
flex:1;
|
||
.right-name{
|
||
width: 380rpx;
|
||
line-height: 40rpx;
|
||
font-size: 28rpx;
|
||
font-weight: 500;
|
||
margin-bottom: 25rpx;
|
||
word-break: break-all;
|
||
white-space: nowrap;
|
||
text-overflow: ellipsis;
|
||
overflow: hidden;
|
||
}
|
||
.right-desc{
|
||
width: 380rpx;
|
||
line-height: 38rpx;
|
||
font-size: 25rpx;
|
||
color: #999;
|
||
margin-bottom: 12rpx;
|
||
word-break: break-all;
|
||
white-space: nowrap;
|
||
text-overflow: ellipsis;
|
||
overflow: hidden;
|
||
}
|
||
.audio-box{
|
||
width: 90%;
|
||
height: 70rpx;
|
||
line-height: 70rpx;
|
||
background-color: #eaf2ff;
|
||
padding: 15rpx 25rpx;
|
||
overflow: hidden;
|
||
.talking-gif{
|
||
width: 140rpx;
|
||
height: 40rpx;
|
||
margin-right: 20rpx;
|
||
// background-color: #ccc;
|
||
background: url(@/static/images/course/voice-gray.png) no-repeat;
|
||
background-size: 100%;
|
||
box-sizing: border-box;
|
||
float: left;
|
||
&.is-play{
|
||
background: url(@/static/images/course/voice-gif.gif) no-repeat;
|
||
background-size: 100%;
|
||
}
|
||
}
|
||
.talking-time{
|
||
float: right;
|
||
text-align: right;
|
||
line-height: 40rpx;
|
||
font-size: 23rpx;
|
||
color: #b1bbd3;
|
||
}
|
||
.play-btn{
|
||
float:right;
|
||
width: 35rpx;
|
||
height: 40rpx;
|
||
padding-top:2rpx;
|
||
line-height: 35rpx;
|
||
margin-left: 20rpx;
|
||
}
|
||
}
|
||
}
|
||
.radio-box{
|
||
position: absolute;
|
||
right: 20rpx;
|
||
top: 20rpx;
|
||
}
|
||
}
|
||
.start-btns{
|
||
margin-top:20rpx;
|
||
height: 80rpx;
|
||
width: 100%;
|
||
background-color: #06f;
|
||
color:#fff;
|
||
text-align: center;
|
||
line-height: 80rpx;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
.overlay-box{
|
||
display: flex;
|
||
flex-direction: column-reverse;
|
||
// justify-content: flex-end;
|
||
width: 100%;
|
||
height: 100%;
|
||
.circle-bg-box{
|
||
height: 234rpx;
|
||
background: url(@/static/images/course/talking.png) no-repeat;
|
||
background-size: cover;
|
||
position: relative;
|
||
.icon-box{
|
||
position: absolute;
|
||
left:50%;
|
||
top: 50%;
|
||
margin-left: -17rpx;
|
||
margin-top: -24rpx;
|
||
width: 34rpx;
|
||
height: 48rpx;
|
||
image{
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
}
|
||
}
|
||
.tips-box{
|
||
height: 40rpx;
|
||
line-height: 40rpx;
|
||
color:#c0c0c0;
|
||
text-align: center;
|
||
margin: 12rpx 0 24rpx;
|
||
}
|
||
.btns-box{
|
||
height: 270rpx;
|
||
// padding: 0 120rpx;
|
||
width: 154rpx;
|
||
margin: 0 auto 100rpx;
|
||
overflow: hidden;
|
||
font-size: 28rpx;
|
||
text-align: center;
|
||
line-height: 116rpx;
|
||
.close-btn{
|
||
float: left;
|
||
height: 100%;
|
||
width: 154rpx;
|
||
overflow: hidden;
|
||
color:#999;
|
||
&.is-out{
|
||
color:#fff;
|
||
}
|
||
}
|
||
.translate-btn{
|
||
float: right;
|
||
height: 100%;
|
||
width: 154rpx;
|
||
overflow: hidden;
|
||
color:#999;
|
||
}
|
||
.btn-box{
|
||
width: 100%;
|
||
height: 154rpx;
|
||
}
|
||
}
|
||
.talk-dialog{
|
||
width: 506rpx;
|
||
height: 234rpx;
|
||
margin: 0 auto;
|
||
background-color: #06f;
|
||
}
|
||
.white-bg-box{
|
||
height: 365rpx;
|
||
background-color: #fff;
|
||
overflow: hidden;
|
||
border-radius: 32rpx 32rpx 0 0;
|
||
padding: 58rpx 36rpx;
|
||
&.white-bg-box-setting{
|
||
height: auto;
|
||
min-height: 400rpx;
|
||
}
|
||
.box-title{
|
||
font-size: 32rpx;
|
||
font-weight: bold;
|
||
line-height: 46rpx;
|
||
margin-bottom: 46rpx;
|
||
}
|
||
.box-btns{
|
||
height: 46rpx;
|
||
line-height: 46rpx;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
color: #999;
|
||
.is-active{
|
||
color: #06f;
|
||
}
|
||
}
|
||
.box-items{
|
||
.box-item{
|
||
margin-bottom: 20rpx;
|
||
overflow: hidden;
|
||
.box-item-title{
|
||
width: 86rpx;
|
||
height: 100%;
|
||
float: left;
|
||
overflow: hidden;
|
||
color: #666;
|
||
font-size: 28rpx;
|
||
}
|
||
.box-item-cont{
|
||
display: block;
|
||
float: left;
|
||
width: calc(100% - 86rpx);
|
||
overflow: hidden;
|
||
.overlay-voice-item{
|
||
width: 123rpx;
|
||
float: left;
|
||
position: relative;
|
||
margin-bottom: 30rpx;
|
||
&:not(:nth-child(3n)){
|
||
margin-right: 100rpx;
|
||
}
|
||
.overlay-avatar-box{
|
||
width: 123rpx;
|
||
height: 123rpx;
|
||
border-radius: 50%;
|
||
overflow: hidden;
|
||
box-shadow: 1rpx 0rpx 5rpx #ccc;
|
||
}
|
||
.overlay-teacher-name{
|
||
width: 123rpx;
|
||
text-align: center;
|
||
height: 58rpx;
|
||
line-height: 58rpx;
|
||
font-size: 28rpx;
|
||
}
|
||
.overlay-radio-box{
|
||
position: absolute;
|
||
left: 100rpx;
|
||
top: 5rpx;
|
||
pointer-events: none;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
</style>
|