合并冲突

This commit is contained in:
杨航
2025-09-09 10:16:39 +08:00
12 changed files with 358 additions and 75 deletions
+5 -7
View File
@@ -2,11 +2,8 @@
ENV = 'development'
# 'development'
# UAT
# VITE_APP_BASE_API_Url = 'http://25.18.122.78:9786'
# DEV
VITE_APP_BASE_API_Url = 'https://aitstest.jlbank.com.cn:7001'
# app入口
#VITE_APP_BASE_API_Url = 'http://25.16.122.65:7001'
@@ -17,12 +14,12 @@ VITE_APP_BASE_API_Url = 'https://aitstest.jlbank.com.cn:7001'
# VITE_APP_BASE_H5_API_Url = 'http://25.16.122.65:7001'
# VITE_APP_BASE_H5_API_Url = 'http://25.18.122.21:9786'
# VITE_APP_BASE_H5_API_Url = 'https://aitstest.jlbank.com.cn:7002'
VITE_APP_BASE_H5_API_Url = 'https://aitstest.jlbank.com.cn:7001'
# dev
VITE_APP_BASE_H5_API_Url = 'http://25.18.122.65:7001'
#VITE_APP_BASE_H5_API_Url = 'http://25.18.122.65:7001'
# sit
# VITE_APP_BASE_H5_API_Url = 'http://25.18.122.91:9786'
#VITE_APP_BASE_H5_API_Url = 'http://25.18.122.91:9786'
# UAT
# VITE_APP_BASE_H5_API_Url = 'http://25.18.122.78:9786'
@@ -44,3 +41,4 @@ VITE_APP_BASE_H5_API_Url_TRASTUDY = 'http://25.64.16.130:9602'
# 付丙鑫
# VITE_APP_BASE_H5_API_Url_TRASTUDY = 'http://25.64.16.145:9602'
+2
View File
@@ -6,6 +6,7 @@ import common from '@/common/common.js'
const ENV = import.meta.env
export const get_base_url = (url = '') => {
if (ENV.MODE === 'development') { // 开发环境
// #ifdef H5
if (url) {
@@ -21,6 +22,7 @@ export const get_base_url = (url = '') => {
// #endif
} else { // 其他环境,包括生产环境,sit环境,uat环境
return ENV.VITE_APP_BASE_API_Url
}
}
+25 -25
View File
@@ -80,7 +80,9 @@
<script setup>
import { computed, unref, ref, toRefs, watch, onMounted, nextTick } from 'vue';
import { onUnload } from '@dcloudio/uni-app';
import { useAudioStore } from '@/store/audioStore';
// import { useinnerAudio, useAudioFun } from '@/store/innerAudio';
import { useAudio } from './useAudio.js';
import {downloadFile} from '@/api/common.js'
const props = defineProps({
dataInfo: {
@@ -124,8 +126,9 @@
type: Boolean
}
});
const audioStore = useAudioStore();
const { dataInfo } = toRefs(props);
const innerAudio = useAudio();
const status = computed(() => props.status);
watch(
() => props.status,
@@ -142,7 +145,7 @@
return props.text || props.dataInfo.anserResult || '';
});
const isPlaying = ref(false);
const isPlaying = innerAudio.isPlaying;
const textLoading = ref(false);
const textShow = ref(props.defaultDisplayText); // 翻译文字展示状态
const voiceTime = ref('');
@@ -170,57 +173,54 @@
textShow.value = false;
}
};
const initVoice = () => {
innerAudioContext.onEnded(() => {
innerAudioContext.src = '';
});
innerAudioContext.onError(() => {
innerAudioContext.src = '';
});
};
const playVoice = () => {
// isPlaying.value = true;
console.log('点击播放');
audioStore.playAudio();
innerAudio.playAudio();
};
let unsubscribe = () => {};
onMounted(() => {
// 2. 监听 Store 中 isPlaying 的变化,实时同步
unsubscribe = audioStore.$subscribe((mutation, state) => {
isPlaying.value = state.isPlaying;
});
// unsubscribe = innerAudio.$subscribe((mutation, state) => {
// isPlaying.value = state.isPlaying;
// });
// 注册播放完成回调
audioStore.onAudioEnded(() => {
innerAudio.onAudioEnded(() => {
console.log('播放完成(通过回调)');
setTimeout(() => {
isPlaying.value = false;
}, 60);
});
// 注册播放错误回调
audioStore.onAudioError((error) => {
innerAudio.onAudioError((error) => {
console.error('播放错误(通过回调):', error);
});
// 声音加载完毕
audioStore.onAudioLoaded((e) => {
innerAudio.onAudioLoaded((e) => {
console.log('声音加载完毕', e.duration);
// 计算原始时长
const originalTime = Math.ceil(e.duration) || 1;
// 判断是否在118-123秒范围内
const displayTime = originalTime >= 118 && originalTime <= 124 ? 120 : originalTime;
// 赋值显示
console.log('displayTime', displayTime);
voiceTime.value = displayTime + 's';
console.log('displayTime', voiceTime.value);
});
watch(
() => props.dataInfo.voicePath,
(newVal) => {
console.log('newVal', newVal);
if(newVal === '' && props.dataInfo.ossKey) { // 去下载oss内容
console.log('去下载oss内容');
downloadFile(props.dataInfo.ossKey).then(res => {
audioStore.setAudioSrc(res.tempFilePath);
innerAudio.setAudioSrc(res.tempFilePath);
})
} else if(newVal === undefined){
innerAudio.stopAudio();
} else {
audioStore.setAudioSrc(newVal);
innerAudio.setAudioSrc(newVal);
}
},
{ deep: true, immediate: true }
@@ -228,9 +228,9 @@
});
onUnload(() => {
// 1. 停止当前播放(优先处理,避免音频继续播放)
audioStore.stopAudio();
innerAudio.stopAudio();
// 2. 移除当前组件注册的回调(避免内存泄漏)
audioStore.removeCallbacks();
innerAudio.removeCallbacks();
unsubscribe();
});
+3 -2
View File
@@ -78,7 +78,7 @@
<view
class="analysis_div"
v-if="!clearResult && ['study', 'mistake', 'settlement', 'practice'].includes(props.mode)"
v-if="!clearResult && ['study', 'mistake', 'settlement', 'practice', 'practice_record'].includes(props.mode)"
>
<view class="analysis_title" v-if="qnsTyp !== 'ES'">
<view class="success_answer">正确答案{{ correctResultLabel.join('') }}</view>
@@ -141,7 +141,8 @@
// 错题本: mistake
// 学习: study
// 考试结算页 settlement
return ['examination', 'practice', 'mistake', 'study', 'settlement'].includes(value);
// 练习记录页 practice_record
return ['examination', 'practice', 'mistake', 'study', 'settlement', 'practice_record'].includes(value);
}
},
// 是否预览模式
+220
View File
@@ -0,0 +1,220 @@
import {
computed,
reactive,
nextTick,
ref
} from 'vue';
// const innerAudioContext = uni.createInnerAudioContext();
// // 当前播放状态
const audioStopFun = ref(null)
export const useAudio = () => {
const audioInstance = uni.createInnerAudioContext();
const callbacks = reactive({
onEnded: null, // 播放完成回调
onError: null, // 播放错误回调
onLoaded: null, // 音频加载完成回调(可获取时长)
onStop: null
})
const isPlaying = ref(false)
// 加载状态
const isLoading = ref(false)
// 当前播放的音频地址
const currentSrc = ref('')
const errorMsg = ref('')
// 初始化音频实例(确保全局唯一)
const initAudio = () => {
bindAudioEvents();
}
const bindAudioEvents = () => {
if (!audioInstance) return;
// 播放开始事件
audioInstance.onPlay(() => {
isPlaying.value = true;
isLoading.value = false;
errorMsg.value = '';
});
audioInstance.onStop(() => {
console.log('音频停止事件');
isPlaying.value = false;
isLoading.value = false;
// 清空地址避免残留
// 触发自定义回调
if (callbacks.onStop && typeof callbacks.onStop === 'function') {
callbacks.onStop();
}
});
// 播放结束事件
audioInstance.onEnded(() => {
console.log('播放结束事件');
// 清空地址避免残留
// if (audioInstance) {
// audioInstance.src = '';
// }
// 触发自定义回调
if (callbacks.onEnded && typeof callbacks.onEnded === 'function') {
callbacks.onEnded();
}
});
// 音频可以播放时触发(此时通常能获取到时长)
audioInstance.onCanplay(() => {
// 尝试获取时长(部分环境需要延迟一点)
// console.log('尝试获取时长(部分环境需要延迟一点)');
isLoading.value = false;
setTimeout(() => {
const duration = audioInstance.duration || 0;
console.log('尝试获duration', duration);
if (duration > 0) {
// 触发加载完成回调(此时已获取时长)
if (callbacks.onLoaded && typeof callbacks.onLoaded === 'function') {
callbacks.onLoaded({
duration: duration
});
}
}
}, 100); // 短暂延迟确保时长已加载
});
// 错误事件
audioInstance.onError((err) => {
isPlaying.value = false;
isLoading.value = false;
console.error('音频错误:', err);
if (callbacks.onError && typeof callbacks.onError === 'function') {
callbacks.onError(err); // 把错误信息传给外部
}
});
}
// 新增:预设置音频地址并加载(此时不播放,仅获取时长)
const setAudioSrc = (src) => {
console.log('预设置音频地址并加载', src);
if (!src || src === currentSrc.value) return; // 地址不变则不重复加载
initAudio();
isLoading.value = true;
currentSrc.value = src;
errorMsg.value = '';
try {
// 先停止当前播放并清空旧地址
// audioInstance.stop();
// audioInstance.src = '';
// 设置新地址并加载(不自动播放)
audioInstance.src = src;
// audioInstance.load(); // 手动触发加载
} catch (err) {
isLoading.value = false;
errorMsg.value = `设置音频失败: ${err.message}`;
}
}
// 修改:播放当前已设置的音频(需先调用setAudioSrc
const playAudio = async (src) => {
console.log('播放当前已设置的音频', currentSrc.value);
if (!currentSrc.value) {
errorMsg.value = '请先设置音频地址';
return;
}
// 已在播放则暂停
console.log('已在播放则暂停', isPlaying.value);
const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms));
if (isPlaying.value) {
pauseAudio();
return;
}
console.log('当前是是否暂停或停止状态', audioStopFun.value?.paused);
if (!audioStopFun.value?.paused) { // 当前是是否暂停或停止状态,true 表示暂停或停止,false 表示正在播放
audioStopFun.value?.stop();
wait(100)
}
// 执行播放
try {
// 哪个函数的实例正在播放,就把他挂在全局
audioStopFun.value = audioInstance
audioInstance.play();
isPlaying.value = true;
isLoading.value = false;
} catch (err) {
isLoading.value = false;
errorMsg.value = `播放失败: ${err.message}`;
}
}
// 暂停播放
const pauseAudio = () => {
if (audioInstance && isPlaying.value) {
console.log('暂停播放');
audioInstance.pause();
isPlaying.value = false;
}
}
// 停止播放并重置状态
const stopAudio = () => {
if (audioInstance && isPlaying.value) {
audioInstance.stop();
// todo
}
}
// 移除所有回调(组件卸载时调用)
const removeCallbacks = () => {
callbacks.onEnded = null;
callbacks.onError = null;
callbacks.onLoaded = null;
callbacks.onStop = null;
}
// 注册播放完成回调
const onAudioEnded = (callback) => {
if (typeof callback === 'function') {
callbacks.onEnded = callback;
}
}
// 注册播放错误回调
const onAudioError = (callback) => {
if (typeof callback === 'function') {
callbacks.onError = callback;
}
}
// 新增:注册音频加载完成回调(用于获取时长)
const onAudioLoaded = (callback) => {
if (typeof callback === 'function') {
callbacks.onLoaded = callback;
}
}
const onAudioStop = (callback) => {
if (typeof callback === 'function') {
callbacks.onStop = callback;
}
}
return {
isPlaying,
playAudio,
pauseAudio,
stopAudio,
setAudioSrc,
removeCallbacks,
onAudioEnded,
onAudioError,
onAudioLoaded,
onAudioStop
}
}
@@ -10,7 +10,7 @@
<scroll-view :scroll-y="true" class="scroll scroll-Y" :scroll-with-animation="true" :show-scrollbar="false">
<view class="main-div">
<view class="content" :id="'id_' + index" v-for="(item, index) in topicList">
<Subject :item="item" mode="settlement" :clearResult="false" :isPreview="true"></Subject>
<Subject :item="item" mode="practice_record" :clearResult="false" :isPreview="true"></Subject>
</view>
</view>
</scroll-view>
+4 -3
View File
@@ -299,9 +299,10 @@
result.papersId = e.papersId;
flagQueryDetail.value = e.flagQueryDetail;
flagQuery.value = e.flagQuery;
if (flagQueryDetail.value === 'Y' || flagQuery.value === 'Y') {
get_result();
}
// if (flagQueryDetail.value === 'Y' || flagQuery.value === 'Y') {
// }
get_result();
});
// 页面卸载时清除定时器,终止请求
onUnload(() => {
@@ -40,7 +40,7 @@
</view>
<view class="list_small_item">
<image src="@/static/images/courseRecord/duration.png" class="icon_img"></image>
<view class="prompt">考试得分{{ item_list.examGrade }}</view>
<view class="prompt">考试得分{{ item_list.flagQuery === 'Y'?item_list.examGrade:'--' }}</view>
</view>
</view>
<view class="fl1"></view>
@@ -7,7 +7,7 @@
</view>
<view class="time">
<text class="color_3">起止时间</text>
<text class="color_class">{{ item.examStartTm }}{{ item.examEndTm }}</text>
<text class="color_class">{{ item.examStartTm.substr(0 , 10) }}{{ item.examEndTm.substr(0 , 10) }}</text>
</view>
<view class="num_div">
<text class="color_3">完成人数</text>
+5 -2
View File
@@ -61,15 +61,18 @@
.then((res) => {
total.value = res.total;
data_list.push(...res.body);
console.log('total.value', total.value);
})
.catch((res) => {
console.log('resres', res);
})
.catch((res) => {})
.finally(() => {
if (data_list.length >= total.value) {
status.value = 'nomore';
} else {
status.value = 'loadmore';
}
console.log(status.value);
});
}, 200);
};
+34 -4
View File
@@ -101,14 +101,14 @@
</view>
</view>
</view>
<view class="fixed_button_div">
<view class="fixed_button_div" @click="goto_examination()">
<uv-button
type="primary"
class="send_evaluate"
:custom-style="customStyle"
:throttleTime="300"
:hairline="false"
@click="goto_examination()"
:disabled="isTimeInvalid"
>
开始考试
</uv-button>
@@ -152,14 +152,44 @@
return sum + (item.tgtGrade || 0);
}, 0)
);
const isTimeInvalid = computed(() => {
// 检查detailData是否存在
const startTime = new Date(detailData.examStartTm);
const endTime = new Date(detailData.examEndTm);
const currentTime = new Date();
// 检查日期是否有效
if (isNaN(startTime.getTime()) || isNaN(endTime.getTime())) {
return true; // 时间格式无效时视为有效(或根据需求调整)
}
const current = currentTime.getTime();
const start = startTime.getTime();
const end = endTime.getTime();
// 未开始或已结束都返回true(不符合时间要求)
return current < start || current > end;
});
const goto_examination = async () => {
if(detailData.limitTyp === 'SG' && detailData.examStatText === 'Y') return common.msg('考试已完成');
if (detailData.limitTyp === 'SG' && detailData.examStatText === 'Y') return common.msg('考试已完成');
const startTime = new Date(detailData.examStartTm);
const endTime = new Date(detailData.examEndTm);
const currentTime = new Date(); // 当前时间
if (!isNaN(startTime.getTime()) && !isNaN(endTime.getTime())) {
const current = currentTime.getTime();
const start = startTime.getTime();
const end = endTime.getTime();
if (current < start) {
return common.msg('考试时间未开始');
} else if (current > end) {
return common.msg('考试时间已结束');
}
}
common.loading();
startTraExamPapersInfo({
examId: detailData.examId
})
.then((res) => {
setPageCache('examination', { ...res.body, examId: detailData.examId});
setPageCache('examination', { ...res.body, examId: detailData.examId });
common.navigateTo('/pages/examination/index');
})
.finally(() => {
+57 -29
View File
@@ -6,6 +6,7 @@ import {
} from 'vue';
export const useAudioStore = defineStore('audioStore', {
state: () => ({
// 音频实例(全局唯一)
audioInstance: null,
@@ -18,9 +19,9 @@ export const useAudioStore = defineStore('audioStore', {
// 错误信息
errorMsg: '',
callbacks: {
onEnded: null, // 播放完成回调
onError: null, // 播放错误回调
onLoaded: null // 音频加载完成回调(可获取时长)
onEnded: {}, // 播放完成回调
onError: {}, // 播放错误回调
onLoaded: {} // 音频加载完成回调(可获取时长)
}
}),
getters: {
@@ -35,18 +36,18 @@ export const useAudioStore = defineStore('audioStore', {
},
actions: {
// 初始化音频实例(确保全局唯一)
initAudio() {
initAudio(audioId) {
if (!this.audioInstance) {
// 创建实例
this.audioInstance = uni.createInnerAudioContext();
// 绑定事件监听
this.bindAudioEvents();
this.bindAudioEvents(audioId);
}
return this.audioInstance;
},
// 绑定音频事件(统一管理生命周期)
bindAudioEvents() {
bindAudioEvents(audioId) {
if (!this.audioInstance) return;
// 播放开始事件
@@ -60,8 +61,9 @@ export const useAudioStore = defineStore('audioStore', {
this.audioInstance.onEnded(() => {
this.resetAudioState();
// 触发自定义回调
if (this.callbacks.onEnded && typeof this.callbacks.onEnded === 'function') {
this.callbacks.onEnded();
if (this.callbacks.onEnded[audioId] && typeof this.callbacks.onEnded[audioId] ===
'function') {
this.callbacks.onEnded[audioId]();
}
});
@@ -76,10 +78,9 @@ export const useAudioStore = defineStore('audioStore', {
this.duration = duration;
this.isLoaded = true;
this.isLoading = false;
// 触发加载完成回调(此时已获取时长)
if (this.callbacks.onLoaded) {
this.callbacks.onLoaded({
if (this.callbacks.onLoaded[audioId] && typeof this.callbacks.onLoaded[
audioId] === 'function') {
this.callbacks.onLoaded[audioId]({
duration: this.duration
});
}
@@ -98,22 +99,22 @@ export const useAudioStore = defineStore('audioStore', {
this.isLoading = false;
this.errorMsg = `播放错误: ${err.errMsg || '未知错误'}`;
console.error('音频错误:', err);
if (this.callbacks.onError && typeof this.callbacks.onError === 'function') {
this.callbacks.onError(this.errorMsg); // 把错误信息传给外部
if (this.callbacks.onError[audioId] && typeof this.callbacks.onError[audioId] === 'function') {
this.callbacks.onError[audioId](this.errorMsg); // 把错误信息传给外部
}
});
},
// 新增:预设置音频地址并加载(此时不播放,仅获取时长)
setAudioSrc(src) {
setAudioSrc(src, audioId) {
console.log('预设置音频地址并加载(此时不播放,仅获取时长)', src);
if (!src || src === this.currentSrc) return; // 地址不变则不重复加载
this.initAudio();
this.initAudio(audioId);
this.isLoading = true;
this.isLoaded = false; // 重置加载状态
this.currentSrc = src;
this.errorMsg = '';
try {
// 先停止当前播放并清空旧地址
// this.audioInstance.stop();
@@ -133,7 +134,6 @@ export const useAudioStore = defineStore('audioStore', {
this.errorMsg = '请先设置音频地址';
return;
}
// 已在播放则暂停
console.log('已在播放则暂停', this.isPlaying);
if (this.isPlaying) {
@@ -201,30 +201,58 @@ export const useAudioStore = defineStore('audioStore', {
}
},
// 移除所有回调(组件卸载时调用)
removeCallbacks() {
this.callbacks.onEnded = null;
this.callbacks.onError = null;
this.callbacks.onLoaded = null;
removeCallbacks(audioId) {
this.callbacks.onEnded[audioId] = null;
this.callbacks.onError[audioId] = null;
this.callbacks.onLoaded[audioId] = null;
},
// 注册播放完成回调
onAudioEnded(callback) {
onAudioEnded(callback, audioId) {
if (typeof callback === 'function') {
this.callbacks.onEnded = callback;
this.callbacks.onEnded[audioId] = callback;
}
},
// 注册播放错误回调
onAudioError(callback) {
onAudioError(callback, audioId) {
if (typeof callback === 'function') {
this.callbacks.onError = callback;
this.callbacks.onError[audioId] = callback;
}
},
// 新增:注册音频加载完成回调(用于获取时长)
onAudioLoaded(callback) {
onAudioLoaded(callback, audioId) {
if (typeof callback === 'function') {
this.callbacks.onLoaded = callback;
this.callbacks.onLoaded[audioId] = callback;
}
},
}
});
});
export const useAudioFun = (audioId) => {
const audioStore = useAudioStore();
return {
// 播放:忽略res参数,直接使用绑定的audioId
playAudio: (res) => audioStore.playAudio(audioId),
// 停止:无参数,直接调用
stopAudio: () => audioStore.stopAudio(),
// 移除回调:需要组件key,绑定audioId
removeCallbacks: () => audioStore.removeCallbacks(audioId),
// 设置音频源:第一个参数是src,自动绑定audioId
setAudioSrc: (src) => audioStore.setAudioSrc(src, audioId),
// 注册结束回调:第一个参数是回调,第二个是组件key,自动绑定audioId
onAudioEnded: (callback) => audioStore.onAudioEnded(callback, audioId),
// 注册错误回调:同上
onAudioError: (callback) => audioStore.onAudioError(callback, audioId),
// 注册加载完成回调:同上
onAudioLoaded: (callback) => audioStore.onAudioLoaded(callback, audioId)
};
};