Merge branch 'dev-0120' into develop

This commit is contained in:
杨航
2026-01-08 08:37:31 +08:00
14 changed files with 1156 additions and 264 deletions
+1 -1
View File
@@ -5,7 +5,7 @@ import {
import showDialog from '@/common/dialogMessage'
import common from '@/common/common'
// #endif
function msg(title, duration = 1000) {
function msg(title, duration = 1500) {
uni.showToast({
title: title,
duration: duration,
+36 -14
View File
@@ -61,7 +61,7 @@
<!-- <RadioSubjectRequest v-if="[8].indexOf(type) >= 0 && dataInfo.qnsTyp !== 'ES'" :item="dataInfo" :mode="mode" @subject_click="subject_click" /> -->
<!-- 段落图片视频预览 -->
<view v-if="type === 3" style="overflow: hidden;padding-top: 10rpx;">
<ImagePreview class="cont-img-box" v-for="item in (dataInfo.imageAddrs || []) "
<ImagePreview :class="['cont-img-box',imgAddrs.length === 1?'cont-video-box':imgAddrs.length === 2?'cont-img-box2':'']" v-for="item in imgAddrs "
:imgId="item" :previewDetail="false" @click="previewImage(item)"></ImagePreview>
<VideoPreview class="cont-img-box cont-video-box" v-if="(dataInfo.vidoAddrs || []).length > 0"
:fileId="dataInfo.vidoAddrs[0].vidAddr"
@@ -328,7 +328,11 @@
};
});
const mode = ref('study'); // 考试 examination 练习practice 学习study
const imgAddrs = computed(()=>{
return (dataInfo.value.imageAddrs || [])
})
const previewFile = info => {
previewFileFnc({
fileId: info.fileId,
@@ -713,19 +717,30 @@
// }
};
const setVoiceTime = (sum) => {
sum += 1;
if (talkVoiceObj.value.duration) {
voiceTime.value = (Math.ceil(talkVoiceObj.value.duration) || 0) + 's';
} else {
if (sum === 100) {
voiceTime.value = (unref(dataInfo).voiceTime || 0) + 's';
return;
plus.io.getAudioInfo({
filePath: unref(dataInfo).talkingVoice,
success:(res)=>{
voiceTime.value = (Math.floor(res.duration) || 1) + 's'
},
fail:(res)=>{
voiceTime.value = (unref(dataInfo).voiceTime || 1) + 's'
}
setTimeout(() => {
setVoiceTime(sum);
}, 300);
}
};
})
}
// const setVoiceTime = (sum) => {
// sum += 1;
// if (talkVoiceObj.value.duration) {
// voiceTime.value = (Math.ceil(talkVoiceObj.value.duration) || 0) + 's';
// } else {
// if (sum === 100) {
// voiceTime.value = (unref(dataInfo).voiceTime || 0) + 's';
// return;
// }
// setTimeout(() => {
// setVoiceTime(sum);
// }, 300);
// }
// };
const playQnsVoice = (readType = 'qns', item) => {
if (type.value === 2) {
// 文本转语音
@@ -958,6 +973,13 @@
&:nth-child(3n){
margin-right: 0;
}
&.cont-img-box2{
width: calc(50% - 10rpx);
height: 200rpx;
&:nth-child(2n){
margin-right: 0rpx;
}
}
}
}
+257 -233
View File
@@ -1,7 +1,7 @@
<template>
<view class="talk-btns" ref="talkBtnRef">
<view class="touch-btn" @touchstart="getRecordPermission" @touchend="hideOverlayTalking" @touchmove="handleMove"
v-if="!keyboardIsShow">
@touchcancel="hideOverlayTalking" v-if="!keyboardIsShow">
按住说话
</view>
<view class="touch-btn" v-if="keyboardIsShow">
@@ -9,6 +9,7 @@
:adjustPosition="adjustPosition" placeholderStyle="color:#999999;font-size:26rpx"
placeholder="请输入文字"></uv-input>
</view>
<!-- @click="showTalkingModel = true" -->
<view class="talk-btn-box" v-show="!onlyVoice">
<image class="talk-btn-icon" v-show="!keyboardIsShow" src="/src/static/images/course/jianpan.png" mode=""
@click="changeBtn"></image>
@@ -17,25 +18,52 @@
<image class="talk-btn-icon" v-show="keyboardIsShow && !!answerValue" src="/src/static/images/course/send.png"
mode="" @click.stop="showKeyboard"></image>
</view>
<uni-popup ref="talkModelRef" type="bottom" @click="showTalkingModel = false"
mask-background-color="rgba(0,0,0,0.9)">
<!-- @click="showTalkingModel = false" -->
<!-- <uv-popup ref="talkModelRef" mode="bottom" overlay-style="#000000" overlay-opacity="0.9"
mask-background-color="rgba(0,0,0,0.9)" bg-color="none" z-index="10007"
@change="({show}) => {showTalkingModel = show}"> -->
<uni-popup ref="talkModelRef" type="bottom" :mask-click="false" :safe-area="true"
@mask-click="()=>{ console.log(1111)}" mask-background-color="rgba(0,0,0,0.9)">
<view class="overlay-box">
<view class="circle-bg-box">
<view class="icon-box">
<image src="/src/static/images/course/talk-icon.png" mode=""></image>
<view class="circle-bg-box no-bg" v-if="transVoiceIng">
</view>
<view class="circle-bg-box" v-else>
<view class="tips-box">
<!-- <image src="/src/static/images/course/talk-icon.png" mode=""></image> -->
<!-- <view class="tips-box"></view> -->
松开发送
</view>
</view>
<view class="tips-box">松开发送</view>
<view class="btns-box">
<view :class="['close-btn', isOut ? 'is-out' : '']">
取消
<image class="btn-box" v-if="!isOut" src="/src/static/images/course/close.png" mode=""></image>
<image class="btn-box" v-else src="/src/static/images/course/close-active.png" mode=""></image>
<view class="btns-box" v-if="transVoiceIng">
<view class="cancel-btn transVoiceIng" @click="closeModel()"></view>
<view class="trans-btn transVoiceIng" @click="sendVoice()"></view>
<view class="send-btn" @click="sendText()">发送</view>
</view>
<view class="btns-box" v-else>
<view :class="['close-btn', isOutLeft ? 'is-out' : '']">
<image class="btn-box" v-if="!isOutLeft" src="/src/static/images/course/cancel.png" mode="heightFix">
</image>
<image class="btn-box" v-else src="/src/static/images/course/cancel-active.png" mode="heightFix"></image>
</view>
<view :class="['close-btn trans-btn', isOutRight ? 'is-out' : '']">
<image class="btn-box" v-if="!isOutRight" src="/src/static/images/course/translate.png" mode="heightFix">
</image>
<image class="btn-box" v-else src="/src/static/images/course/translate-active.png" mode="heightFix"></image>
</view>
</view>
<view :class="['talk-dialog', isOut ? 'is-out' : '']">
<view :class="['talk-dialog', isOut ? 'is-out' : '']" v-show="!isOutRight">
<image src="@/static/images/course/voice-white.gif" mode="" class="gif-box"></image>
</view>
<view :class="['talk-dialog talk-textarea' ]" v-show="isOutRight" @click.stop>
<view class="trans-loading-box" v-show="transLoading">
<CommonLoading color="#fff" class="loading-box" />
</view>
<uv-textarea class="voice-input-box" ref="inputRef" v-model="answerValue" :maxlength="props.textValueLength"
v-show="!transLoading" @click.stop customStyle="background: #ffffff00; padding-bottom: 50rpx;"
height="250rpx" text-style="color: #fff"
placeholderStyle="color:#999999;font-size:26rpx;border-color: #ffffff00 !important;"
placeholder=""></uv-textarea>
</view>
</view>
</uni-popup>
</view>
@@ -65,6 +93,9 @@
onHide,
onShow
} from '@dcloudio/uni-app';
import {
commonUploadVoiceFile
} from '@/api/common.js';
const storageStore = useStorageStore();
@@ -107,6 +138,10 @@
default: 'mp3', // 生成语音文件类型
type: String
},
answerMethod: {
default: '01', //01 不限制 02 文字 03 语音
type: String
},
});
const {
isDisabled,
@@ -124,7 +159,15 @@
watch(
() => showTalkingModel.value,
(val) => (val ? talkModelRef.value.open() : talkModelRef.value.close())
(val) => {
if (val) {
talkModelRef.value.open()
} else {
talkModelRef.value.close()
transVoiceIng.value = false
}
}
);
watch(
() => props.isLoading,
@@ -161,16 +204,18 @@
const answerValue = ref('');
const talkBtnRef = ref();
const transVoiceIng = ref(false)
// 录音
const audioPath = ref('');
const audioObj = uni.createInnerAudioContext();
const isGettingPermission = ref(false)
const isStartNum = ref(0);
const isStopNum = ref(0);
//#ifdef APP-PLUS
// app端
const recordObjCom = ref({});
const IsAndEnv = getPhoneEnvBool('AND');
const isStartNum = ref(0);
const isStopNum = ref(0);
const transLoading = ref(false)
const initRecord = () => {
recordObjCom.value = uni.getRecorderManager();
unref(recordObjCom)?.onStop((res, duration) => {
@@ -180,27 +225,51 @@
return
}
audioPath.value = res.tempFilePath;
console.log(isOut.value, isOutLeft.value, isOutRight.value)
if (!isOut.value) {
if (startFlag.value !== 2) {
// setTimeout(() => {
// showTalkingModel.value = false
// nextTick(() => {
// // showTalkingModel.value = false
// common.msg('说话时间太短,没有听清!')
// })
// },100)
closeModel(() => {
nextTick(() => {
// showTalkingModel.value = false
common.msg('说话时间太短,没有听清!');
});
});
} else {
closeModel();
if (props.answerMethod === '02') {
common.msg('当前回答限制为仅文本回答!')
return
}
emit('uploadVoice', audioPath.value, recordTime.value);
}
} else {
} else if (isOutLeft.value) {
console.log('取消')
closeModel();
} else if (isOutRight.value) {
console.log('转文本')
transLoading.value = true
commonUploadVoiceFile(audioPath.value, '/traapp/voice/voiceChart', {}).then(res => {
console.log(res)
transLoading.value = false
if (!!(res.body.chatBody || '').trim()) {
answerValue.value = res.body.chatBody
} else {
closeModel()
uni.showToast({
title: '未识别到文字',
icon: 'none',
duration: 1000
});
}
}).catch(err => {
console.log(err)
transLoading.value = false
closeModel()
uni.showToast({
title: '系统异常,请联系管理员',
icon: 'none',
duration: 1000
});
})
}
});
unref(recordObjCom).onStart(() => {
@@ -227,6 +296,8 @@
}
});
};
const setRecordTime = () => {
setTimeout(() => {
if (isRecording.value) {
@@ -251,16 +322,24 @@
try {
if (unref(recordObjCom).stop) {
unref(recordObjCom).stop();
if (!transVoiceIng.value) {
console.log('1234')
setTimeout(() => {
closeModel()
}, 300)
}
} else {
closeModel();
}
} catch {
console.log('catch')
closeModel();
}
// setTimeout(() => {
// canStart.value = true
// }, 1000)
};
// #endif
const closeModel = (cb = () => {}) => {
isStopNum.value = isStartNum.value;
setTimeout(() => {
@@ -271,7 +350,21 @@
isTalking.value = false;
}, 800);
};
// #endif
const sendText = () => {
keyboardIsShow.value = true
showKeyboard(closeModel)
}
const sendVoice = () => {
closeModel();
if (props.answerMethod === '02') {
common.msg('当前回答限制为仅文本回答!')
return
}
emit('uploadVoice', audioPath.value, recordTime.value);
}
//#ifndef APP-PLUS
const recordObjCom = ref({});
const initRecord = () => {};
@@ -293,6 +386,15 @@
let moveVal = windowInfo.screenHeight - touchY.value > _btnHeight;
return moveVal;
});
const isOutLeft = computed(() => {
let _left = windowInfo.screenWidth / 2 > touchX.value
return _left && isOut.value;
});
const isOutRight = computed(() => {
let _right = windowInfo.screenWidth / 2 < touchX.value
return _right && isOut.value;
// return true
});
const outScreen = computed(() => {
windowInfo.screenHeight - touchY.value <= 0;
});
@@ -327,7 +429,7 @@
}
// #endif
//#ifdef H5
showOverlayTalking(obj);
showOverlayTalking(obj);
// #endif
};
const getPermission = async () => {
@@ -381,6 +483,7 @@
const isMoving = ref(false);
const isTalking = ref(false);
const showOverlayTalking = (obj) => {
console.log(obj)
if (showTalkingModel.value) return;
if (isGettingPermission.value) {
//#ifdef APP-PLUS
@@ -422,7 +525,7 @@
startRecord();
// #endif
//#ifdef H5
showTalkingModel.value = true
showTalkingModel.value = true
// #endif
setTimeout(() => {
if (_recnum === recNum.value) {
@@ -442,10 +545,20 @@
isMoving.value = false;
if (!showTalkingModel.value) return;
//#ifdef APP-PLUS
transVoiceIng.value = isOutRight.value
stopRecord();
// #endif
//#ifdef H5
showTalkingModel.value = false
if (isOutRight.value) {
console.log('H5转文本')
transVoiceIng.value = true
emit('transVoice', audioPath.value);
} else {
transVoiceIng.value = false
showTalkingModel.value = false
}
// #endif
};
@@ -459,19 +572,20 @@
}
};
// 发送文本
const showKeyboard = () => {
const showKeyboard = (cb = () => {}) => {
if (props.answerMethod === '03') {
common.msg('当前回答限制为仅语音回答!')
return
}
if (isLoadingState.value) {
uni.showToast({
title: props.loadingText,
icon: 'none',
duration: 1000
});
common.msg(props.loadingText);
return;
}
if (!isDisabled.value) {
if (keyboardIsShow.value) {
if (answerValue.value.trim()) {
emit('submitAnswer', answerValue.value, changeBtn);
cb && cb()
} else {
// keyboardIsShow.value = !keyboardIsShow.value
common.msg('不能发送空白消息!');
@@ -480,11 +594,7 @@
keyboardIsShow.value = !keyboardIsShow.value;
}
} else {
uni.showToast({
title: '暂无问题需要回答!',
icon: 'none',
duration: 1000
});
common.msg('暂无问题需要回答!');
}
};
const changeBtn = () => {
@@ -549,9 +659,10 @@
// justify-content: flex-end;
width: 100%;
height: 100%;
pointer-events: none;
// pointer-events: none;
max-height: 100vh;
justify-content: space-between;
padding-bottom: 0;
.circle-bg-box {
height: 234rpx;
@@ -560,63 +671,102 @@
position: relative;
pointer-events: none;
.icon-box {
position: absolute;
left: 50%;
top: 50%;
margin-left: -17rpx;
margin-top: -24rpx;
width: 34rpx;
height: 48rpx;
pointer-events: none;
&.no-bg {
background: none;
overflow: hidden;
}
image {
width: 100%;
height: 100%;
pointer-events: none;
}
.tips-box {
padding-top: 67rpx;
line-height: 40rpx;
font-size: 33rpx;
color: #000;
text-align: center;
}
}
.tips-box {
height: 40rpx;
line-height: 40rpx;
font-size: 28rpx;
color: #c0c0c0;
text-align: center;
margin: 12rpx 0 24rpx;
pointer-events: none;
}
.btns-box {
height: 270rpx;
height: 240rpx;
// padding: 0 120rpx;
width: 154rpx;
margin: 0 auto;
width: 100%;
margin: 0 auto -20rpx;
overflow: hidden;
font-size: 28rpx;
text-align: center;
line-height: 116rpx;
pointer-events: none;
padding-top: 40rpx;
display: flex;
position: relative;
z-index: 12000;
justify-content: space-around;
.transVoiceIng {
width: 154rpx;
height: 154rpx;
overflow: hidden;
position: relative;
z-index: 12010;
&.cancel-btn {
background: url(@/static/images/course/cancel-btn.png) no-repeat;
background-size: cover;
}
&.trans-btn {
background: url(@/static/images/course/trans-btn.png) no-repeat;
background-size: cover;
}
}
.send-btn {
position: relative;
width: 262rpx;
height: 154rpx;
line-height: 154rpx;
overflow: hidden;
background-color: #d8d8d8;
border-radius: 77rpx;
font-size: 33rpx;
z-index: 12010;
}
.close-btn {
padding-top: 40rpx;
line-height: 28rpx;
display: flex;
flex-direction: column;
justify-content: space-between;
float: left;
height: 100%;
width: 100%;
width: 50%;
overflow: hidden;
color: #999;
pointer-events: none;
position: relative;
&.is-out {
color: #fff;
pointer-events: none;
}
.btn-box {
width: 100%;
height: 200rpx;
pointer-events: none;
margin: 0 auto;
position: absolute;
top: 0;
}
&:not(.trans-btn) {
.btn-box {
right: 40rpx;
}
}
&.trans-btn {
.btn-box {
left: 40rpx;
}
}
}
.translate-btn {
@@ -628,12 +778,6 @@
pointer-events: none;
}
.btn-box {
width: 100%;
max-height: 152rpx;
pointer-events: none;
margin: 0 auto;
}
}
.talk-dialog {
@@ -643,7 +787,33 @@
background-color: #06f;
border-radius: 32rpx;
position: relative;
pointer-events: none;
&.talk-textarea {
width: 700rpx;
background-color: #06f;
&::after {
left: 80%;
}
.trans-loading-box {
width: 100%;
height: 100%;
position: relative;
.loading-box {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}
}
::v-deep .uv-border {
border-color: #ffffff00 !important;
}
&.is-out {
background-color: #ff3e49;
@@ -676,164 +846,18 @@
margin-left: -22rpx;
pointer-events: none;
}
.voice-input-box {
width: 700rpx;
height: 234rpx;
position: relative;
z-index: 1200;
}
}
}
::v-deep .uv-input__content__field-wrapper__field {
padding-right: 60rpx;
}
</style>
<style lang="scss" scoped>
// .talk-btns {
// height: 150rpx;
// background-color: #fff;
// padding: 25rpx 30rpx 0;
// position: relative;
// z-index: v-bind(btnZindex);
// .overlay-box {
// display: flex;
// flex-direction: column-reverse;
// // justify-content: flex-end;
// width: 100%;
// height: 100%;
// .circle-bg-box {
// height: 234rpx;
// background: url(@/static/images/course/talking.png) no-repeat;
// background-size: cover;
// position: relative;
// .icon-box {
// position: absolute;
// left: 50%;
// top: 50%;
// margin-left: -17rpx;
// margin-top: -24rpx;
// width: 34rpx;
// height: 48rpx;
// image {
// width: 100%;
// height: 100%;
// }
// }
// }
// .tips-box {
// height: 40rpx;
// line-height: 40rpx;
// color: #c0c0c0;
// text-align: center;
// margin: 12rpx 0 24rpx;
// }
// .btns-box {
// height: 270rpx;
// // padding: 0 120rpx;
// width: 154rpx;
// margin: 0 auto 100rpx;
// overflow: hidden;
// font-size: 28rpx;
// text-align: center;
// line-height: 116rpx;
// .close-btn {
// float: left;
// height: 100%;
// width: 154rpx;
// overflow: hidden;
// color: #999;
// &.is-out {
// color: #fff;
// }
// }
// .translate-btn {
// float: right;
// height: 100%;
// width: 154rpx;
// overflow: hidden;
// color: #999;
// }
// .btn-box {
// width: 100%;
// height: 154rpx;
// }
// }
// .talk-dialog {
// width: 506rpx;
// height: 234rpx;
// margin: 0 auto;
// background-color: #06f;
// border-radius: 32rpx;
// position: relative;
// &.is-out {
// background-color: #ff3e49;
// &::after {
// background-color: #ff3e49;
// }
// }
// .gif-box {
// position: absolute;
// width: 200rpx;
// height: 80rpx;
// left: 50%;
// top: 50%;
// transform: translate(-50%, -50%);
// }
// &::after {
// content: '';
// width: 44rpx;
// height: 44rpx;
// clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
// background-color: #06f;
// overflow: hidden;
// position: absolute;
// bottom: -22rpx;
// left: 50%;
// margin-left: -22rpx;
// }
// }
// .white-bg-box {
// height: 365rpx;
// background-color: #fff;
// overflow: hidden;
// border-radius: 32rpx 32rpx 0 0;
// padding: 58rpx 36rpx;
// .box-title {
// font-size: 32rpx;
// font-weight: bold;
// line-height: 46rpx;
// margin-bottom: 46rpx;
// }
// .box-btns {
// height: 46rpx;
// line-height: 46rpx;
// display: flex;
// justify-content: space-between;
// color: #999;
// .is-active {
// color: #06f;
// }
// }
// }
// }
// ::v-deep .uv-input__content__field-wrapper__field {
// padding-right: 60rpx;
// }
</style>
+839
View File
@@ -0,0 +1,839 @@
<template>
<view class="talk-btns" ref="talkBtnRef">
<view class="touch-btn" @touchstart="getRecordPermission" @touchend="hideOverlayTalking" @touchmove="handleMove" @touchcancel="hideOverlayTalking"
v-if="!keyboardIsShow">
按住说话
</view>
<view class="touch-btn" v-if="keyboardIsShow">
<uv-input class="input-box" ref="inputRef" v-model="answerValue" :maxlength="props.textValueLength"
:adjustPosition="adjustPosition" placeholderStyle="color:#999999;font-size:26rpx"
placeholder="请输入文字"></uv-input>
</view>
<view class="talk-btn-box" v-show="!onlyVoice">
<image class="talk-btn-icon" v-show="!keyboardIsShow" src="/src/static/images/course/jianpan.png" mode=""
@click="changeBtn"></image>
<image class="talk-btn-icon" v-show="keyboardIsShow && !answerValue" src="/src/static/images/course/record.png"
mode="" @click="changeBtn"></image>
<image class="talk-btn-icon" v-show="keyboardIsShow && !!answerValue" src="/src/static/images/course/send.png"
mode="" @click.stop="showKeyboard"></image>
</view>
<uni-popup ref="talkModelRef" type="bottom" @click="showTalkingModel = false"
mask-background-color="rgba(0,0,0,0.9)">
<view class="overlay-box">
<view class="circle-bg-box">
<view class="icon-box">
<image src="/src/static/images/course/talk-icon.png" mode=""></image>
</view>
</view>
<view class="tips-box">松开发送</view>
<view class="btns-box">
<view :class="['close-btn', isOut ? 'is-out' : '']">
取消
<image class="btn-box" v-if="!isOut" src="/src/static/images/course/close.png" mode=""></image>
<image class="btn-box" v-else src="/src/static/images/course/close-active.png" mode=""></image>
</view>
</view>
<view :class="['talk-dialog', isOut ? 'is-out' : '']">
<image src="@/static/images/course/voice-white.gif" mode="" class="gif-box"></image>
</view>
</view>
</uni-popup>
</view>
</template>
<script setup>
import {
toRefs,
ref,
computed,
unref,
onMounted,
onUnmounted,
nextTick,
watch,
getCurrentInstance
} from 'vue';
import common from '../../../common/common';
import permission from '@/common/permission.js';
import {
getPhoneEnvBool
} from '@/common/common.js';
import {
useStorageStore
} from '@/store/storage.js';
import {
onHide,
onShow
} from '@dcloudio/uni-app';
const storageStore = useStorageStore();
const proxy = getCurrentInstance();
const emit = defineEmits(['uploadVoice', 'submitAnswer', 'startRecord']);
const props = defineProps({
isDisabled: {
default: true,
type: Boolean
},
isLoading: {
default: false,
type: Boolean
},
loadingText: {
default: '发送回答中,请稍后再试!',
type: String
},
onlyVoice: {
default: false,
type: Boolean
},
canAnswer: {
default: true,
type: Boolean
},
canAnswerText: {
default: '当前状态不能发送内容!',
type: String
},
textValueLength: {
type: Number,
default: 500
},
adjustPosition: {
default: false,
type: Boolean
},
formatType: {
default: 'mp3', // 生成语音文件类型
type: String
},
});
const {
isDisabled,
onlyVoice,
adjustPosition,
formatType
} = toRefs(props);
const isLoadingState = computed(() => props.isLoading);
const showTalkingModel = ref(false);
const keyboardIsShow = ref(false);
const talkModelRef = ref();
storageStore.setTouchBtnZIndex(12);
const btnZindex = computed(() => storageStore.getTouchBtnZIndex);
watch(
() => showTalkingModel.value,
(val) => (val ? talkModelRef.value.open() : talkModelRef.value.close())
);
watch(
() => props.isLoading,
() => {}, {
deep: true,
immediate: true
}
);
watch(
() => props.canAnswer,
() => {}, {
deep: true,
immediate: true
}
);
watch(
() => props.onlyVoice,
() => {}, {
deep: true,
immediate: true
}
);
watch(
() => props.isDisabled,
(val) => {
if (val) {
keyboardIsShow.value = false;
}
}, {
deep: true,
immediate: true
}
);
const answerValue = ref('');
const talkBtnRef = ref();
// 录音
const audioPath = ref('');
const audioObj = uni.createInnerAudioContext();
const isGettingPermission = ref(false)
//#ifdef APP-PLUS
// app端
const recordObjCom = ref({});
const IsAndEnv = getPhoneEnvBool('AND');
const isStartNum = ref(0);
const isStopNum = ref(0);
const initRecord = () => {
recordObjCom.value = uni.getRecorderManager();
unref(recordObjCom)?.onStop((res, duration) => {
isRecording.value = false
if (isGettingPermission.value) {
isGettingPermission.value = false
return
}
audioPath.value = res.tempFilePath;
if (!isOut.value) {
if (startFlag.value !== 2) {
// setTimeout(() => {
// showTalkingModel.value = false
// nextTick(() => {
// // showTalkingModel.value = false
// common.msg('说话时间太短,没有听清!')
// })
// },100)
closeModel(() => {
nextTick(() => {
// showTalkingModel.value = false
common.msg('说话时间太短,没有听清!');
});
});
} else {
closeModel();
emit('uploadVoice', audioPath.value, recordTime.value);
}
} else {
closeModel();
}
});
unref(recordObjCom).onStart(() => {
isRecording.value = true
recordTime.value = 0
setRecordTime()
if (isGettingPermission.value) {
isGettingPermission.value = false
return
}
startFlag.value = 1;
setTimeout(() => {
startFlag.value = 2;
}, 1000);
let _recordPerm = uni.getAppAuthorizeSetting();
let _state = _recordPerm.microphoneAuthorized;
console.log(isStopNum.value === isStartNum.value, _state);
if (_state === 'authorized') {
if (isStopNum.value === isStartNum.value) {
stopRecord();
} else {
showTalkingModel.value = true;
}
}
});
};
const setRecordTime = () => {
setTimeout(() => {
if (isRecording.value) {
recordTime.value += 1
setRecordTime()
}
}, 1000)
}
const isRecording = ref(false)
const recordTime = ref(0)
const startFlag = ref(1);
// const canStart = ref(true)
const startRecord = () => {
isStartNum.value += 1;
unref(recordObjCom).start({
format: formatType.value
});
};
const stopRecord = () => {
isStopNum.value = isStartNum.value;
try {
if (unref(recordObjCom).stop) {
unref(recordObjCom).stop();
} else {
closeModel();
}
} catch {
closeModel();
}
// setTimeout(() => {
// canStart.value = true
// }, 1000)
};
const closeModel = (cb = () => {}) => {
isStopNum.value = isStartNum.value;
setTimeout(() => {
showTalkingModel.value = false;
cb && cb();
}, 350);
setTimeout(() => {
isTalking.value = false;
}, 800);
};
// #endif
//#ifndef APP-PLUS
const recordObjCom = ref({});
const initRecord = () => {};
// #endif
onMounted(async () => {
initRecord?.();
});
// 录音结束
// 记录 按下移动位置
const touchX = ref(0);
const touchY = ref(0);
const baseY = ref(0);
const windowInfo = uni.getWindowInfo();
// 计算是否超出按下盒子位置
const isOut = computed(() => {
let _btnHeight = (windowInfo.screenWidth / 750) * 234;
let moveVal = windowInfo.screenHeight - touchY.value > _btnHeight;
return moveVal;
});
const outScreen = computed(() => {
windowInfo.screenHeight - touchY.value <= 0;
});
const recNum = ref(1);
const activeNum = ref(1);
const hasPermission = ref(false)
const getRecordPermission = async (obj) => {
if (isDisabled.value) return;
//#ifdef APP-PLUS
if (isTalking.value) {
common.msg('操作频繁,请稍后重试!');
setTimeout(() => {
isTalking.value = false;
}, 500);
return;
}
if (!hasPermission.value) {
let _res = await getPermission()
if (_res) {
hasPermission.value = true
setTimeout(() => {
if (!isGettingPermission.value) {
showOverlayTalking(obj);
} else {
isGettingPermission.value = false
}
}, 100)
}
} else {
showOverlayTalking(obj);
}
// #endif
//#ifdef H5
showOverlayTalking(obj);
// #endif
};
const getPermission = async () => {
//#ifdef APP-PLUS
// 安卓端
if (IsAndEnv) {
let _recordPerm = await permission.requestAndroidPermission('android.permission.RECORD_AUDIO');
if (_recordPerm !== 1) {
showTalkingModel.value = false;
let res = await common.show('提示信息', '当前页面需要使用的录音权限,是否前往开启?', true, '取消', '确定')
if (res) {
// 去开启权限
permission.gotoAppPermissionSetting();
} else {
// 取消
common.navigateBack();
}
return false
} else {
// isGettingPermission.value = false
return true
}
} else {
let _recordPerm = uni.getAppAuthorizeSetting();
let _state = _recordPerm.microphoneAuthorized;
if (_state === 'denied') {
common.show('提示信息', '当前页面需要使用的录音权限,是否前往开启?', true, '取消', '确定').then((res) => {
if (res) {
// 去开启权限
uni.openAppAuthorizeSetting();
} else {
// 取消
common.navigateBack();
}
});
return false
} else if (_state === 'not determined') {
// startRecord()
// isGettingPermission.value = false
return true
} else {
// isGettingPermission.value = false
return true
}
}
// #endif
// //#ifndef APP-PLUS
// return true
// // #endif
}
const isMoving = ref(false);
const isTalking = ref(false);
const showOverlayTalking = (obj) => {
if (showTalkingModel.value) return;
if (isGettingPermission.value) {
//#ifdef APP-PLUS
stopRecord()
// #endif
isGettingPermission.value = false
return
}
if (isLoadingState.value) {
uni.showToast({
title: props.loadingText,
icon: 'none',
duration: 1000
});
return;
}
if (!props.canAnswer) {
uni.showToast({
title: props.canAnswerText,
icon: 'none',
duration: 1000
});
return;
}
// if(!canStart.value){
// return
// }
isMoving.value = true;
isTalking.value = true;
emit('startRecord');
recNum.value += 1;
let _recnum = recNum.value
if (!isDisabled.value) {
touchX.value = obj.changedTouches[0].pageX;
touchY.value = obj.changedTouches[0].pageY;
baseY.value = obj.currentTarget.offsetTop;
//#ifdef APP-PLUS
// canStart.value = false
startRecord();
// #endif
//#ifdef H5
showTalkingModel.value = true
// #endif
setTimeout(() => {
if (_recnum === recNum.value) {
hideOverlayTalking(recNum.value);
}
}, 59 * 1010);
} else {
showTalkingModel.value = false;
uni.showToast({
title: '暂无问题需要回答!',
icon: 'none',
duration: 1000
});
}
};
const hideOverlayTalking = (obj) => {
isMoving.value = false;
if (!showTalkingModel.value) return;
//#ifdef APP-PLUS
stopRecord();
// #endif
//#ifdef H5
showTalkingModel.value = false
// #endif
};
const handleMove = (obj) => {
if (isMoving.value) {
touchX.value = obj.changedTouches[0].pageX;
touchY.value = obj.changedTouches[0].pageY;
// if (outScreen.value) {
// hideOverlayTalking(obj);
// }
}
};
// 发送文本
const showKeyboard = () => {
if (isLoadingState.value) {
uni.showToast({
title: props.loadingText,
icon: 'none',
duration: 1000
});
return;
}
if (!isDisabled.value) {
if (keyboardIsShow.value) {
if (answerValue.value.trim()) {
emit('submitAnswer', answerValue.value, changeBtn);
} else {
// keyboardIsShow.value = !keyboardIsShow.value
common.msg('不能发送空白消息!');
}
} else {
keyboardIsShow.value = !keyboardIsShow.value;
}
} else {
uni.showToast({
title: '暂无问题需要回答!',
icon: 'none',
duration: 1000
});
}
};
const changeBtn = () => {
if (props.isDisabled) return;
keyboardIsShow.value = !keyboardIsShow.value;
answerValue.value = '';
};
onHide(() => {
isGettingPermission.value = true
})
</script>
<style lang="scss" scoped>
.talk-btns {
height: 150rpx;
background-color: #fff;
padding: 25rpx 30rpx 0;
position: relative;
z-index: v-bind(btnZindex);
.touch-btn {
height: 92rpx;
box-shadow: 0 8rpx 20rpx 0 rgba(0, 0, 0, 0.06);
text-align: center;
line-height: 92rpx;
font-size: 28rpx;
color: #000;
font-weight: 400;
position: relative;
.input-box {
width: 100%;
height: 92rpx;
line-height: 92rpx;
text-align: left;
box-sizing: border-box;
padding: 0 40rpx 0 40rpx;
}
}
.talk-btn-box {
position: absolute;
height: 100%;
width: 120rpx;
right: 0;
top: 0;
.talk-btn-icon {
width: 40rpx;
height: 40rpx;
position: absolute;
right: 60rpx;
top: 50%;
margin-top: -20rpx;
}
}
}
.overlay-box {
display: flex;
flex-direction: column-reverse;
// justify-content: flex-end;
width: 100%;
height: 100%;
pointer-events: none;
max-height: 100vh;
justify-content: space-between;
.circle-bg-box {
height: 234rpx;
background: url(@/static/images/course/talking.png) no-repeat;
background-size: cover;
position: relative;
pointer-events: none;
.icon-box {
position: absolute;
left: 50%;
top: 50%;
margin-left: -17rpx;
margin-top: -24rpx;
width: 34rpx;
height: 48rpx;
pointer-events: none;
image {
width: 100%;
height: 100%;
pointer-events: none;
}
}
}
.tips-box {
height: 40rpx;
line-height: 40rpx;
font-size: 28rpx;
color: #c0c0c0;
text-align: center;
margin: 12rpx 0 24rpx;
pointer-events: none;
}
.btns-box {
height: 270rpx;
// padding: 0 120rpx;
width: 154rpx;
margin: 0 auto;
overflow: hidden;
font-size: 28rpx;
text-align: center;
line-height: 116rpx;
pointer-events: none;
display: flex;
.close-btn {
padding-top: 40rpx;
line-height: 28rpx;
display: flex;
flex-direction: column;
justify-content: space-between;
float: left;
height: 100%;
width: 100%;
overflow: hidden;
color: #999;
pointer-events: none;
&.is-out {
color: #fff;
pointer-events: none;
}
}
.translate-btn {
float: right;
height: 100%;
width: 154rpx;
overflow: hidden;
color: #999;
pointer-events: none;
}
.btn-box {
width: 100%;
max-height: 152rpx;
pointer-events: none;
margin: 0 auto;
}
}
.talk-dialog {
width: 506rpx;
height: 234rpx;
margin: 0 auto;
background-color: #06f;
border-radius: 32rpx;
position: relative;
pointer-events: none;
&.is-out {
background-color: #ff3e49;
&::after {
background-color: #ff3e49;
}
}
.gif-box {
position: absolute;
width: 200rpx;
height: 80rpx;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
pointer-events: none;
}
&::after {
content: '';
width: 44rpx;
height: 44rpx;
clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
background-color: #06f;
overflow: hidden;
position: absolute;
bottom: -22rpx;
left: 50%;
margin-left: -22rpx;
pointer-events: none;
}
}
}
::v-deep .uv-input__content__field-wrapper__field {
padding-right: 60rpx;
}
</style>
<style lang="scss" scoped>
// .talk-btns {
// height: 150rpx;
// background-color: #fff;
// padding: 25rpx 30rpx 0;
// position: relative;
// z-index: v-bind(btnZindex);
// .overlay-box {
// display: flex;
// flex-direction: column-reverse;
// // justify-content: flex-end;
// width: 100%;
// height: 100%;
// .circle-bg-box {
// height: 234rpx;
// background: url(@/static/images/course/talking.png) no-repeat;
// background-size: cover;
// position: relative;
// .icon-box {
// position: absolute;
// left: 50%;
// top: 50%;
// margin-left: -17rpx;
// margin-top: -24rpx;
// width: 34rpx;
// height: 48rpx;
// image {
// width: 100%;
// height: 100%;
// }
// }
// }
// .tips-box {
// height: 40rpx;
// line-height: 40rpx;
// color: #c0c0c0;
// text-align: center;
// margin: 12rpx 0 24rpx;
// }
// .btns-box {
// height: 270rpx;
// // padding: 0 120rpx;
// width: 154rpx;
// margin: 0 auto 100rpx;
// overflow: hidden;
// font-size: 28rpx;
// text-align: center;
// line-height: 116rpx;
// .close-btn {
// float: left;
// height: 100%;
// width: 154rpx;
// overflow: hidden;
// color: #999;
// &.is-out {
// color: #fff;
// }
// }
// .translate-btn {
// float: right;
// height: 100%;
// width: 154rpx;
// overflow: hidden;
// color: #999;
// }
// .btn-box {
// width: 100%;
// height: 154rpx;
// }
// }
// .talk-dialog {
// width: 506rpx;
// height: 234rpx;
// margin: 0 auto;
// background-color: #06f;
// border-radius: 32rpx;
// position: relative;
// &.is-out {
// background-color: #ff3e49;
// &::after {
// background-color: #ff3e49;
// }
// }
// .gif-box {
// position: absolute;
// width: 200rpx;
// height: 80rpx;
// left: 50%;
// top: 50%;
// transform: translate(-50%, -50%);
// }
// &::after {
// content: '';
// width: 44rpx;
// height: 44rpx;
// clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
// background-color: #06f;
// overflow: hidden;
// position: absolute;
// bottom: -22rpx;
// left: 50%;
// margin-left: -22rpx;
// }
// }
// .white-bg-box {
// height: 365rpx;
// background-color: #fff;
// overflow: hidden;
// border-radius: 32rpx 32rpx 0 0;
// padding: 58rpx 36rpx;
// .box-title {
// font-size: 32rpx;
// font-weight: bold;
// line-height: 46rpx;
// margin-bottom: 46rpx;
// }
// .box-btns {
// height: 46rpx;
// line-height: 46rpx;
// display: flex;
// justify-content: space-between;
// color: #999;
// .is-active {
// color: #06f;
// }
// }
// }
// }
// ::v-deep .uv-input__content__field-wrapper__field {
// padding-right: 60rpx;
// }
</style>
+5
View File
@@ -89,6 +89,7 @@
:canAnswer="testAnswer"
:adjustPosition="true"
:canAnswerText="canAnswerText"
:answerMethod="answerMethod"
:isDisabled="(lastTalkingInfo.type || 0) !== 4 || ['SN', 'MU', 'JD'].includes(lastTalkingInfo.qnsTyp)" />
</view>
<uv-overlay :show="showSeekModel" opacity=".6" @click="showSeekModel = false">
@@ -256,6 +257,7 @@ const getData = async flag => {
watchFlag.value += 1
}
}
const answerMethod = ref('01')
const startStudy = data => {
studyStartApi(data)
.then(res => {
@@ -266,6 +268,9 @@ const startStudy = data => {
logId.value = body.logId || ''
getGraphInfoUnStudy(1)
step.value = 2
console.log(body)
answerMethod.value = body.answerMethod || '01'
})
.catch(err => {})
}
+11 -2
View File
@@ -824,13 +824,20 @@
const keyboardHeight = ref('0px');
const setHeight = (res) => {
keyboardHeight.value = (res.height || 0) + 'px';
setTimeout(()=>{
console.log(totalBotHeight.value)
},1000)
};
const keyBoardIsHide = computed(() => {
return parseInt(keyboardHeight.value) == 0;
});
const bottomHeight = ref('0px');
const totalBotHeight = computed(() => {
return keyBoardIsHide.value ? 0 + 'px' : parseInt(keyboardHeight.value) - parseInt(bottomHeight.value) + 'px';
let isIos
// #ifdef APP-PLUS
isIos = (plus.os.name == "iOS")
// #endif
return isIos || keyBoardIsHide.value ? 0 + 'px' : parseInt(keyboardHeight.value) + parseInt(bottomHeight.value) + 'px';
});
const changeAppHeightBottom = (height) => {
//#ifdef APP-PLUS
@@ -838,6 +845,8 @@
const systemInfo = uni.getSystemInfoSync();
// 计算安全区域底部高度
bottomHeight.value = systemInfo.screenHeight - systemInfo.safeArea.bottom + 'px';
console.log(systemInfo)
console.log(systemInfo.screenHeight , systemInfo.safeArea.bottom )
// #endif
};
@@ -907,7 +916,7 @@
bottom: 0;
left: 0;
box-sizing: border-box;
padding-top: v-bind(pageTopPadding);
padding-top: var(--status-bar-height);
// padding-bottom: v-bind(bottomHeight);
// display: flex;
// flex-direction: column;
+1 -1
View File
@@ -200,7 +200,7 @@
const badgeNumRef = ref(null);
const currentPointsRef = ref(null);
const gotoTestPage = () => {
// common.navigateTo('/pages/test/index');
common.navigateTo('/pages/test/index');
};
const user_info = reactive({
userName: '',
+6 -13
View File
@@ -83,7 +83,6 @@ export default function useWebSocket(aaas = null, options = {}) {
playPcmCallback(numArray)
break;
case MessageType.ERROR:
console.log('错误',body.errCode, body.errMsg);
switch (body.errCode) {
case 1:
// ASR或TTS初始化出错
@@ -105,20 +104,14 @@ export default function useWebSocket(aaas = null, options = {}) {
code: 4001,
reason: body.errMsg
})
onClose({
code: 4001,
reason: body.errMsg
})
break;
default:
ws.value.close({
code: 4001,
reason: body.errMsg
})
onClose({
code: 4001,
reason: body.errMsg
})
console.log('其他错误码的默认处理', body);
// 其他错误码的默认处理(如果需要)
// ws.value.close({
// code: 1001,
// reason: body.errMsg
// })
break;
}
break;
Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 745 KiB