Merge branch 'develop' into 'sit'

Develop

See merge request K17_AITS/tra-app!7
This commit is contained in:
田岩
2025-07-10 09:24:08 +08:00
6 changed files with 182 additions and 75 deletions
+4 -2
View File
@@ -23,8 +23,8 @@ function streamRequest(options) {
onError
} = options
headers['Accept'] = 'text/event-stream'
headers['Content-Type'] = 'application/json;charset=UTF-8'
// headers['Accept'] = 'text/event-stream'
headers['Content-Type'] = 'text/event-stream'
headers['summary'] = token
// 创建请求任务
@@ -33,8 +33,10 @@ function streamRequest(options) {
method,
data,
header: headers,
responseType: 'arraybuffer',
...baseConfig,
success: (res) => {
console.log(res.data)
onComplete?.(res)
},
fail: (err) => {
+52 -15
View File
@@ -1,4 +1,4 @@
<!-- type: 0纯文本信息不翻译 1语音回答 2 文本回答 3, 段落信息 4 问题信息 5 统计信息 6: 发送loading状态 -->
<!-- type: 0纯文本信息不翻译 1语音回答 2 文本回答 3, 段落信息 4 问题信息 5 统计信息 6: 发送loading状态 7: 教师反馈文本不翻译 -->
<template>
<view class="ai-answer" v-if="[3,4,5].indexOf(type) >= 0">
<view class="user-info">
@@ -27,7 +27,7 @@
<ImagePreview class="cont-img-box" v-if="(dataInfo.imageAddrs||[]).length>0" :imgId="dataInfo.imageAddrs[0]"></ImagePreview>
<VideoPreview class="cont-img-box" v-if="(dataInfo.vidoAddrs||[]).length>0" :imgId="dataInfo.vidoAddrs[0]"></VideoPreview>
</view>
<view v-if="type === 5 && !dataInfo.isLoading">
<view v-if="type === 5 && !dataInfo.isLoading" style="padding-top: 16rpx;">
<ChartFour v-if="dataInfo.evalDimens.length === 4" :dataList="dataInfo.evalDimens" :score="dataInfo.intrctGrade"/>
<ChartThree v-if="dataInfo.evalDimens.length === 3" :dataList="dataInfo.evalDimens" :score="dataInfo.intrctGrade"/>
</view>
@@ -59,7 +59,7 @@
</view>
<view
class="next-learn-btn"
v-if="showNextBtn && [5].indexOf(type) >= 0 && isLast"
v-if="showNextBtn && [5].indexOf(type) >= 0 && isLast && dataInfo.passStat === 'PS'"
@click.stop="goOnGetQues"
>
继续学
@@ -70,13 +70,41 @@
v-if="showNextBtn && [5].indexOf(type) >= 0 && isLast"
@click.stop="goOnRestart"
>
重新
重新
<image class="icon icon-iamge" src="@/static/images/course/restart.png" ></image>
</view>
<!-- <view class="replay-study-btn" v-if="showNextBtn && [5].indexOf(type) >= 0 && isLast"><image class="icon icon-iamge" src="@/static/images/course/restudy.png" ></image></view> -->
<view class="replay-study-btn"
v-if="showNextBtn && [5].indexOf(type) >= 0 && isLast"
@click.stop="goOnRestartPrgh"
>
<image class="icon icon-iamge" src="@/static/images/course/restudy.png" ></image>
</view>
<view class="next-btn" @click.stop="goOnGetQues" v-if="showNextBtn && [3, 6].indexOf(type) >= 0 && isLast">继续
<image class="icon icon-iamge" src="@/static/images/course/goon.png" ></image></view>
</view>
<view class="talking-cont last-cont" v-if=" [5].indexOf(type) >= 0 && !dataInfo.isLoading">
<view class="talking-text">
{{ changeBr(dataInfo.suggestContent || '--') }}
</view>
</view>
</view>
</view>
<view class="ai-answer" v-if="[7].indexOf(type) >= 0">
<view class="user-info">
<view class="avatar-box">
<ImagePreview class="img-box" :imgId="dataInfo?.userInfo?.imgAddr"></ImagePreview>
</view>
<view class="ai-name">
{{ dataInfo?.userInfo?.teacherName }}
<view class="ai-name-desc">{{dataInfo?.userInfo?.teacherDesc}}</view>
</view>
</view>
<view :class="['talking-info', [4,7].indexOf(type) >= 0 ? 'talking-info-less':''] ">
<view class="talking-cont">
<view class="talking-text">
{{ showContent }}
</view>
</view>
</view>
</view>
<view class="user-answer" v-else-if="[1, 2].indexOf(type) >= 0">
@@ -162,7 +190,6 @@ import ChartThree from "./chartThree.vue"
import common from '@/common/common';
import { getUserInfo } from '@/common/common';
import { useStorageStore } from "@/store/storage.js"
import { demoUsage } from "@/api/arraybuffer.js"
import { onUnload } from '@dcloudio/uni-app'
const storageStore = useStorageStore()
@@ -249,6 +276,7 @@ const audioCacheObj = computed(() => storageStore.audioObj)
else if(props.type === 4) return unref(dataInfo)?.chatContent || unref(dataInfo)?.qnsContent
else if(props.type === 5) return unref(dataInfo)?.chatContent || unref(dataInfo)?.evalContent
else if(props.type === 0) return unref(dataInfo)?.chatContent || unref(dataInfo)?.talkingContent
else return unref(dataInfo)?.chatContent || unref(dataInfo)?.talkingContent
}
})
const showContent = computed(() => {
@@ -256,13 +284,10 @@ const audioCacheObj = computed(() => storageStore.audioObj)
return _str.replace(/\/n/g,"\n")
})
onMounted(()=>{
// demoUsage({
// question: unref(dataInfo)?.pgrphContent,
// streaming: true
// })
// if(props.isTransform){}
})
const changeBr = (str = '') => {
return str.replace(/\/n/g,"\n")
}
onUnload(()=>{
pauseVoice()
})
@@ -392,6 +417,9 @@ const audioCacheObj = computed(() => storageStore.audioObj)
const goOnRestart = () => {
emit('nextQuestion', unref(dataInfo), 'reStart')
}
const goOnRestartPrgh = () => {
emit('nextQuestion', unref(dataInfo), 'reStartPrgh')
}
// 撤回
const withdraw = (data) => {
emit('withdraw',data)
@@ -406,6 +434,7 @@ const audioCacheObj = computed(() => storageStore.audioObj)
.ai-answer{
width: 100%;
overflow: hidden;
margin-bottom: 20rpx;
.user-info{
overflow: hidden;
.avatar-box{
@@ -440,7 +469,7 @@ const audioCacheObj = computed(() => storageStore.audioObj)
border-radius: 15rpx;
background-color: #fff;
overflow: hidden;
padding-bottom: 40rpx;
padding-bottom: 30rpx;
&.talking-info-less{
float: left;
min-width: 400rpx;
@@ -458,9 +487,15 @@ const audioCacheObj = computed(() => storageStore.audioObj)
}
}
.talking-cont{
margin-top: 16rpx;
margin: 16rpx 0;
overflow: hidden;
min-height: 48rpx;
&.last-cont{
margin-bottom: 0;
.talking-text{
margin-bottom: 0;
}
}
.talking-text{
font-size: 28rpx;
line-height: 48rpx;
@@ -584,6 +619,7 @@ const audioCacheObj = computed(() => storageStore.audioObj)
.user-answer{
overflow: hidden;
width: 100%;
margin-bottom: 20rpx;
.user-info{
overflow: hidden;
.avatar-box{
@@ -620,6 +656,7 @@ const audioCacheObj = computed(() => storageStore.audioObj)
padding: 20rpx;
border-radius: 15rpx;
background-color: #06f;
padding-bottom: 30rpx;
color: #fff;
.talking-gif{
width: 200rpx;
+20 -17
View File
@@ -4,6 +4,7 @@
<view class="bot-bg"></view>
<view class="course-center-body">
<view class="body-title">
<!-- @click="test.testApi" -->
<uv-icon class="arrow-icon" name="arrow-left" @click="common.navigateBack()"/>
课程中心
</view>
@@ -28,7 +29,8 @@
<uv-icon class="filter-icon" name="list"/>
</view>
<view :class="['filter-list',showFilterList?'show':'']">
<view class="filter-type" v-for="item in tagsTree.filter(t=>(t.children||[]).length>0)" :key="item.value">
<view class="filter-type" v-for="item in tagsTree" :key="item.value">
<!-- .filter(t=>(t.children||[]).length>0) -->
<view class="filter-type-title">{{item.label}}</view>
<view class="filter-type-list">
<view :class="['filter-type-item', activeTag===itemT.value?'is-active':'']" v-for="itemT in (item.children||[])" @click="chooseTag(itemT)">
@@ -46,7 +48,7 @@
:type="activeTag === ''?'primary':'info'"
@click="chooseTag({value:''})"
></uv-tags>
<uv-tags :text="item.label"
<uv-tags :text="item.label+item.required"
v-for="item in tagsList"
:key="item.value"
shape="circle"
@@ -112,17 +114,17 @@
</template>
<script setup>
import CourseItem from "@/components/course-item/course-item.vue"
import common from '@/common/common'
import {
getPrdLineTypeListApi,
getTraTagListApi,
getCourseTypeListApi,
queryCrsInfoByCatalogApi,
queryCrsInfoByPrdLineApi,
queryCrsInfoByLastnewApi,
queryCrsInfoByPopularApi,
queryCrsInfoByRecmdApi
import CourseItem from "@/components/course-item/course-item.vue"
import common from '@/common/common'
import {
getPrdLineTypeListApi,
getTraTagListApi,
getCourseTypeListApi,
queryCrsInfoByCatalogApi,
queryCrsInfoByPrdLineApi,
queryCrsInfoByLastnewApi,
queryCrsInfoByPopularApi,
queryCrsInfoByRecmdApi
} from "@/api/course.js"
import { onMounted, ref, computed } from 'vue'
const customStyle = {
@@ -185,14 +187,14 @@ import { onMounted, ref, computed } from 'vue'
let _tarr = t.children || []
_arr = _arr.concat(_tarr)
})
return _arr.sort((a,b)=>{
a.required == "Y" && b.required === 'N'
})
let _reqList = _arr.filter(t=>t.required === "Y")
let _reqListN = _arr.filter(t=>t.required === "N")
return _reqList.concat(_reqListN)
})
const courseListBase = ref([])
const courseList = computed(()=>{
return activeTag.value
?courseListBase.value.filter(t=>t.tagCataList.map(k=>k.tagId).indexOf(activeTag.value)>=0)
?courseListBase.value.filter(t=>(t.tagCataList||[]).map(k=>k.tagId).indexOf(activeTag.value)>=0)
:courseListBase.value
})
// 整理树形
@@ -293,6 +295,7 @@ import { onMounted, ref, computed } from 'vue'
})
</script>
<style lang="scss" scoped>
.course-center{
position: relative;
+97 -37
View File
@@ -45,6 +45,9 @@
<uv-icon class="arrow-icon" name="arrow-left" @click="common.navigateBack()"/>
课程学习
</view>
<view class="study-num">
<view class="color-blue">{{ showOrder }}</view> / {{ pgrphNum }}
</view>
<scroll-view
class="talking-list"
:scroll-y="true"
@@ -53,9 +56,6 @@
: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"
@@ -148,13 +148,14 @@ const inputRef = ref('')
const flagVal = ref('1')
// 初始化数据
const getData = async (flag) => {
if(flag === flagVal.value){
flagVal.value = '2'
}else{
return
}
// 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,
@@ -164,6 +165,19 @@ const getData = async (flag) => {
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) {
// 继续学习 获取学习历史接口
@@ -279,21 +293,23 @@ const pgrphNum = ref(1)
const showOrder = ref(0)
const isLastPgrph = ref(false)
// 获取段落
const getGraphInfoUnStudy = () => {
if(isLastPgrph.value){
common.msg('您已学完全部知识点!')
return
}
textChartApi({
processType: 'PRGH-STDY',
requestBody: JSON.stringify({
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 _id = new Date().getTime() + 'id'
let _body = res.body|| {}
console.log(_body)
if(!_body.chatBody) {
return
}
@@ -391,10 +407,17 @@ const nextQuestionSuccess = (info, type='next') => {
scrollToBottomNow(0)
getQuestionInfoUnStudy(info.pgrphId)
})
}else if( type === 'reStart'){
}else if(type === 'reStart'){
talkingList.value.push({
crsId: info.crsId,
pgrphId: info.pgrphId,
talkingContent: '重新学',
type: 0,
id: _id
})
// 重新回答
textChartApi({
processType: 'NEW-ANSER',
processType: 'NEW-ANSER',//NEW-KNOW
requestBody: info.qstLogId
}).then(res=>{
let _body = res.body|| {}
@@ -416,6 +439,40 @@ const nextQuestionSuccess = (info, type='next') => {
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()
@@ -453,7 +510,7 @@ const initsocketTask = () => {
stdyId.value = body.stdyId || ''
stdyBatch.value = body.stdyBatch || ''
logId.value = body.logId || ''
getGraphInfoUnStudy()
getGraphInfoUnStudy(1)
}
}else{
if(['0005', 'QQ0005'].includes(res.rtnCode)) {
@@ -472,19 +529,20 @@ const sendMessage = (data) => {
unref(SocketObj).send(data)
}
// ws 结束
const startPgrphId = ref('')
onLoad((params) => {
crsId.value = params.crsId||'';
crsNum.value = params.crsNum||'';
startPgrphId.value = params.pgrphId||'';
});
const showTalkingModel = ref(false)
// 录音
onMounted(()=>{
getData('1');
})
onShow(()=>{
getData('2');
})
// onShow(()=>{
// getData('2');
// })
onUnload(()=>{
changePlayItemId()
teacherVoiceObj?.stop?.()
@@ -771,28 +829,30 @@ const chooseRateNow = (item) => {
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: 30rpx;
padding: 10rpx 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;
}
@@ -4,7 +4,7 @@
<view class="blue-ball">
<view class="ball"></view>
</view>
<view class="item-label">
<view class="item-label" @click="toDetail(item)">
<view class="fz-30 font_pf fw-400 mb-30">知识点0{{ index + 1 }}</view>
<view class="fz-30 font_pf fw-400 mb-36">{{ item.pgrphContent }}</view>
</view>
@@ -20,12 +20,14 @@
import common from '@/common/common';
const emit = defineEmits(['toStudyPrgh'])
const props = defineProps({
modelValue: Object
})
const toDetail = (item) => {
emit('toStudyPrgh', item)
}
</script>
<style lang="scss" scoped>
+4 -1
View File
@@ -34,7 +34,7 @@
<introduceVue v-model="form"></introduceVue>
</view>
<view class="point" v-if="tabAct == 1">
<knowledge v-model="form"></knowledge>
<knowledge v-model="form" @toStudyPrgh="toStudyPrgh"></knowledge>
</view>
<view class="evaluate" v-if="tabAct == 2">
<comment ref="commentRef" :crsNum="form.crsNum" :imgId="form.imgId"></comment>
@@ -107,6 +107,9 @@
commentRef.value.getData();
}
});
const toStudyPrgh = (info)=>{
common.navigateTo('/pages/course/study?crsNum='+crsNum.value+'&crsId='+form.value.crsId+'&pgrphId=' + info.pgrphId)
}
</script>
<style lang="scss" scoped>