From 8e50a7f4187d4395b274f4b0cc21fadc997fc596 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E8=88=AA?= Date: Fri, 14 Nov 2025 17:36:42 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=99=AA=E7=BB=83=E9=A2=84=E8=A7=88?= =?UTF-8?q?=EF=BC=8C=E9=99=AA=E7=BB=83=E8=AE=B0=E5=BD=95=EF=BC=8C=E9=99=AA?= =?UTF-8?q?=E7=BB=83=E6=8A=A5=E5=91=8A=EF=BC=8C=E9=99=AA=E7=BB=83=E5=AF=B9?= =?UTF-8?q?=E8=AF=9D=E7=AD=89=E8=81=94=E8=B0=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.development.yh | 2 +- src/api/preview.js | 8 + src/api/traRecord.js | 58 +++ src/pages.json | 9 + src/pages/course/components/chartSeven.vue | 1 - src/pages/course/components/chartSix.vue | 1 - src/pages/me/index.vue | 2 +- .../preview/components/previewAiSparring.vue | 89 +++-- src/pages/sparring/components/answer.vue | 212 ++++++++-- .../sparring/components/question-preview.vue | 248 ++++++++++++ src/pages/sparring/components/question.vue | 49 ++- .../sparring/components/talking-item.vue | 13 +- .../components/talking-preview-item.vue | 180 +++++++++ src/pages/sparring/detail.vue | 8 +- src/pages/sparring/result.vue | 367 ++++++++++++------ src/pages/sparring/tip.vue | 18 +- .../traRecord/components/examRecordItem.vue | 248 ++++++++++++ .../traRecord/components/examRecordList.vue | 53 +++ .../components/practiceRecordItem.vue | 250 ++++++++++++ .../components/practiceRecordList.vue | 53 +++ src/pages/traRecord/index.vue | 161 ++++++++ src/pages/traRecord/useItemList.js | 112 ++++++ src/pagesB.json | 11 +- 23 files changed, 1935 insertions(+), 218 deletions(-) create mode 100644 src/api/traRecord.js create mode 100644 src/pages/sparring/components/question-preview.vue create mode 100644 src/pages/sparring/components/talking-preview-item.vue create mode 100644 src/pages/traRecord/components/examRecordItem.vue create mode 100644 src/pages/traRecord/components/examRecordList.vue create mode 100644 src/pages/traRecord/components/practiceRecordItem.vue create mode 100644 src/pages/traRecord/components/practiceRecordList.vue create mode 100644 src/pages/traRecord/index.vue create mode 100644 src/pages/traRecord/useItemList.js diff --git a/.env.development.yh b/.env.development.yh index 943c5717..0bc26d12 100644 --- a/.env.development.yh +++ b/.env.development.yh @@ -6,5 +6,5 @@ VITE_APP_BASE_API_Url = 'http://25.18.122.65:7001' # VITE_APP_BASE_H5_API_Url = 'http://25.18.122.78:9786' # VITE_APP_BASE_H5_API_Url_TRAEXAM = 'http://25.64.32.154:9604' -# VITE_APP_BASE_H5_API_Url_TRAPRACTICE = 'http://25.64.32.154:9603' +VITE_APP_BASE_H5_API_Url_TRAPRACTICE = 'http://25.64.32.156:9603' diff --git a/src/api/preview.js b/src/api/preview.js index cbf1e63e..86eca834 100644 --- a/src/api/preview.js +++ b/src/api/preview.js @@ -10,3 +10,11 @@ export const queryTraStdyInfoPreviewPaging = (data) => { data }); }; +export const queryTraListPreviewPaging = (data) => { + return request({ + url: '/trapractice/traPartnerInfo/queryTraPartnerPreviewPaging', + method: 'post', + toastErrors: true, + data + }); +}; diff --git a/src/api/traRecord.js b/src/api/traRecord.js new file mode 100644 index 00000000..761429b5 --- /dev/null +++ b/src/api/traRecord.js @@ -0,0 +1,58 @@ +import request from '@/api/request' +const base_url = '/trapractice' + + +/**练习*/ +// 练习列表页 +export const queryPracticeRecordPaging = (data) => { + return request({ + url: base_url + '/traPartnerExecuteHis/queryTraPartnerExecutePaging', + method: 'post', + toastErrors: true, + data:{ + traType:'01', + ...data + } + }); +}; +// 练习每一项列表 +export const queryPracticeHis = (data) => { + return request({ + url: base_url + '/traPartnerExecuteHis/queryTraPartnerExecuteHisPaging', + method: 'post', + toastErrors: true, + data + }); +}; +// 练习详情 通过 exrId 查询 +export const queryPracticeRecordByExrId = (data) => { + return request({ + url: base_url + '/traCrsPractice/queryPracticeRecordByExrId', + method: 'post', + toastErrors: true, + data + }); +}; + +/**考试*/ +export const queryCrsExamRecordPaging = (data) => { + return request({ + url: base_url + '/traPartnerExecuteHis/queryTraPartnerExecutePaging', + method: 'post', + toastErrors: true, + data:{ + traType:'02', + ...data + } + }); +}; +// 考试每一项列表 +export const queryCrsExamHisByExamId = (data) => { + return request({ + url: base_url + '/traPartnerExecuteHis/queryTraPartnerExecuteHisPaging', + method: 'post', + toastErrors: true, + data + }); +}; + diff --git a/src/pages.json b/src/pages.json index 1348a422..9487bedc 100644 --- a/src/pages.json +++ b/src/pages.json @@ -510,6 +510,15 @@ "titleNView": false } } + }, + { + "path": "pages/traRecord/index", + "style": { + "navigationBarTitleText": "陪练记录页", + "app-plus": { + "titleNView": false + } + } } ], "tabBar": { diff --git a/src/pages/course/components/chartSeven.vue b/src/pages/course/components/chartSeven.vue index e937eb79..54ff061e 100644 --- a/src/pages/course/components/chartSeven.vue +++ b/src/pages/course/components/chartSeven.vue @@ -119,7 +119,6 @@ dataY: dataY + '%' }); } - console.log(vertices) return vertices; }, diff --git a/src/pages/course/components/chartSix.vue b/src/pages/course/components/chartSix.vue index 15860cf5..1b2fba48 100644 --- a/src/pages/course/components/chartSix.vue +++ b/src/pages/course/components/chartSix.vue @@ -119,7 +119,6 @@ dataY: dataY + '%' }); } - console.log(vertices) return vertices; }, diff --git a/src/pages/me/index.vue b/src/pages/me/index.vue index acca313e..9bc2598e 100644 --- a/src/pages/me/index.vue +++ b/src/pages/me/index.vue @@ -97,7 +97,7 @@ - + AI陪练记录 diff --git a/src/pages/preview/components/previewAiSparring.vue b/src/pages/preview/components/previewAiSparring.vue index ee9c7ad0..1a9b36d7 100644 --- a/src/pages/preview/components/previewAiSparring.vue +++ b/src/pages/preview/components/previewAiSparring.vue @@ -1,42 +1,67 @@ \ No newline at end of file diff --git a/src/pages/sparring/components/answer.vue b/src/pages/sparring/components/answer.vue index 823c525c..68b93536 100644 --- a/src/pages/sparring/components/answer.vue +++ b/src/pages/sparring/components/answer.vue @@ -5,16 +5,19 @@ + + + - - + + @@ -39,9 +42,20 @@ watch, toRefs } from 'vue' - import { onUnload } from '@dcloudio/uni-app'; + import { + onUnload + } from '@dcloudio/uni-app'; import MarkdownPreview from '@/components/markdown-preview/markdown-preview.vue' import common from '@/common/common' + import { + useStorageStore + } from '@/store/storage.js'; + import { + downloadFile, + downloadVoiceFile + } from '@/api/common.js' + + const storageStore = useStorageStore(); const emit = defineEmits(['handleEvents', 'changePlay']) const props = defineProps({ @@ -49,6 +63,10 @@ default: () => ({}), type: Object }, + roleInfo: { + default: () => ({}), + type: Object + }, isAnswering: { default: false, type: Boolean @@ -56,24 +74,31 @@ isLast: { default: false, type: Boolean - } + }, + activeVoiceId: { + default: '', + type: String + }, + isStudy: { + default: true, + type: Boolean + }, }) - const { isLast, dataInfo } = toRefs(props) - watch( - () => props.dataInfo, - () => {}, { - deep: true, - immediate: true - } - ) - + const { + isLast, + dataInfo, + roleInfo, + activeVoiceId, + isStudy + } = toRefs(props) const isPlaying = ref(false) + const voiceIsPlaying = ref(false) + const startPlay = ref(false) + const mdText = computed(() => { let _text = (props.dataInfo?.talkingText || '').replace(/\\n/g, `\n`); return _text }) - const fileList = computed(() => props.dataInfo?.talkingFiles || []) - const crsList = computed(() => props.dataInfo?.talkingCrsList || []) const activeNames = ref([1, 2]) const changeActive = val => { @@ -102,6 +127,22 @@ common.navigateTo('/pages/courseDetail/index?crsId=' + item.crsId) } const talkVoiceObj = ref(null) + const voiceLoading = ref(false) + const itemVoice = ref('') + // 重播 + const restartPlay = () => { + if (itemVoice.value) { + emit('changePlay', ''); + emit('changePlay', unref(dataInfo)?.id); + setTimeout(() => { + talkVoiceObj.value.src = itemVoice.value; + talkVoiceObj.value.seek(0); + talkVoiceObj.value.play(); + }, 100); + } else { + playVoice(); + } + }; const playVoice = (readType = 'pgth') => { let _content = mdText.value; if (!_content) { @@ -109,41 +150,91 @@ return; } isPlaying.value = true - voiceLoading.value = true; setTimeout(() => { - downloadFile(props.dataInfo?.talkingVoiceId) - .then((res) => { - if (res?.data) { - common.msg(res.data.message); - return; - } - voiceLoading.value = false; - itemVoice.value = res.tempFilePath; - talkVoiceObj.value.src = res.tempFilePath; + emit('changePlay', unref(dataInfo)?.id); + // readType 阅读内容类型pgrh段落/qns问题 + if (talkVoiceObj.value.src && talkVoiceObj.value.src === itemVoice.value) { + if (voiceIsPlaying.value) return + talkVoiceObj.value.play(); + voiceLoading.value = false; + return; + } + talkVoiceObj.value.src = ''; + if (itemVoice.value) { + setTimeout(() => { + talkVoiceObj.value.src = itemVoice.value; + if (voiceIsPlaying.value) return talkVoiceObj.value.play(); - }) - .catch((err) => { - voiceLoading.value = false; + }, 100); + } else { + voiceLoading.value = true; + let _params = { + chaId: unref(roleInfo)?.chaId || '', + execLogId: unref(dataInfo)?.execLogId || '', + }; + let _url = + '/trapractice/traPartnerChat/readContent?' + + Object.keys(_params) + .map((t) => { + return encodeURIComponent(t) + '=' + encodeURIComponent(_params[t]); + }) + .join('&'); + storageStore.getStorageById('audio', _url + _params.teachNo, (url) => { + if (url) { + voiceLoading.value = false; + itemVoice.value = url; + talkVoiceObj.value.src = url; + if (voiceIsPlaying.value) return + talkVoiceObj.value.play(); + } else { + downloadVoiceFile(_url) + .then((res) => { + if (res?.data) { + common.msg(res.data.message); + return; + } + voiceLoading.value = false; + itemVoice.value = res.tempFilePath; + talkVoiceObj.value.src = res.tempFilePath; + if (voiceIsPlaying.value) return + talkVoiceObj.value.play(); + storageStore.setStorage('audio', _url + _params.teachNo, itemVoice.value); + }) + .catch((err) => { + voiceLoading.value = false; + emit('changePlay', ''); + }); + } }); + + } }, 100); }; const initVoice = () => { talkVoiceObj.value = uni.createInnerAudioContext() - talkVoiceObj.value.onEnded(()=>{ + talkVoiceObj.value.onEnded(() => { emit('changePlay', '') talkVoiceObj.value.src = '' + voiceIsPlaying.value = false }) - talkVoiceObj.value.onError(()=>{ + talkVoiceObj.value.onPause(() => { + voiceIsPlaying.value = false + }); + talkVoiceObj.value.onPlay(() => { + console.log('onPlay'); + startPlay.value = true + voiceIsPlaying.value = true + setTimeout(() => { + startPlay.value = false + }, 300) + emit('changePlay', unref(dataInfo)?.id); + }); + + talkVoiceObj.value.onError(() => { talkVoiceObj.value.src = '' + voiceIsPlaying.value = false }) - if(unref(dataInfo).intrctWay === '02'){ - itemVoice.value = unref(dataInfo).talkingVoice - talkVoiceObj.value.src = itemVoice.value - setTimeout(()=>{ - setVoiceTime(0) - },100) - } } const pauseVoice = () => { if (!talkVoiceObj.value) return @@ -158,18 +249,45 @@ pauseVoice() }) watch(() => props.dataInfo.talkingVoice, (val) => { - if (val) { + if (val && isStudy.value) { initVoice() } }, { deep: true, immediate: true }) + watch( + () => props.dataInfo, + (val) => { + if (val.execLogId && isStudy.value) { + playVoice() + } + }, { + deep: true, + immediate: true + } + ) + watch( + () => props.activeVoiceId, + (val) => { + if (val !== props.dataInfo.id) { + if (isPlaying.value) { + isPlaying.value = false + if (!talkVoiceObj.value) return + talkVoiceObj.value.pause() + } + } + }, { + deep: true, + immediate: true + } + ) \ No newline at end of file diff --git a/src/pages/sparring/components/question.vue b/src/pages/sparring/components/question.vue index 96121d50..ac76bc43 100644 --- a/src/pages/sparring/components/question.vue +++ b/src/pages/sparring/components/question.vue @@ -16,7 +16,7 @@ @@ -59,7 +59,6 @@ import { onUnload } from '@dcloudio/uni-app'; }) const { activeVoiceId, dataInfo } = toRefs(props) const emit = defineEmits(['changePlay']) - watch(() => props.dataInfo, () => {},{deep: true,immediate: true}) // 声音播放初始化 const talkVoiceObj = ref(null) @@ -78,14 +77,6 @@ import { onUnload } from '@dcloudio/uni-app'; onUnload(()=>{ pauseVoice() }) - watch(() => props.dataInfo.talkingVoice,(val)=>{ - if(val){ - initVoice() - } - },{ - deep: true, - immediate: true - }) const pauseVoice = () => { if(!talkVoiceObj.value) return talkVoiceObj.value.pause() @@ -95,7 +86,10 @@ import { onUnload } from '@dcloudio/uni-app'; const playVoice = () =>{ emit('changePlay', unref(dataInfo)?.id) if(talkVoiceObj.value && talkVoiceObj.value.src) { - talkVoiceObj.value.play() + setTimeout(()=>{ + talkVoiceObj.value.src = unref(dataInfo).talkingVoice + talkVoiceObj.value.play() + },300) return }else{ talkVoiceObj.value.src = '' @@ -110,6 +104,14 @@ import { onUnload } from '@dcloudio/uni-app'; } const initVoice = () => { talkVoiceObj.value = uni.createInnerAudioContext() + talkVoiceObj.value.onPause(()=>{ + }) + talkVoiceObj.value.onPlay(()=>{ + if(isTesting.value){ + talkVoiceObj.value.pause() + isTesting.value = false + } + }) talkVoiceObj.value.onEnded(()=>{ emit('changePlay', '') talkVoiceObj.value.src = '' @@ -117,14 +119,16 @@ import { onUnload } from '@dcloudio/uni-app'; talkVoiceObj.value.onError(()=>{ talkVoiceObj.value.src = '' }) - if(unref(dataInfo).intrctWay === '02'){ - itemVoice.value = unref(dataInfo).talkingVoice - talkVoiceObj.value.src = itemVoice.value - setTimeout(()=>{ - setVoiceTime(0) - },100) - } + itemVoice.value = unref(dataInfo).talkingVoice + talkVoiceObj.value.src = itemVoice.value + talkVoiceObj.value.play() + isTesting.value = true + talkVoiceObj.value.pause() + setTimeout(()=>{ + setVoiceTime(0) + },100) } + const isTesting= ref(false) const setVoiceTime = (sum) => { sum += 1 if(Math.ceil(talkVoiceObj.value.duration)>0){ @@ -139,6 +143,15 @@ import { onUnload } from '@dcloudio/uni-app'; },200) } } + watch(() => props.dataInfo, () => {},{deep: true,immediate: true}) + watch(() => props.dataInfo.talkingVoice,(val)=>{ + if(val){ + initVoice() + } + },{ + deep: true, + immediate: true + }) \ No newline at end of file diff --git a/src/pages/sparring/detail.vue b/src/pages/sparring/detail.vue index a89955e2..26db1305 100644 --- a/src/pages/sparring/detail.vue +++ b/src/pages/sparring/detail.vue @@ -60,7 +60,10 @@ @click="previewFile(item)"> {{ item.dataName }}.{{ item.dataSuffix }} - + + 预览陪练 + + 去练习 去考试 @@ -117,8 +120,11 @@ const toChooseRole = type => { common.navigateTo('/pages/sparring/chooseRole?traId=' + traId.value + '&type=' + type) } + const isPreview = ref(false) onLoad(params => { traId.value = params.traId + isPreview.value = params.isPreview === '1' + console.log(params) }) onMounted(() => { getDetailInfo() diff --git a/src/pages/sparring/result.vue b/src/pages/sparring/result.vue index d31bcc5a..dcdadc6b 100644 --- a/src/pages/sparring/result.vue +++ b/src/pages/sparring/result.vue @@ -23,36 +23,49 @@ - - + + - + + + - + 维度评价总览 {{item.gradeDimensName}}: {{ item.traGrade }} - + {{item.settDesc}} - + 得分 {{ totalScore }} {{1?'革命尚未成功,同志仍需努力!':'恭喜阁下过关斩将,顺利通关!'}} - + - + - - + + @@ -72,13 +85,18 @@ onLoad, onUnload } from '@dcloudio/uni-app'; + import common from '@/common/common' import Feedback from './components/feedback.vue'; import ChartFour from '@/pages/course/components/chartFour.vue'; import ChartThree from '@/pages/course/components/chartThree.vue'; import ChartFive from '@/pages/course/components/chartFive.vue'; import ChartSix from '@/pages/course/components/chartSix.vue'; import ChartSeven from '@/pages/course/components/chartSeven.vue'; - import { partnerChatReport } from "@/api/sparring.js" + import TalkingPreviewItem from './components/talking-preview-item.vue' + import ImagePreview from '@/components/image-preview/image-preview.vue'; + import { + partnerChatReport + } from "@/api/sparring.js" const traId = ref('') const execId = ref('') const feedbackRef = ref() @@ -87,8 +105,8 @@ const length = ref(6) const traInfo = ref({}) const traTm = ref('') - const tabList = ref([ - { + const activeVoiceTalkingItemId = ref('') + const tabList = ref([{ name: '全部', value: '' }, @@ -101,60 +119,135 @@ value: '02' } ]) - - // const evalDimens = [{ - // gradeDimensName: '维度1', - // anlyGrade: 60 - // }, { - // gradeDimensName: '维度2', - // anlyGrade: 80 - // }, { - // gradeDimensName: '维度3', - // anlyGrade: 30 - // }] + const showTalkList = computed(() => { + return allTalkList.value.slice(0, 2) + }) + const hideTalkList = computed(() => { + return allTalkList.value.slice(2) + }) + const allTalkList = computed(() => { + if (!filterType.value) { + return traPartnerExecuteAnswerRecords.value + } else if (filterType.value === '01') { + return traPartnerExecuteAnswerRecords.value.filter((t, index) => { + return t.talkingText.indexOf('#15b859') >= 0 || traPartnerExecuteAnswerRecords.value[index + 1] + ?.talkingText.indexOf('#15b859') >= 0 + }) + } else if (filterType.value === '02') { + return traPartnerExecuteAnswerRecords.value.filter((t, index) => { + return t.talkingText.indexOf('#d70c18') >= 0 || traPartnerExecuteAnswerRecords.value[index + 1] + ?.talkingText.indexOf('#d70c18') >= 0 + }) + } + }) + // 关闭播放状态 + const changePlayItemId = (id = '') => { + activeVoiceTalkingItemId.value = id + } + + const talkItemEvents = ({ + type, + data, + id, + info = {} + }) => { + switch (type) { + case 'changePlay': + activeVoiceTalkingItemId.value = id; + break; + case 'scrollToBottom': + break; + default: + break; + } + }; const feedbackNow = () => { feedbackRef.value.open({ traId: traId.value }); } + const filterType = ref('') const changeTab = (info) => { console.log(info) + filterType.value = info.value } const evalDimens = ref([]) const traPartnerExecuteAnswerRecords = ref([]) const totalScore = ref(0) const showLoading = ref(true) + const isPass = ref(false) + const negativeList = ref([]) // 劣质话术 + const positiveList = ref([]) // 劣质话术 const getData = () => { - // execId=db2f275b-c04a-4572-83dd-74a95ff49d67&traId=PARTNERINFO02501812207220251103160409000007402 - partnerChatReport({ - // traId: traId.value, - // execId: execId.value - traId: 'PARTNERINFO02501812207220251103160409000007402', - execId: 'db2f275b-c04a-4572-83dd-74a95ff49d67' - }).then(res=>{ - if(res.body.code === 200){ + traId: traId.value, + execId: execId.value + // traId: 'PARTNERINFO02501812207220251103160409000007402', + // execId: 'db2f275b-c04a-4572-83dd-74a95ff49d67' + }).then(res => { + if (res.body.code === 200) { setTimeout(getData, 2000) - }else{ - - traInfo.value = res.body.traPartnerInfo - roleInfo.value = res.body.traPartnerCharacterInfoVo - traTm.value = res.body.traTm - evalDimens.value = res.body.traPartnerSettlement.map(t=>{ + } else { + traInfo.value = res.body.traPartnerInfo || {} + roleInfo.value = res.body.traPartnerCharacterInfoVo || {} + traTm.value = res.body.traTm || '0' + isPass.value = res.body.traPartnerExecuteHisVo?.traResult === 'P' + totalScore.value = res.body.traPartnerExecuteHisVo?.traGrade || '0' + positiveList.value = res.body.positiveList || [] + negativeList.value = res.body.negativeList || [] + let _evalDimens = res.body.traPartnerSettlement || [] + evalDimens.value = _evalDimens.map(t => { return { ...t, anlyGrade: t.traGrade, - gradeDimensName: t.gradeDimensName||'维度' - + gradeDimensName: t.gradeDimensName || '维度' + } }) - traPartnerExecuteAnswerRecords.value = res.body.traPartnerExecuteAnswerRecords - setTimeout(()=>{ + let _talkingList = res.body.traPartnerExecuteAnswerRecords || [] + traPartnerExecuteAnswerRecords.value = _talkingList.map(t => { + let _content = setTextColor(t.content) + if (t.openStyle === '02') { + return { + ...t, + type: 'question', + talkingText: _content, + isLoading: false, + execLogId: t.execLogId || '', + intrctWay: '02', + id: t.execLogId, + ...t, + } + } else if (t.openStyle === '01') { + return { + ...t, + type: 'answer', + talkingText: _content, + isLoading: false, + execLogId: t.execLogId || '', + id: t.execLogId, + } + } else { + return t + } + }) + setTimeout(() => { showLoading.value = false - },800) + }, 800) } }) } + const setTextColor = (str) => { + let _str = str + positiveList.value.forEach(t => { + _str = _str.split(t).join(`${t}`) + }) + negativeList.value.forEach(t => { + _str = _str.split(t).join(`${t}`) + }) + console.log(_str) + return _str + } onLoad((params) => { traId.value = params.traId; execId.value = params.execId @@ -179,44 +272,46 @@ padding: calc(var(--status-bar-height) + 90rpx) 0 0rpx; // background-image: url(@/static/images/sparring/bg.png); // background-size: cover; - - + + } - .sparring-result-loading { - position: fixed; - z-index: 20; - height: 100vh; - width: 100vw; - background-image: url(@/static/images/sparring/bg.png); - - .loading-img { + + .sparring-result-loading { + position: fixed; + z-index: 20; + height: 100vh; + width: 100vw; + background-image: url(@/static/images/sparring/bg.png); + + .loading-img { + position: absolute; + width: 380rpx; + height: 540rpx; + top: 40%; + transform: translateY(-50%) translateX(-50%); + left: 50%; + padding-top: 540rpx; + line-height: 50rpx; + box-sizing: border-box; + text-align: center; + background-image: url(@/static/images/sparring/waiting.png); + background-repeat: no-repeat; + background-size: 380rpx 590rpx; + + &::after { + content: '报告生成中,请耐心等待 ~ '; + display: block; position: absolute; - width: 380rpx; - height: 540rpx; - top: 40%; - transform: translateY(-50%) translateX(-50%); - left: 50%; - padding-top: 540rpx; + left: 0%; + width: 100%; + bottom: -50rpx; + height: 50rpx; line-height: 50rpx; - box-sizing: border-box; text-align: center; - background-image: url(@/static/images/sparring/waiting.png); - background-repeat: no-repeat; - background-size: 380rpx 590rpx; - &::after{ - content: '报告生成中,请耐心等待 ~ '; - display: block; - position: absolute; - left: 0%; - width: 100%; - bottom: -50rpx; - height: 50rpx; - line-height: 50rpx; - text-align: center; - font-size: 28rpx; - } + font-size: 28rpx; } } + } .top-box { height: 90rpx; @@ -325,13 +420,56 @@ } .body-top-collepse-title { + padding-top: 10rpx; position: relative; - .collepse-tab{ - position: absolute; - width: 360rpx; - right: 120rpx; - top: 4rpx; + min-height: 100rpx; + .user-info { + overflow: hidden; + height: 92rpx; + + .avatar-box { + float: left; + width: 76rpx; + height: 76rpx; + border: 3rpx solid #fff; + // background-color: #f1f1f1; + overflow: hidden; + border-radius: 10rpx; + margin-right: 16rpx; + margin-bottom: 16rpx; + + .img-box { + width: 76rpx; + height: 76rpx; + overflow: hidden; + } + + } + + .ai-name { + float: left; + color: #666; + font-size: 25rpx; + line-height: 35rpx; + width: 140rpx; + white-space: pre-wrap; + + .ai-name-desc { + color: #999; + margin-left: 16rpx; + display: inline-block; + } + } } + + .collepse-tab { + position: absolute; + width: 300rpx; + right: 120rpx; + top: 0rpx; + z-index: 10; + } + .collepse-btn { position: absolute; right: 0; @@ -340,6 +478,7 @@ height: 46rpx; border: 2rpx solid #d4dce8; border-radius: 23rpx; + z-index: 10; &.show-all { &::after { @@ -399,25 +538,25 @@ // top: 2rpx; // left: 10rpx; // } - - line-height: 40rpx; - position: relative; - padding: 0rpx 46rpx 0rpx 26rpx; - line-height: 42rpx; - font-size: 29rpx; - margin-bottom: 16rpx; - - &::before { - content: ''; - display: block; - background-color: #000; - width: 12rpx; - height: 12rpx; - border-radius: 50%; - position: absolute; - left: 0rpx; - top: 14rpx; - } + + line-height: 40rpx; + position: relative; + padding: 0rpx 46rpx 0rpx 26rpx; + line-height: 42rpx; + font-size: 29rpx; + margin-bottom: 16rpx; + + &::before { + content: ''; + display: block; + background-color: #000; + width: 12rpx; + height: 12rpx; + border-radius: 50%; + position: absolute; + left: 0rpx; + top: 14rpx; + } } .item-cont { @@ -425,32 +564,37 @@ line-height: 50rpx; } } - &-static{ + + &-static { border-top: 2px solid #eee; padding-top: 38rpx; background-repeat: no-repeat; background-size: 270rpx 190rpx; background-position: right 40rpx; - - &.fialed{ + + &.fialed { background-image: url(@/static/images/sparring/def-res.png); } - &.pass{ + + &.pass { background-image: url(@/static/images/sparring/pass-res.png); } - .static-tip{ + + .static-tip { line-height: 44rpx; font-size: 32rpx; margin-bottom: 24rpx; font-weight: 600; } - .static-score{ + + .static-score { font-size: 66rpx; color: #06f; font-family: Arial, Helvetica, sans-serif; marign-bottom: 20rpx; } - .static-desc{ + + .static-desc { color: #999; font-size: 25rpx; margin-bottom: 32rpx; @@ -460,14 +604,15 @@ } .talk-item { - height: 200rpx; + // height: 200rpx; overflow: hidden; - background-color: #0f0; } + .my_tabs { + // 解决这个圆角的图片,开穿透 :deep(.uv-tabs__wrapper__nav__line) { - background: linear-gradient(90deg, #06f 0%, rgb(0,159,255) 50%, rgb(255,255,255) 100%) !important; + background: linear-gradient(90deg, #06f 0%, rgb(0, 159, 255) 50%, rgb(255, 255, 255) 100%) !important; } :deep(.uv-tabs__wrapper__nav__item) { diff --git a/src/pages/sparring/tip.vue b/src/pages/sparring/tip.vue index ab45330d..6b97136a 100644 --- a/src/pages/sparring/tip.vue +++ b/src/pages/sparring/tip.vue @@ -235,8 +235,8 @@ case 'changePlay': activeVoiceTalkingItemId.value = id; break; - case 'askTip': - // 获取问答提示 + case 'scrollToBottom': + scrollToBottomNow(); break; default: break; @@ -249,7 +249,8 @@ const answerText = ref('') const touchBtnCallBack = ref(null) const initsocketTask = () => { - socketStore.creatSocket('/trapractice/partnersocket/open?summary='); + let _src = talkingType.value === 'preview'?'/trapractice/partnersocket/open':'/trapractice/partnersocket/open' + socketStore.creatSocket(_src + '?summary='); // unref(SocketObj).on('open', (res)=>{ // if(!answerIsOver.value){ // sendMessage({ @@ -346,26 +347,31 @@ console.log('开始回答!'); answerIsOver.value = false; answerText.value = ''; - talkingList.value.pop(); detailInfo.chaName + talkingList.value.pop(); talkingList.value.push({ type: 'answer', talkingText: answerText.value, isLoading: false, + execLogId: body.execLogId||'', id: _id, ...body, askData: { ...askData.value }, - talkingFiles: [], - talkingCrsList: [] }); } else if (body.event === 'token') { answerIsOver.value = false; answerText.value += body.data; + console.log(answerText.value) + talkingList.value[talkingList.value.length - 1].talkingText = answerText.value; } else if (body.event === 'end') { console.log('完成回答!'); answerIsOver.value = true; + } else if (body.event === 'recordAi') { + console.log('推送声音id!'); + talkingList.value[talkingList.value.length - 1].execLogId = body.execLogId } + scrollToBottomNow(); return; } if (commond === 'ASK-RE-ANSWER') { diff --git a/src/pages/traRecord/components/examRecordItem.vue b/src/pages/traRecord/components/examRecordItem.vue new file mode 100644 index 00000000..8087220a --- /dev/null +++ b/src/pages/traRecord/components/examRecordItem.vue @@ -0,0 +1,248 @@ + + + + + diff --git a/src/pages/traRecord/components/examRecordList.vue b/src/pages/traRecord/components/examRecordList.vue new file mode 100644 index 00000000..e021485b --- /dev/null +++ b/src/pages/traRecord/components/examRecordList.vue @@ -0,0 +1,53 @@ + + + + diff --git a/src/pages/traRecord/components/practiceRecordItem.vue b/src/pages/traRecord/components/practiceRecordItem.vue new file mode 100644 index 00000000..c96a9e80 --- /dev/null +++ b/src/pages/traRecord/components/practiceRecordItem.vue @@ -0,0 +1,250 @@ + + + + + diff --git a/src/pages/traRecord/components/practiceRecordList.vue b/src/pages/traRecord/components/practiceRecordList.vue new file mode 100644 index 00000000..30bc5634 --- /dev/null +++ b/src/pages/traRecord/components/practiceRecordList.vue @@ -0,0 +1,53 @@ + + + + diff --git a/src/pages/traRecord/index.vue b/src/pages/traRecord/index.vue new file mode 100644 index 00000000..46388ad0 --- /dev/null +++ b/src/pages/traRecord/index.vue @@ -0,0 +1,161 @@ + + + + + diff --git a/src/pages/traRecord/useItemList.js b/src/pages/traRecord/useItemList.js new file mode 100644 index 00000000..2ff3c9b1 --- /dev/null +++ b/src/pages/traRecord/useItemList.js @@ -0,0 +1,112 @@ +import { + computed, + reactive, + nextTick, + ref +} from 'vue'; + + +// 计算两个标准时间的时间差值 +export const formatDuration = (startTm, endTm) => { + if (endTm === null || startTm === null) return "00:00:00"; + // 解析时间字符串为时间戳(毫秒) + const startTime = new Date(startTm).getTime(); + const endTime = new Date(endTm).getTime(); + + // 计算时间差(秒),确保为正数 + const seconds = Math.abs(Math.floor((endTime - startTime) / 1000)); + // 计算时、分、秒 + const hours = Math.floor(seconds / 3600); + const minutes = Math.floor((seconds % 3600) / 60); + const remainingSeconds = seconds % 60; + + // 补零格式化函数(统一处理所有单位) + const formatNumber = (num) => num.toString().padStart(2, '0'); + + // 小时、分钟、秒均保持两位数格式 + return `${formatNumber(hours)}:${formatNumber(minutes)}:${formatNumber(remainingSeconds)}`; +}; + +// 秒数转换成小时,带小数点的小时 +export const secondsToHours = (seconds) => { + if (typeof seconds === 'number') { + // 转换为小时并保留一位小数 + return (seconds / 3600).toFixed(1); + } + return '0.0'; +}; + +export function useItemList(fetchFunction, item) { + const status = ref('loadmore'); // loadmore - 加载前,loading - 加载中,nomore - 没有数据 + const limit = 5; + const page = ref(0); + const total = ref(-1); + const data_list = reactive([]); + const show_list_state = ref(false); + + const list_div_height = computed(() => { + const statusHeight = status.value === 'nomore' ? 0 : 100; + if (!show_list_state.value) { + return '0'; + } else if (status.value === 'loading') { + return data_list.length * 136 + statusHeight + 'rpx'; + } else { + return data_list.length * 136 + statusHeight + 'rpx'; + } + }); + // 点击获取详情 + const show_list_click = (num = -1) => { + if (num === 0) return; + show_list_state.value = !show_list_state.value; + if (show_list_state.value) { + nextTick(() => { + getData('first'); + }); + } + }; + + const getData = (from = '') => { + if (from === 'first') { + if (total.value !== -1) { + return; + } + status.value = 'loading'; + total.value = -1; + page.value = 1; + data_list.length = 0; + } else { + if (status.value !== 'loadmore' && status.value !== '') return; + status.value = 'loading'; + page.value++; + } + fetchFunction({ + page: page.value, + limit: limit + }) + .then((res) => { + total.value = res.total; + data_list.push(...res.body); + }) + .finally(() => { + if (data_list.length >= total.value) { + status.value = 'nomore'; + } else { + status.value = 'loadmore'; + } + }); + }; + const clear = () => { + total.value = -1 + data_list.length = 0 + show_list_state.value = false + } + return { + status, + data_list, + show_list_state, + list_div_height, + show_list_click, + getData, + clear + }; +} \ No newline at end of file diff --git a/src/pagesB.json b/src/pagesB.json index d1d11dc2..d4a3b69b 100644 --- a/src/pagesB.json +++ b/src/pagesB.json @@ -116,6 +116,15 @@ "titleNView": false } } - } + }, + { + "path": "pages/traRecord/index", + "style": { + "navigationBarTitleText": "陪练记录页", + "app-plus": { + "titleNView": false + } + } + }, ] } \ No newline at end of file