语言转文字接口

This commit is contained in:
yanghang
2025-06-27 17:07:08 +08:00
parent 2830d02d50
commit 58065003d3
7 changed files with 308 additions and 64 deletions
+1 -5
View File
@@ -9,14 +9,10 @@ ENV = 'development'
# VITE_APP_BASE_API_Url = 'http://25.64.16.133:9786'
# VITE_APP_BASE_API_Url = 'https://aitstest.jlbank.com.cn:7001'
VITE_APP_BASE_API_Url = 'https://aitstest.jlbank.com.cn:7001'
app入口
<<<<<<< HEAD
VITE_APP_BASE_API_Url = 'http://25.18.122.65:7001'
=======
# VITE_APP_BASE_API_Url = 'http://25.18.122.65:7001'
# VITE_APP_BASE_API_Url = 'http://25.16.122.65:7001'
# VITE_APP_BASE_API_Url = 'http://25.18.122.66:9786'
>>>>>>> f9ef91e5fe7dbec56a29867b9d2d9f7e3792f0f4
+25
View File
@@ -62,6 +62,31 @@ export function downloadFile(id) {
});
});
}
// 上传音频 翻译文本
export const uploadVoiceFile = (file) => {
const url = '/trastudy/traStdyInfo/audioTranscriptions';
const base_url = get_base_url(url);
const token = getToken();
const header = {
"Content-Type": "multipart/form-data; charset=UTF-8",
};
if (token)
header['summary'] = token
return new Promise((resolve, reject) => {
uni.uploadFile({
url: `${base_url}${url}`,
header: header,
filePath: file,
name: 'voice',
success(result) {
resolve(result)
},
fail(error) {
reject(new Error('Upload failed'));
}
});
});
};
// 获取音频 通用
export function downloadVoiceFile(url) {
const token = getToken();
+8
View File
@@ -26,4 +26,12 @@ export const studyParagraphOverApi = (data) => {
method: 'post',
data
});
};
// 上传语音翻译i文本 /traStdyInfo/audioTranscriptions
export const audioTranscriptionsApi = (data) => {
return request({
url: base_url + '/traStdyInfo/audioTranscriptions',
method: 'post',
data
});
};
+113 -18
View File
@@ -1,14 +1,26 @@
<template>
<view class="chart-box">
<view class="cont-box">
<view v-for="width in 3" :class="`percent-${width * 25}`"></view>
<view class="data-box">
<view class="line-box">
<view v-for="width in 3" :class="`percent-${width * 25}`"></view>
</view>
</view>
<view class="chart-box">
<view class="cont-box">
<view v-for="width in 3" :class="`percent-${width * 25}`"></view>
<view class="data-box">
<view class="line-box">
<view v-for="width in 3" :class="`percent-${width * 25}`"></view>
</view>
</view>
</view>
<view class="title">
<img class="title-icon" src="@/static/images/course/edit.png" alt="" />
改进建议
</view>
<view class="total-score">
<view class="score-num">57</view>
<img class="total-score-icon" src="@/static/images/course/score.png" alt="" />
</view>
<view v-for="item,index in 4" :class="['score-item', 'score-item'+(index + 1)]">
<view class="score-item-name">词命中率</view>
<view class="score-item-score">98</view>
</view>
</view>
</template>
<script setup>
@@ -18,17 +30,25 @@
default:() => [60,70,80,90],
type: Array
},
width:{
default: 300,
type: [Number,String]
},
height:{
default: 300,
size:{
default: 262,
type: [Number,String]
}
})
const chartWidth = computed(() => props.width + 'rpx')
const chartHeight = computed(() => props.height + 'rpx')
const chartWidth = computed(() => props.size + 'rpx')
const chartHeight = computed(() => props.size + 'rpx')
const scoreMarginX = computed(() => {
return props.size/2 + 'rpx'
})
const scoreMarginY = computed(() => {
return (props.size/2 + 30) + 'rpx'
})
const scoreMarginAbsX = computed(() => {
return '-' + (props.size/2 ) + 'rpx'
})
const scoreMarginAbsY = computed(() => {
return '-' + (props.size/2 - 25) + 'rpx'
})
const styleFour = ref('polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%)')
const styleFourResult = computed(()=>{
let score = props.data
@@ -42,16 +62,17 @@
let _ly = 50
return `polygon(${_tx}% ${_ty}%, ${_rx}% ${_ry}%, ${_bx}% ${_by}%, ${_lx}% ${_ly}%)`
})
console.log(styleFourResult.value)
</script>
<style lang="scss" scoped>
.chart-box{
width: 100%;
height: 500rpx;
height: 580rpx;
background:linear-gradient( 270deg, #4a74ff 0%, #6fa7fe 100%);
overflow: hidden;
position: relative;
border-radius: 16rpx;
margin-bottom: 20rpx;
.cont-box{
background-color: #87abfe;
width: v-bind(chartWidth);
@@ -61,6 +82,7 @@
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
margin-top: 25rpx;
&::before{
content: '';
display: block;
@@ -146,5 +168,78 @@
}
}
}
.title{
padding: 24rpx;
line-height: 46rpx;
font-size: 32rpx;
color: #fff;
.title-icon{
width: 27rpx;
height: 27rpx;
}
}
.total-score{
position: absolute;
right: 30rpx;
top:30rpx;
width: 100rpx;
height: 80rpx;
line-height: 70rpx;
font-size: 30rpx;
color: #fff;
white-space: nowrap;
text-shadow: 0 4px 12px rgba(0, 0, 0, .33);
.score-num{
display: inline-block;
font-size: 60rpx;
line-height: 70rpx;
font-weight: bold;
}
.total-score-icon{
width: 100%;
height: 16rpx;
position: absolute;
bottom:0;
left:0;
}
}
.score-item{
position: absolute;
color: #fff;
width: 120rpx;
text-align: center;
.score-item-name{
font-size: 28rpx;
line-height: 36rpx;
margin-bottom: 4rpx;
}
.score-item-score{
font-size: 38rpx;
line-height: 46rpx;
font-weight: bold;
}
&.score-item1{
left: 50%;
margin-left: -60rpx;
bottom: 50%;
transform: translateY(v-bind(scoreMarginAbsY));
}
&.score-item2{
left: 0%;
top: 50%;
transform: translate(calc(v-bind(scoreMarginX) - 60rpx), calc(-50% + 25rpx));
}
&.score-item3{
left: 50%;
margin-left: -60rpx;
top: 50%;
transform: translateY(v-bind(scoreMarginY));
}
&.score-item4{
right: 0%;
top: 50%;
transform: translate(calc(v-bind(scoreMarginAbsX) + 60rpx), calc(-50% + 25rpx));
}
}
}
</style>
+110 -23
View File
@@ -1,17 +1,29 @@
<template>
<view class="chart-box">
<view class="cont-box">
<view class="total-line total-line1"></view>
<view class="total-line total-line2"></view>
<view class="total-line total-line3"></view>
<view v-for="width in 3" :class="`percent-${width * 25}`"></view>
<view class="data-box">
<view class="line-box">
<view v-for="width in 3" :class="`percent-${width * 25}`"></view>
</view>
</view>
<view class="chart-box">
<view class="cont-box">
<view class="total-line total-line1"></view>
<view class="total-line total-line2"></view>
<view class="total-line total-line3"></view>
<view v-for="width in 3" :class="`percent-${width * 25}`"></view>
<view class="data-box">
<view class="line-box">
<view v-for="width in 3" :class="`percent-${width * 25}`"></view>
</view>
</view>
</view>
<view class="title">
<img class="title-icon" src="@/static/images/course/edit.png" alt="" />
改进建议
</view>
<view class="total-score">
<view class="score-num">57</view>
<img class="total-score-icon" src="@/static/images/course/score.png" alt="" />
</view>
<view v-for="item,index in 3" :class="['score-item', 'score-item'+(index + 1)]">
<view class="score-item-name">词命中率</view>
<view class="score-item-score">98</view>
</view>
</view>
</template>
<script setup>
@@ -21,18 +33,27 @@
default:() => [60,70,80,90],
type: Array
},
width:{
default: 300,
type: [Number,String]
},
height:{
default: 300,
size:{
default: 262,
type: [Number,String]
}
})
let _scale = .8
const chartWidth = computed(() => props.width / _scale + 'rpx')
const chartHeight = computed(() => props.height / _scale + 'rpx')
let _scale = .9
const chartWidth = computed(() => props.size / _scale + 'rpx')
const chartHeight = computed(() => props.size / _scale + 'rpx')
const scoreMarginX = computed(() => {
return (props.size/2 + 20) + 'rpx'
})
const scoreMarginAbsY = computed(() => {
return '-' + (props.size/2 - 40) + 'rpx'
})
const scoreMarginPercentY = computed(() => {
let score = props.data
return setPercent(score[2], Math.round(100 * 1.732 / 6 * _scale * 100)/100) + '%'
})
const styleFour = computed(()=>{
// let _blank = (50 - Math.round(100 * 1.732 / 3))/2
// let _margin = _blank +
@@ -57,23 +78,24 @@
const setPercent = (precent, show) => {
return Math.floor((precent/100) * show)
}
console.log(styleFourResult.value)
</script>
<style lang="scss" scoped>
.chart-box{
width: 100%;
height: 500rpx;
height: 580rpx;
background:linear-gradient( 270deg, #4a74ff 0%, #6fa7fe 100%);
overflow: hidden;
position: relative;
border-radius: 16rpx;
margin-bottom: 20rpx;
.cont-box{
background-color: #87abfe;
width: v-bind(chartWidth);
height: v-bind(chartHeight);
clip-path: v-bind(styleFour);
position: absolute;
top: 60%;
top: calc(60% + 20rpx);
left: 50%;
transform: translate(-50%, -50%);
.total-line{
@@ -160,5 +182,70 @@
}
}
}
.title{
padding: 24rpx;
line-height: 46rpx;
font-size: 32rpx;
color: #fff;
.title-icon{
width: 27rpx;
height: 27rpx;
}
}
.total-score{
position: absolute;
right: 30rpx;
top:30rpx;
width: 100rpx;
height: 80rpx;
line-height: 70rpx;
font-size: 30rpx;
color: #fff;
white-space: nowrap;
text-shadow: 0 4px 12px rgba(0, 0, 0, .33);
.score-num{
display: inline-block;
font-size: 60rpx;
line-height: 70rpx;
font-weight: bold;
}
.total-score-icon{
width: 100%;
height: 16rpx;
position: absolute;
bottom:0;
left:0;
}
}
.score-item{
position: absolute;
color: #fff;
width: 120rpx;
text-align: center;
.score-item-name{
font-size: 28rpx;
line-height: 36rpx;
margin-bottom: 4rpx;
}
.score-item-score{
font-size: 38rpx;
line-height: 46rpx;
font-weight: bold;
}
&.score-item1{
left: 50%;
margin-left: -60rpx;
bottom: 50%;
transform: translateY(v-bind(scoreMarginAbsY));
}
&.score-item2{
left: calc( 50% + v-bind(scoreMarginX));
bottom: calc(v-bind(scoreMarginPercentY) - 30rpx);
}
&.score-item3{
right: calc( 50% + v-bind(scoreMarginX));
bottom: calc(v-bind(scoreMarginPercentY) - 30rpx);
}
}
}
</style>
+23 -11
View File
@@ -17,6 +17,8 @@
</view>
<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>
<ChartFour/>
<ChartThree/>
<!-- <ChartFour/>
<ChartThree/> -->
</view>
@@ -35,11 +37,11 @@
src="@/static/images/course/play.png"
style="width: 100%;height: 100%;"
@click="playVoice"
v-show="activeVoiceId !== dataInfo.id"
v-show="props.activeVoiceId !== dataInfo.id"
></image>
<image
class="icon"
v-show="activeVoiceId === dataInfo.id"
v-show="props.activeVoiceId === dataInfo.id"
src="@/static/images/course/pause.png"
style="width: 100%;height: 100%;"
@click="pauseVoice"
@@ -55,10 +57,11 @@
<view class="user-answer" v-else-if="type === 1">
<view class="user-info">
<view class="avatar-box">
<ImagePreview class="img-box" :imgId="''"></ImagePreview>
<!-- <ImagePreview class="img-box" :imgId="''"></ImagePreview> -->
<img class="img-box" :src="userInfo.imageAddr" alt="" fit="contain" style="width: 100%; height: 100%;"/>
</view>
<view class="ai-name">
{{ '李想' }}
{{ userInfo.userName }}
</view>
</view>
@@ -83,10 +86,11 @@
<view class="user-answer" v-else-if="type===2">
<view class="user-info">
<view class="avatar-box">
<ImagePreview class="img-box" :imgId="''"></ImagePreview>
<!-- <ImagePreview class="img-box" :imgId="''"></ImagePreview> -->
<img class="img-box" :src="userInfo.imageAddr" alt="" fit="contain" style="width: 100%; height: 100%;"/>
</view>
<view class="ai-name">
{{ '李想' }}
{{ userInfo.userName }}
</view>
</view>
<view class="talking-info-less">
@@ -95,16 +99,18 @@
</view>
</view>
</view>
<view v-else></view>
</template>
<script setup>
import { watch, unref, ref, toRefs } from 'vue'
import { watch, unref, ref, toRefs, computed } from 'vue'
import ImagePreview from '@/components/image-preview/image-preview.vue'
import VideoPreview from '@/components/image-preview/video-preview.vue'
import { downloadVoiceFile } from "@/api/common.js"
import { studyParagraphOverApi } from "@/api/study.js"
import ChartFour from "./chartFour.vue"
import ChartThree from "./chartThree.vue"
import { getUserInfo } from '@/common/common';
const props = defineProps({
type:{
@@ -122,22 +128,28 @@ import ChartThree from "./chartThree.vue"
})
const { type, dataInfo, activeVoiceId } = toRefs(props)
const emit = defineEmits(['changePlay', 'nextQuestion'])
watch('activeVoiceId',(val) => {
if(val !== unref(dataInfo).id){
pauseVoice()
const talkVoiceObj = uni.createInnerAudioContext()
const userInfo = computed(() => getUserInfo())
watch(() => props.activeVoiceId,(val) => {
if(val && (val !== unref(dataInfo).id)){
talkVoiceObj?.stop()
}
},{
deep: true,
immediate: true
})
const talkVoiceObj = uni.createInnerAudioContext()
talkVoiceObj.onEnded(()=>{
// talkVoiceObj.src = ''
emit('changePlay',unref(dataInfo)?.id)
})
const pauseVoice = () => {
if(!talkVoiceObj) return
talkVoiceObj.pause()
emit('changePlay',unref(dataInfo)?.id)
}
const restartPlay = () => {
console.log(userInfo)
if(talkVoiceObj.src){
talkVoiceObj.seek(0)
talkVoiceObj.play()
+28 -7
View File
@@ -58,7 +58,9 @@
@touchstart="showOverlayTalking"
@touchend="hideOverlayTalking"
@touchmove="handleMove"
>按住说话</view>
>按住说话
<image class="talk-btn-icon" src="/src/static/images/course/jianpan.png" mode=""></image>
</view>
</view>
</view>
<uv-overlay :show="showTalkingModel" opacity=".9" @click="showTalkingModel = false">
@@ -86,10 +88,10 @@
<script setup>
import { reactive, ref, computed, unref, onMounted, onUnmounted } from 'vue';
import { onLoad,onShow,onHide } from '@dcloudio/uni-app';
import { previewFile, downloadFile, downloadVoiceFile } from "@/api/common.js"
import { previewFile, downloadFile, downloadVoiceFile, uploadVoiceFile } from "@/api/common.js"
import { getToken } from '@/common/common.js'
import { get_base_url } from '@/api/request'
import { getCourseStudyInfoApi, queryTraStdyBatchWaitParagraphInfoApi } from "@/api/study.js"
import { getCourseStudyInfoApi, queryTraStdyBatchWaitParagraphInfoApi,audioTranscriptionsApi } from "@/api/study.js"
import common from '@/common/common';
import TalkingItem from '@/pages/course/components/talkingItem.vue'
import ImagePreview from '@/components/image-preview/image-preview.vue'
@@ -112,6 +114,7 @@ const getData = async () => {
voicePath: '',
voiceObj: null
}))
initsocketTask(1)
}else{
step.value = 2
choiceTeacher.value = body.choiceTeacher
@@ -165,8 +168,7 @@ var socketTask = null
const nextStep = () => {
if(choiceTeacher.value){
teacherVoiceObj.destroy()
initsocketTask(1)
setTimeout(()=>{
console.log(111)
if( socketTask && socketTask.readyState === 1){
sendMessage({
"commond" : 'open',
@@ -178,7 +180,6 @@ const nextStep = () => {
step.value = 2
getGraphInfoUnStudy()
}
},200)
}else{
uni.showToast({
title: '请选择一位教师!',
@@ -285,7 +286,7 @@ const initRecord = () => {
})
}
const startRecord = () => {
unref(recordObj).start()
unref(recordObj).start({format:'mp3'})
}
const stopRecord = () => {
unref(recordObj).stop()
@@ -341,6 +342,17 @@ const hideOverlayTalking = (obj) => {
// #endif
if(!isOut.value){
console.log('发送')
// let formData = new FormData()
// formData.append('voice',)
console.log(audioPath.value)
setTimeout(()=>{
uploadVoiceFile(audioPath.value,{}).then(res=>{
console.log(JSON.parse(res.data))
}).catch(err=>{
console.log(err)
})
},1000)
}
// else{
// console.log('取消发送')
@@ -430,6 +442,15 @@ const handleMove = (obj) => {
line-height: 92rpx;
color: #000;
font-weight: 400;
position: relative;
.talk-btn-icon{
width: 40rpx;
height: 40rpx;
position: absolute;
right: 40rpx;
top: 50%;
margin-top: -20rpx
}
}
}
.voice-list{