Files
tra-app/src/pages/course/study.vue
T

1184 lines
30 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.
<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"
@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"
: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">
<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>
</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
} 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) => {
// if(flag === flagVal.value){
// flagVal.value = '2'
// }else{
// return
// }
try{
const { body } = await getCourseStudyInfoApi({crsId: crsId.value})
initsocketTask()
teacherList.value = body.teacheList.map(t=>({
...t,
voicePath: '',
voiceObj: null
}))
if(body.stdyProgressFlag === 'N'){
step.value = 1
}else{
if(startPgrphId.value){
scrollToBottomNow()
choiceTeacher.value = body.choiceTeacher
sendMessage({
"commond" : 'STDY',
"body" : {
crsId: crsId.value,
teacherNo: choiceTeacher.value
}
})
step.value = 2
return
}
common.show('提示', '系统检测到您已存在学习记录,要继续学习吗?',true, '重新学习','继续学习').then((res) => {
if(res) {
// 继续学习 获取学习历史接口
// 滚动到最下面
scrollToBottomNow()
choiceTeacher.value = body.choiceTeacher
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 = () => {
showBgModel.value = true
}
// 关闭播放状态
const changePlayItemId = (id = '') => {
if(activeVoiceTalkingItemId.value === id){
activeVoiceTalkingItemId.value = ''
}else{
activeVoiceTalkingItemId.value = id
}
}
// 选择的教师对象信息
const choiceTeacherInfo = computed(()=>{
return teacherList.value.find(t=> t.teacherNo === choiceTeacher.value)
})
// 选择教师语音对象
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`)
}
// 获取段落
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(_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(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)
qstLogId.value = _body.chatBody.qstLogId
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('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)
}
}else{
if(['0005', 'QQ0005'].includes(res.rtnCode)) {
goto_login_fun();
}
}
})
}
const reconnect = ()=> {
if(reconnectTimer || isManualClose.value) return
reconnectTimer = setTimeout(()=>{
initsocketTask()
},3000)
}
const sendMessage = (data) => {
unref(SocketObj).send(data)
}
// ws 结束
const startPgrphId = ref('')
const imageId = ref('')
onLoad((params) => {
crsId.value = params.crsId||'';
crsNum.value = params.crsNum||'';
imageId.value = params.imgId || ''
startPgrphId.value = params.pgrphId||'';
getData('1');
});
const showTalkingModel = ref(false)
// 录音
// onMounted(()=>{
// getData('1');
// })
onHide(()=>{
if(watchFlag.value === 1) return
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
socketStore.createSocket()
})
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},
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)
})
}
// 查询问题回答评分
const getQuestionEvalateRequest = (id, data) => {
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{
setTimeout(()=>{
getQuestionEvalateRequest(id, data)
}, 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:36rpx;
top:80rpx;
color: #000;
}
}
.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;
.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;
}
}
}
}
</style>