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 {
getToken
} from '@/common/common.js'
import common from '@/common/common';
export const uploadFile = (file, data) => {
@@ -145,9 +146,11 @@ export function downloadVoiceFile(url) {
url: `${baseUrl}${url}`,
header,
success(result) {
console.log(result)
resolve(result)
},
fail(error) {
console.log(error)
reject(new Error(error));
}
});
+16 -3
View File
@@ -26,13 +26,14 @@ export default class WebSocketUtil {
close: [], // 连接关闭回调
error: [] // 错误处理回调
};
this.init(); // 初始化WebSocket连接
this.init(true); // 初始化WebSocket连接
}
/**
* 初始化WebSocket连接
*/
init() {
init(flag = false) {
if(flag) this.reconnectCount = 0
// 创建WebSocket连接
this.socketTask = uni.connectSocket({
url: this.url,
@@ -59,7 +60,17 @@ export default class WebSocketUtil {
// 监听WebSocket消息接收事件
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); // 触发消息接收事件
});
@@ -198,6 +209,8 @@ export default class WebSocketUtil {
} else {
console.error('达到最大重连次数,停止尝试');
this.triggerEvent('error', new Error('达到最大重连次数'));
this.reconnectTimer = null
this.close()
goto_login_fun()
}
}
+25 -4
View File
@@ -4,7 +4,10 @@
<script>
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 {
name:"image-preview",
props:{
@@ -36,6 +39,12 @@
base_url: get_base_url()
};
},
computed:{
storageStore:() => useStorageStore(),
imageObj() {
return this.storageStore?.imgObj||{}
}
},
watch: {
imgId: {
handler(newval, oldval) {
@@ -58,15 +67,26 @@
},
methods: {
getPreviewUrlBlob(id) {
if (id === null) {
console.log('谢谢谢谢谢谢', id);
if (!id) {
this.imgUrlShow = ''
return
}
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) => {
this.imgUrlShow = res;
this.storageStore.setStorage('image', `/${id}/${this.width}/${this.height}`, res)
});
}else{
let _cache = this.imageObj[`${id}`] || ''
if(_cache){
this.imgUrlShow = _cache;
return
}
previewFile(id).then((result) => {
//#ifdef APP-PLUS
let _header = result.header['Content-Type'].split(';')[0]
@@ -76,6 +96,7 @@
// #endif
let base64 = `data:${_header};base64,` + uni.arrayBufferToBase64(result.data)
this.imgUrlShow = base64;
this.storageStore.setStorage('image', id, base64)
});
}
},
+24 -7
View File
@@ -1,27 +1,34 @@
<template>
<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>
</template>
<script>
import { downloadFile } from'@/api/common.js'
import { useStorageStore } from "@/store/storage.js"
export default {
name:"video-preview",
props:{
imgId:{
fileId:{
default:'',
type:String
}
},
data() {
return {
imgUrlShow:'',
fileUrlShow:'',
watching:false
};
},
computed:{
storageStore:() => useStorageStore(),
videoObj() {
return this.storageStore?.videoObj||{}
}
},
watch: {
imgId: {
fileId: {
handler(newval, oldval) {
if (newval && this.watching) {
this.getPreviewUrlBlob(newval);
@@ -33,14 +40,24 @@
},
methods: {
getPreviewUrlBlob(id) {
if (!id) {
this.fileUrlShow = ''
return
}
let _cache = this.videoObj[id] || ''
if(_cache){
this.fileUrlShow = _cache;
return
}
downloadFile(id).then(res=>{
this.imgUrlShow = res.tempFilePath
this.fileUrlShow = res.tempFilePath
this.storageStore.setStorage('video', id, this.fileUrlShow)
})
},
},
mounted() {
if (this.imgId) {
this.getPreviewUrlBlob(this.imgId);
if (this.fileId) {
this.getPreviewUrlBlob(this.fileId);
}
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>
<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="avatar-box">
<ImagePreview class="img-box" :imgId="dataInfo?.userInfo?.imgAddr"></ImagePreview>
@@ -108,7 +108,7 @@
</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="avatar-box">
<!-- <ImagePreview class="img-box" :imgId="''"></ImagePreview> -->
@@ -119,9 +119,12 @@
</view>
</view>
<view class="talking-info-less">
<view class="talking-text">
<view class="talking-text" v-if="type === 0">
{{ dataInfo.talkingContent }}
</view>
<view class="talking-text" v-if="type === 6">
<CommonLoading color="#fff"/>
</view>
</view>
</view>
<view v-else></view>
@@ -135,7 +138,12 @@ import { downloadVoiceFile, uploadVoiceFile, } from "@/api/common.js"
import { studyParagraphOverApi } from "@/api/study.js"
import ChartFour from "./chartFour.vue"
import ChartThree from "./chartThree.vue"
import common 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({
type:{
@@ -213,6 +221,11 @@ import { getUserInfo } from '@/common/common';
}
const itemVoice = ref('')
const playVoice = (readType = 'pgth') =>{
let _content = unref(dataInfo)?.pgrphContent || unref(dataInfo)?.qnsContent || unref(dataInfo)?.talkingContent || ''
if(!_content){
common.msg('没有可以播放的文字信息!')
return
}
// readType 阅读内容类型pgrh段落/qns问题
emit('changePlay', unref(dataInfo)?.id)
if(itemVoice.value){
@@ -228,19 +241,52 @@ import { getUserInfo } from '@/common/common';
qnsId: unref(dataInfo)?.qnsId || '',
readType: readType,
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=>{
return encodeURIComponent(t) + '=' + encodeURIComponent(_params[t])
}).join('&')
downloadVoiceFile(_url).then(res=>{
voiceLoading.value = false
itemVoice.value = res.tempFilePath
talkVoiceObj.value.src = res.tempFilePath
talkVoiceObj.value.play()
}).catch((err)=>{
voiceLoading.value = false
storageStore.getStorageById('audio', _url, (url)=>{
if(url){
voiceLoading.value = false
itemVoice.value = url
talkVoiceObj.value.src = url
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) =>{
+30 -14
View File
@@ -43,9 +43,15 @@
</view>
<view class="body-title" @click="scrollToBottomNow">
<uv-icon class="arrow-icon" name="arrow-left" @click="common.navigateBack()"/>
课程学习
课程学习{{scrollTop}}
</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="study-num">
<view class="color-blue">{{ showOrder }}</view> / {{ pgrphNum }}
@@ -79,7 +85,7 @@
>按住说话
</view>
<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="请输入答案">
</uv-input>
</view>
@@ -153,10 +159,13 @@ import TalkingItem from '@/pages/course/components/talkingItem.vue'
import ImagePreview from '@/components/image-preview/image-preview.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('');
@@ -242,6 +251,7 @@ const choiceTeacherInfo = computed(()=>{
const activeVoiceTeachNo = ref('')
const teacherVoiceObj = uni.createInnerAudioContext()
teacherVoiceObj.onEnded(()=>{
activeVoiceTeachNo.value = ''
teacherVoiceObj.src = ''
@@ -256,10 +266,18 @@ const playTeacherVoice = (item) =>{
teacherVoiceObj.src = item.voicePath
teacherVoiceObj.play()
}else{
downloadVoiceFile('/trastudy/traStdyInfo/trialListening?teachNo='+item.teacherNo).then(res=>{
item.voicePath = res.tempFilePath
teacherVoiceObj.src = item.voicePath
teacherVoiceObj.play()
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)
})
}
})
}
}
@@ -347,18 +365,15 @@ const nextQuestionSuccess = (info) => {
getQuestionInfoUnStudy(info.pgrphId)
}
const scrollBoxRef = ref()
const scrollTop = ref(99999)
const scrollToBottomNow = () => {
nextTick(()=>{
setTimeout(() => {
// let scrollElem = scrollBoxRef.value.$el
// // let scrollElem = document.getElementById('scroll-box')
// scrollElem.scrollTo({
// top: scrollElem.scrollHeight, behavior: 'smooth'
// })
scrollTop.value += 1
scrollTop.value += 1
let _arr = talkingList.value || []
if(_arr.length && _arr.length > 0){
let _last = talkingList.value[talkingList.value.length - 1]
console.log('#item' + _last.id)
uni.pageScrollTo({
selector: '#item' + _last.id,
duration: 300
@@ -516,10 +531,10 @@ const hideOverlayTalking = (obj) => {
userInfo: {...choiceTeacherInfo.value},
id: _id
})
scrollToBottomNow()
// #endif
//#ifdef APP-PLUS
commonUploadVoiceFile(audioPath.value, '/trastudy/traStdyInfo/studyAnswerVoiceUp', _data).then(res=>{
console.log(res.data)
let _res = JSON.parse(res.data)
if(_res.rtnCode === '0000'){
let _id = new Date().getTime() + 'id'
@@ -631,6 +646,7 @@ const chooseRate = ref('1.0')
const chooseRateNow = (item) => {
chooseRate.value = item
showSeekModel.value = false
teacherVoiceObj.playbackRate = Number(item)
}
</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
}
}
})