Merge branch 'develop' of http://25.13.9.101:9000/K17_AITS/tra-app into develop

This commit is contained in:
田岩
2025-07-04 10:11:53 +08:00
7 changed files with 208 additions and 40 deletions
+3
View File
@@ -4,6 +4,7 @@ import {
import { import {
getToken getToken
} from '@/common/common.js' } from '@/common/common.js'
import common from '@/common/common';
export const uploadFile = (file, data) => { export const uploadFile = (file, data) => {
@@ -145,9 +146,11 @@ export function downloadVoiceFile(url) {
url: `${baseUrl}${url}`, url: `${baseUrl}${url}`,
header, header,
success(result) { success(result) {
console.log(result)
resolve(result) resolve(result)
}, },
fail(error) { fail(error) {
console.log(error)
reject(new Error(error)); reject(new Error(error));
} }
}); });
+16 -3
View File
@@ -26,13 +26,14 @@ export default class WebSocketUtil {
close: [], // 连接关闭回调 close: [], // 连接关闭回调
error: [] // 错误处理回调 error: [] // 错误处理回调
}; };
this.init(); // 初始化WebSocket连接 this.init(true); // 初始化WebSocket连接
} }
/** /**
* 初始化WebSocket连接 * 初始化WebSocket连接
*/ */
init() { init(flag = false) {
if(flag) this.reconnectCount = 0
// 创建WebSocket连接 // 创建WebSocket连接
this.socketTask = uni.connectSocket({ this.socketTask = uni.connectSocket({
url: this.url, url: this.url,
@@ -59,7 +60,17 @@ export default class WebSocketUtil {
// 监听WebSocket消息接收事件 // 监听WebSocket消息接收事件
this.socketTask.onMessage((res) => { this.socketTask.onMessage((res) => {
console.log('收到WebSocket消息', res); if(res.data){
const { rtnCode } = JSON.parse(res.data)
console.log('收到WebSocket消息', JSON.parse(res.data) );
if(['0005', 'QQ0005'].includes(res.rtnCode)) {
goto_login_fun();
return
}
}else{
goto_login_fun();
return
}
this.triggerEvent('message', res); // 触发消息接收事件 this.triggerEvent('message', res); // 触发消息接收事件
}); });
@@ -198,6 +209,8 @@ export default class WebSocketUtil {
} else { } else {
console.error('达到最大重连次数,停止尝试'); console.error('达到最大重连次数,停止尝试');
this.triggerEvent('error', new Error('达到最大重连次数')); this.triggerEvent('error', new Error('达到最大重连次数'));
this.reconnectTimer = null
this.close()
goto_login_fun() goto_login_fun()
} }
} }
+25 -4
View File
@@ -4,7 +4,10 @@
<script> <script>
import {previewFile, previewCacheThumbImage} from'@/api/common.js' import {previewFile, previewCacheThumbImage} from'@/api/common.js'
import {get_base_url} from '@/api/request.js' import { get_base_url } from '@/api/request.js'
import { useStorageStore } from "@/store/storage.js"
// const storageStore = useStorageStore()
export default { export default {
name:"image-preview", name:"image-preview",
props:{ props:{
@@ -36,6 +39,12 @@
base_url: get_base_url() base_url: get_base_url()
}; };
}, },
computed:{
storageStore:() => useStorageStore(),
imageObj() {
return this.storageStore?.imgObj||{}
}
},
watch: { watch: {
imgId: { imgId: {
handler(newval, oldval) { handler(newval, oldval) {
@@ -58,15 +67,26 @@
}, },
methods: { methods: {
getPreviewUrlBlob(id) { getPreviewUrlBlob(id) {
if (!id) {
if (id === null) { this.imgUrlShow = ''
console.log('谢谢谢谢谢谢', id); return
} }
if(this.isCache){ if(this.isCache){
let _cache = this.imageObj[`/${id}/${this.width}/${this.height}`] || ''
if(_cache){
this.imgUrlShow = _cache;
return
}
previewCacheThumbImage(id, this.width,this.height).then((res) => { previewCacheThumbImage(id, this.width,this.height).then((res) => {
this.imgUrlShow = res; this.imgUrlShow = res;
this.storageStore.setStorage('image', `/${id}/${this.width}/${this.height}`, res)
}); });
}else{ }else{
let _cache = this.imageObj[`${id}`] || ''
if(_cache){
this.imgUrlShow = _cache;
return
}
previewFile(id).then((result) => { previewFile(id).then((result) => {
//#ifdef APP-PLUS //#ifdef APP-PLUS
let _header = result.header['Content-Type'].split(';')[0] let _header = result.header['Content-Type'].split(';')[0]
@@ -76,6 +96,7 @@
// #endif // #endif
let base64 = `data:${_header};base64,` + uni.arrayBufferToBase64(result.data) let base64 = `data:${_header};base64,` + uni.arrayBufferToBase64(result.data)
this.imgUrlShow = base64; this.imgUrlShow = base64;
this.storageStore.setStorage('image', id, base64)
}); });
} }
}, },
+24 -7
View File
@@ -1,27 +1,34 @@
<template> <template>
<view class="video-view-box"> <view class="video-view-box">
<video class="video-cont" v-if="imgUrlShow" :src="imgUrlShow" controls></video> <video class="video-cont" v-if="fileUrlShow" :src="fileUrlShow" controls></video>
</view> </view>
</template> </template>
<script> <script>
import { downloadFile } from'@/api/common.js' import { downloadFile } from'@/api/common.js'
import { useStorageStore } from "@/store/storage.js"
export default { export default {
name:"video-preview", name:"video-preview",
props:{ props:{
imgId:{ fileId:{
default:'', default:'',
type:String type:String
} }
}, },
data() { data() {
return { return {
imgUrlShow:'', fileUrlShow:'',
watching:false watching:false
}; };
}, },
computed:{
storageStore:() => useStorageStore(),
videoObj() {
return this.storageStore?.videoObj||{}
}
},
watch: { watch: {
imgId: { fileId: {
handler(newval, oldval) { handler(newval, oldval) {
if (newval && this.watching) { if (newval && this.watching) {
this.getPreviewUrlBlob(newval); this.getPreviewUrlBlob(newval);
@@ -33,14 +40,24 @@
}, },
methods: { methods: {
getPreviewUrlBlob(id) { getPreviewUrlBlob(id) {
if (!id) {
this.fileUrlShow = ''
return
}
let _cache = this.videoObj[id] || ''
if(_cache){
this.fileUrlShow = _cache;
return
}
downloadFile(id).then(res=>{ downloadFile(id).then(res=>{
this.imgUrlShow = res.tempFilePath this.fileUrlShow = res.tempFilePath
this.storageStore.setStorage('video', id, this.fileUrlShow)
}) })
}, },
}, },
mounted() { mounted() {
if (this.imgId) { if (this.fileId) {
this.getPreviewUrlBlob(this.imgId); this.getPreviewUrlBlob(this.fileId);
} }
this.watching = true this.watching = true
}, },
+58 -12
View File
@@ -1,6 +1,6 @@
<!-- type: 3, 段落信息 4 问题信息 5 统计信息 1语音回答 2 文本回答 0纯文本信息 不翻译 --> <!-- type: 3, 段落信息 4 问题信息 5 统计信息 1语音回答 2 文本回答 0纯文本信息 不翻译 6: 发送loading状态-->
<template> <template>
<view class="ai-answer" v-if="[3,4,5,6].indexOf(type) >= 0"> <view class="ai-answer" v-if="[3,4,5].indexOf(type) >= 0">
<view class="user-info"> <view class="user-info">
<view class="avatar-box"> <view class="avatar-box">
<ImagePreview class="img-box" :imgId="dataInfo?.userInfo?.imgAddr"></ImagePreview> <ImagePreview class="img-box" :imgId="dataInfo?.userInfo?.imgAddr"></ImagePreview>
@@ -108,7 +108,7 @@
</view> </view>
</view> </view>
</view> </view>
<view class="user-answer" v-else-if="type === 0"> <view class="user-answer" v-else-if="[0, 6].indexOf(type) >= 0">
<view class="user-info"> <view class="user-info">
<view class="avatar-box"> <view class="avatar-box">
<!-- <ImagePreview class="img-box" :imgId="''"></ImagePreview> --> <!-- <ImagePreview class="img-box" :imgId="''"></ImagePreview> -->
@@ -119,9 +119,12 @@
</view> </view>
</view> </view>
<view class="talking-info-less"> <view class="talking-info-less">
<view class="talking-text"> <view class="talking-text" v-if="type === 0">
{{ dataInfo.talkingContent }} {{ dataInfo.talkingContent }}
</view> </view>
<view class="talking-text" v-if="type === 6">
<CommonLoading color="#fff"/>
</view>
</view> </view>
</view> </view>
<view v-else></view> <view v-else></view>
@@ -135,7 +138,12 @@ import { downloadVoiceFile, uploadVoiceFile, } from "@/api/common.js"
import { studyParagraphOverApi } from "@/api/study.js" import { studyParagraphOverApi } from "@/api/study.js"
import ChartFour from "./chartFour.vue" import ChartFour from "./chartFour.vue"
import ChartThree from "./chartThree.vue" import ChartThree from "./chartThree.vue"
import common from '@/common/common';
import { getUserInfo } from '@/common/common'; import { getUserInfo } from '@/common/common';
import { useStorageStore } from "@/store/storage.js"
const storageStore = useStorageStore()
const audioCacheObj = computed(() => storageStore.audioObj)
const props = defineProps({ const props = defineProps({
type:{ type:{
@@ -213,6 +221,11 @@ import { getUserInfo } from '@/common/common';
} }
const itemVoice = ref('') const itemVoice = ref('')
const playVoice = (readType = 'pgth') =>{ const playVoice = (readType = 'pgth') =>{
let _content = unref(dataInfo)?.pgrphContent || unref(dataInfo)?.qnsContent || unref(dataInfo)?.talkingContent || ''
if(!_content){
common.msg('没有可以播放的文字信息!')
return
}
// readType 阅读内容类型pgrh段落/qns问题 // readType 阅读内容类型pgrh段落/qns问题
emit('changePlay', unref(dataInfo)?.id) emit('changePlay', unref(dataInfo)?.id)
if(itemVoice.value){ if(itemVoice.value){
@@ -228,19 +241,52 @@ import { getUserInfo } from '@/common/common';
qnsId: unref(dataInfo)?.qnsId || '', qnsId: unref(dataInfo)?.qnsId || '',
readType: readType, readType: readType,
teachNo: unref(dataInfo)?.userInfo?.teacherNo, // 必填 teachNo: unref(dataInfo)?.userInfo?.teacherNo, // 必填
content: unref(dataInfo)?.pgrphContent || unref(dataInfo)?.qnsContent || unref(dataInfo)?.talkingContent, content: _content
} }
let _url = '/trastudy/traStdyInfo/readContent?' + Object.keys(_params).map(t=>{ let _url = '/trastudy/traStdyInfo/readContent?' + Object.keys(_params).map(t=>{
return encodeURIComponent(t) + '=' + encodeURIComponent(_params[t]) return encodeURIComponent(t) + '=' + encodeURIComponent(_params[t])
}).join('&') }).join('&')
downloadVoiceFile(_url).then(res=>{
voiceLoading.value = false storageStore.getStorageById('audio', _url, (url)=>{
itemVoice.value = res.tempFilePath if(url){
talkVoiceObj.value.src = res.tempFilePath voiceLoading.value = false
talkVoiceObj.value.play() itemVoice.value = url
}).catch((err)=>{ talkVoiceObj.value.src = url
voiceLoading.value = false talkVoiceObj.value.play()
}else{
downloadVoiceFile(_url).then(res=>{
console.log(res)
if(res?.data){
common.msg(res.data.message)
return
}
voiceLoading.value = false
itemVoice.value = res.tempFilePath
talkVoiceObj.value.src = res.tempFilePath
talkVoiceObj.value.play()
storageStore.setStorage('audio', _url, itemVoice.value)
}).catch((err)=>{
voiceLoading.value = false
})
}
}) })
// let _cache = audioCacheObj.value[_url]
// if(_cache){
// voiceLoading.value = false
// itemVoice.value = _cache
// talkVoiceObj.value.src = _cache
// talkVoiceObj.value.play()
// return
// }
// downloadVoiceFile(_url).then(res=>{
// voiceLoading.value = false
// itemVoice.value = res.tempFilePath
// talkVoiceObj.value.src = res.tempFilePath
// talkVoiceObj.value.play()
// storageStore.setStorage('audio', _url, itemVoice.value)
// }).catch((err)=>{
// voiceLoading.value = false
// })
} }
} }
const playQnsVoice = (readType = 'qns', item) =>{ const playQnsVoice = (readType = 'qns', item) =>{
+30 -14
View File
@@ -43,9 +43,15 @@
</view> </view>
<view class="body-title" @click="scrollToBottomNow"> <view class="body-title" @click="scrollToBottomNow">
<uv-icon class="arrow-icon" name="arrow-left" @click="common.navigateBack()"/> <uv-icon class="arrow-icon" name="arrow-left" @click="common.navigateBack()"/>
课程学习 课程学习{{scrollTop}}
</view> </view>
<scroll-view class="talking-list" :scroll-y="true"> <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="talking-list-cont" id="scroll-box" ref="scrollBoxRef"> -->
<view class="study-num"> <view class="study-num">
<view class="color-blue">{{ showOrder }}</view> / {{ pgrphNum }} <view class="color-blue">{{ showOrder }}</view> / {{ pgrphNum }}
@@ -79,7 +85,7 @@
>按住说话 >按住说话
</view> </view>
<view class="touch-btn" v-if="keyboardIsShow"> <view class="touch-btn" v-if="keyboardIsShow">
<uv-input class="input-box" ref="inputRef" v-model.trim="answerValue" <uv-input class="input-box" ref="inputRef" v-model.trim="answerValue" maxlength="1000"
placeholderStyle='color:#999999;font-size:26rpx' placeholder="请输入答案"> placeholderStyle='color:#999999;font-size:26rpx' placeholder="请输入答案">
</uv-input> </uv-input>
</view> </view>
@@ -153,10 +159,13 @@ import TalkingItem from '@/pages/course/components/talkingItem.vue'
import ImagePreview from '@/components/image-preview/image-preview.vue' import ImagePreview from '@/components/image-preview/image-preview.vue'
import { useSocketStore } from "@/store/socket.js" import { useSocketStore } from "@/store/socket.js"
import { useStorageStore } from "@/store/storage.js"
import { storeToRefs } from 'pinia' import { storeToRefs } from 'pinia'
const socketStore = useSocketStore() const socketStore = useSocketStore()
const storageStore = useStorageStore()
const { SocketObj } = storeToRefs(socketStore) const { SocketObj } = storeToRefs(socketStore)
const audioCacheObj = computed(() => storageStore.audioObj)
const { proxy } = getCurrentInstance() const { proxy } = getCurrentInstance()
const crsNum = ref(''); const crsNum = ref('');
@@ -242,6 +251,7 @@ const choiceTeacherInfo = computed(()=>{
const activeVoiceTeachNo = ref('') const activeVoiceTeachNo = ref('')
const teacherVoiceObj = uni.createInnerAudioContext() const teacherVoiceObj = uni.createInnerAudioContext()
teacherVoiceObj.onEnded(()=>{ teacherVoiceObj.onEnded(()=>{
activeVoiceTeachNo.value = '' activeVoiceTeachNo.value = ''
teacherVoiceObj.src = '' teacherVoiceObj.src = ''
@@ -256,10 +266,18 @@ const playTeacherVoice = (item) =>{
teacherVoiceObj.src = item.voicePath teacherVoiceObj.src = item.voicePath
teacherVoiceObj.play() teacherVoiceObj.play()
}else{ }else{
downloadVoiceFile('/trastudy/traStdyInfo/trialListening?teachNo='+item.teacherNo).then(res=>{ storageStore.getStorageById('audio', item.teacherNo, (url)=>{
item.voicePath = res.tempFilePath if(url){
teacherVoiceObj.src = item.voicePath teacherVoiceObj.src = _cache
teacherVoiceObj.play() 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)
})
}
}) })
} }
} }
@@ -347,18 +365,15 @@ const nextQuestionSuccess = (info) => {
getQuestionInfoUnStudy(info.pgrphId) getQuestionInfoUnStudy(info.pgrphId)
} }
const scrollBoxRef = ref() const scrollBoxRef = ref()
const scrollTop = ref(99999)
const scrollToBottomNow = () => { const scrollToBottomNow = () => {
nextTick(()=>{ nextTick(()=>{
setTimeout(() => { setTimeout(() => {
// let scrollElem = scrollBoxRef.value.$el scrollTop.value += 1
// // let scrollElem = document.getElementById('scroll-box') scrollTop.value += 1
// scrollElem.scrollTo({
// top: scrollElem.scrollHeight, behavior: 'smooth'
// })
let _arr = talkingList.value || [] let _arr = talkingList.value || []
if(_arr.length && _arr.length > 0){ if(_arr.length && _arr.length > 0){
let _last = talkingList.value[talkingList.value.length - 1] let _last = talkingList.value[talkingList.value.length - 1]
console.log('#item' + _last.id)
uni.pageScrollTo({ uni.pageScrollTo({
selector: '#item' + _last.id, selector: '#item' + _last.id,
duration: 300 duration: 300
@@ -516,10 +531,10 @@ const hideOverlayTalking = (obj) => {
userInfo: {...choiceTeacherInfo.value}, userInfo: {...choiceTeacherInfo.value},
id: _id id: _id
}) })
scrollToBottomNow()
// #endif // #endif
//#ifdef APP-PLUS //#ifdef APP-PLUS
commonUploadVoiceFile(audioPath.value, '/trastudy/traStdyInfo/studyAnswerVoiceUp', _data).then(res=>{ commonUploadVoiceFile(audioPath.value, '/trastudy/traStdyInfo/studyAnswerVoiceUp', _data).then(res=>{
console.log(res.data)
let _res = JSON.parse(res.data) let _res = JSON.parse(res.data)
if(_res.rtnCode === '0000'){ if(_res.rtnCode === '0000'){
let _id = new Date().getTime() + 'id' let _id = new Date().getTime() + 'id'
@@ -631,6 +646,7 @@ const chooseRate = ref('1.0')
const chooseRateNow = (item) => { const chooseRateNow = (item) => {
chooseRate.value = item chooseRate.value = item
showSeekModel.value = false showSeekModel.value = false
teacherVoiceObj.playbackRate = Number(item)
} }
</script> </script>
+52
View File
@@ -0,0 +1,52 @@
import { defineStore } from 'pinia';
import { ref } from 'vue';
export const useStorageStore = defineStore('storageStore',{
state:()=>({
imgObjStr: '{}',
videoObjStr: '{}',
audioObjStr: '{}'
}),
getters:{
imgObj(state){
return JSON.parse(state.imgObjStr)
},
videoObj(state){
return JSON.parse(state.videoObjStr)
},
audioObj(state){
return JSON.parse(state.audioObjStr)
}
},
actions:{
setStorage(type, id, url) {
if(type === 'image'){
let _obj = JSON.parse(this.imgObjStr)
_obj[id] = url
this.imgObjStr = JSON.stringify(_obj)
}else if(type === 'video'){
let _obj = JSON.parse(this.videoObjStr)
_obj[id] = url
this.videoObjStr = JSON.stringify(_obj)
}else if(type === 'audio'){
let _obj = JSON.parse(this.audioObjStr)
_obj[id] = url
this.audioObjStr = JSON.stringify(_obj)
}
},
getStorageById(type, id, cb = (()=>{})){
// 两种方式 ,传递回调函数 或 直接取return值
let _obj
if(type === 'image'){
_obj = JSON.parse(this.imgObjStr)
}else if(type === 'video'){
_obj = JSON.parse(this.videoObjStr)
}else if(type === 'audio'){
_obj = JSON.parse(this.audioObjStr)
}
let _cache = _obj[id]||''
cb && cb(_cache)
return _cache
}
}
})