<script setup lang="ts">
import { ref } from 'vue'
import request from '@/utils/request'
import AudioPlayer from '@/component/AudioPlayer.vue'
const voiceList = ref([])
const isLoading = ref(false)
const progress = ref(0)
let progressInterval: number | null = null
const getVoiceList = async() => {
try {
const result = await request.get('/voiceList')
voiceList.value = result.data.voiceList
} catch (error) {
alert('请求出错: ' + error)
}
}
getVoiceList()
const selectedVoice = ref('')
const gen_text = ref('')
const audioUrl = ref('')
const teacherMode = ref(false)
const generateAudio = async() => {
if (!gen_text.value || !selectedVoice.value) {
alert('请选择声音并输入文本')
return
}
const formData = new FormData()
formData.append("ref_audio_name", selectedVoice.value + '.mp3')
formData.append("gen_text", gen_text.value)
try {
isLoading.value = true
progress.value = 0
// 设置3分钟(180000毫秒)内从0%增加到99%
const targetDuration = 180000 // 3分钟(毫秒)
const targetProgress = 99 // 目标进度
const steps = 100 // 总步数
const increment = targetProgress / steps // 每步增量
const intervalTime = targetDuration / steps // 每步间隔时间
progressInterval = setInterval(() => {
if (progress.value < targetProgress) {
progress.value = Math.min(progress.value + increment, targetProgress)
}
}, intervalTime) as unknown as number
letresponse
if (teacherMode.value == false) {
response = await request.post('/generateAudio', formData, {
responseType: 'blob',
})
} else {
response = await request.post('/teacher', formData, {
responseType: 'blob',
})
}
// 清除定时器
if (progressInterval) {
clearInterval(progressInterval)
progressInterval = null
}
// 直接跳到100%
progress.value = 100
const audioBlob = new Blob([response.data], { type: 'audio/wav' })
audioUrl.value = URL.createObjectURL(audioBlob)
} catch (error) {
alert('生成失败: ' + (error.response?.data?.error || error.message))
if (progressInterval) {
clearInterval(progressInterval)
progressInterval = null
}
} finally {
isLoading.value = false
}
}
</script>
<template>
<div class="container mt-5">
<div class="row">
<!-- 左侧选择语音库 -->
<div class="col-md-3">
<div class="d-flex justify-content-between align-items-center mb-3">
<label for="voiceSelect" class="form-label mb-0">选择声音</label>
<select v-model="selectedVoice" id="voiceSelect" class="form-select w-50">
<option value="" disabled>请选择</option>
<option v-for="(voice, index) in voiceList" :key="index" :value="voice">
{{ voice }}
</option>
</select>
</div>
<div class="d-flex justify-content-between align-items-center">
<label for="teacherMode" class="form-label mb-0">教师模式</label>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="teacherMode" v-model="teacherMode" style="width: 3em; height: 1.5em;">
</div>
</div>
</div>
<!-- 右侧文本框和生成按钮 -->
<div class="col-md-9">
<div class="mb-3">
<h4>输入文本</h4>
<textarea v-model="gen_text" class="form-control" rows="10" placeholder="请输入教学内容..."></textarea>
</div>
<div class="d-flex justify-content-end">
<button @click="generateAudio" class="btn btn-primary" :disabled="isLoading">
{{ isLoading ? '生成中...' : '生成语音' }}
</button>
</div>
<!-- 加载进度条 -->
<div v-if="isLoading || audioUrl" class="mt-3">
<h4 v-if="isLoading">音频生成中... {{ Math.floor(progress) }}%</h4>
<h4 v-else>生成的音频</h4>
<div class="progress mb-3">
<divclass="progress-bar progress-bar-striped"
:class="{'progress-bar-animated':isLoading}"
role="progressbar"
:style="{ width:progress+'%'}"
:aria-valuenow="progress"
aria-valuemin="0"
aria-valuemax="100">
{{ Math.floor(progress) }}%
</div>
</div>
</div>
<!-- 显示生成的音频 -->
<div v-if="audioUrl && !isLoading" class="mt-3">
<AudioPlayer :src="audioUrl"></AudioPlayer>
</div>
</div>
</div>
</div>
</template>
<style scoped>
.form-switch .form-check-input {
cursor: pointer;
transition: all 0.3s ease;
}
.form-switch .form-check-input:checked {
background-color: #0d6efd;
border-color: #0d6efd;
}
.btn-primary:hover {
background-color: #0b5ed7;
}
</style>
浙公网安备 33010602011771号