Files
tra-app/src/pages/course/study.vue
T
2025-07-09 19:30:54 +08:00

1051 lines
26 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" @click="showChangeSeekModelNow">
{{chooseRate}}
<image src="@/static/images/course/setting.png" alt="" class="image-icon"></image>
</view>
<view class="body-title">
<uv-icon class="arrow-icon" name="arrow-left" @click="common.navigateBack()"/>
语言选择开启学习之旅
</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" :imgId="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 ? 'is-play':'']"></view>
<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" @click="showChangeSeekModelNow">
{{chooseRate}}
<image src="@/static/images/course/setting.png" alt="" class="image-icon"></image>
</view>
<view class="body-title" @click="scrollToBottomNow">
<uv-icon class="arrow-icon" name="arrow-left" @click="common.navigateBack()"/>
课程学习
</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"> -->
<view class="study-num">
<view class="color-blue">{{ showOrder }}</view> / {{ pgrphNum }}
</view>
<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('')"
:answerType="lastTalkingInfo.type||0"
/>
</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>
</view>
</template>
<script setup>
import { reactive, ref, computed, unref, onMounted, onUnmounted, getCurrentInstance, nextTick } from 'vue';
import { onLoad,onShow, onUnload } 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 getData = async (flag) => {
// if(flag === flagVal.value){
// flagVal.value = '2'
// }else{
// return
// }
try{
const { body } = await getCourseStudyInfoApi({crsId: crsId.value})
console.log(body)
initsocketTask()
teacherList.value = body.teacheList.map(t=>({
...t,
voicePath: '',
voiceObj: null
}))
if(body.stdyProgressFlag === 'N'){
step.value = 1
}else{
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{
}
};
const showChangeSeekModelNow = () => {
// changePlayItemId()
showSeekModel.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 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{
storageStore.getStorageById('audio', item.teacherNo, (url)=>{
if(url){
teacherVoiceObj.src = _cache
teacherVoiceObj.play()
}else{
downloadVoiceFile('/trastudy/traStdyInfo/trialListening?teachNo='+item.teacherNo).then(res=>{
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 getGraphInfoUnStudy = () => {
console.log(isLastPgrph.value)
let _id = new Date().getTime() + 'id'
// if(isLastPgrph.value){
// talkingList.value.push({
// chatContent: '您已学完全部知识点!',
// userInfo: {...choiceTeacherInfo.value},
// type: 7,
// id: _id
// })
// scrollToBottomNow()
// // common.msg('您已学完全部知识点!')
// return
// }
textChartApi({
processType: 'PRGH-STDY',
requestBody: JSON.stringify({
crsId: crsId.value,
stdyId: unref(stdyId),
stdyBatch: unref(stdyBatch)
})
}).then(res=>{
let _body = res.body|| {}
console.log(_body)
if(!_body.chatBody) {
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.chatBody)
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'){
// 重新回答
textChartApi({
processType: 'NEW-ANSER',
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()
})
}
}
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()
}
}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 结束
onLoad((params) => {
crsId.value = params.crsId||'';
crsNum.value = params.crsNum||'';
});
const showTalkingModel = ref(false)
// 录音
onMounted(()=>{
console.log('init/111')
getData('1');
})
// onShow(()=>{
// getData('2');
// })
onUnload(()=>{
changePlayItemId()
teacherVoiceObj?.stop?.()
teacherVoiceObj.src = ''
})
const uploadRecordNow = (voicePath) => {
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
})
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)
console.log(_res)
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.chatBody,
type: 1,
userInfo: {...choiceTeacherInfo.value},
id: _id
}
}else{
return t
}
})
scrollToBottomNow()
}else{}
}).catch(err=>{
talkingList.value = talkingList.value.filter(t=>t.id !== _id)
uni.showToast({
title: '系统异常,请联系管理员',
icon: "none",
duration:1000
});
})
// #endif
}
// 发送文本
const submitAnswerNow = (val, cb) => {
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=>{
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()
})
}
// 撤回
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) => {
let _id1 = new Date().getTime() + 'id'
talkingList.value.push({
id: _id1,
talkingContent: '完成',
userInfo: {...choiceTeacherInfo.value},
type: 0
})
// waitMakeEvaluateApi({
// qstLogId: data.qstLogId
// })
textChartApi({
processType: 'ANSER-OVER',
requestBody: JSON.stringify({
qstLogId: data.qstLogId
})
})
.then(res=> {
// common.msg('完成成功,开始获取评分维度数据!')
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){
// queryQuestionEvalateApi({
// qstLogId: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;
}
}
.talking-list{
flex:1;
overflow-y: auto;
padding: 30rpx;
background: url(@/static/images/course/study-bg.png) no-repeat;
background-size: cover;
border-radius: 30rpx 30rpx 0 0;
box-sizing: border-box;
width: 100%;
.talking-list-cont{
padding: 0;
}
.study-num{
height: 36rpx;
line-height: 36rpx;
text-align: center;
margin-bottom: 15rpx;
.color-blue{
color: #06f;
display: inline;
}
}
.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;
}
}
.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;
// 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>