添加文件预览信息展示,统一按钮操作逻辑

This commit is contained in:
WUSIJIAN
2026-02-04 11:13:00 +08:00
parent 61ba18b03f
commit 1458fb7d7e
3 changed files with 283 additions and 123 deletions

View File

@@ -1,128 +1,188 @@
<template>
<el-dialog
v-model="visible"
title="文本转语音"
title="添加音频"
width="700px"
:close-on-click-modal="false"
@close="handleClose"
>
<el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
<el-form-item label="音频名称" prop="name">
<el-input v-model="form.name" placeholder="请输入音频名称" maxlength="50" show-word-limit />
</el-form-item>
<el-form-item label="文本内容" prop="text">
<el-input
v-model="form.text"
type="textarea"
:rows="5"
placeholder="请输入要转换的文本内容"
maxlength="500"
show-word-limit
/>
</el-form-item>
<el-form-item label="音色选择" prop="voice">
<el-select v-model="form.voice" placeholder="请选择音色" style="width: 100%">
<el-option-group label="男声">
<el-option label="商务男声" value="male_business" />
<el-option label="磁性男声" value="male_magnetic" />
<el-option label="新闻男声" value="male_news" />
</el-option-group>
<el-option-group label="女声">
<el-option label="甜美女声" value="female_sweet" />
<el-option label="知性女声" value="female_intellectual" />
<el-option label="温柔女声" value="female_gentle" />
</el-option-group>
<el-option-group label="童声">
<el-option label="活泼童声" value="child_lively" />
<el-option label="可爱童声" value="child_cute" />
</el-option-group>
</el-select>
</el-form-item>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="语速">
<el-slider v-model="form.speed" :min="0.5" :max="2" :step="0.1" :format-tooltip="(val: number) => val + 'x'" />
<el-tabs v-model="activeTab" class="audio-tabs">
<el-tab-pane label="上传音频" name="upload">
<el-form ref="uploadFormRef" :model="uploadForm" :rules="uploadRules" label-width="100px">
<el-form-item label="音频名称" prop="name">
<el-input v-model="uploadForm.name" placeholder="请输入音频名称" maxlength="50" show-word-limit />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="音量">
<el-slider v-model="form.volume" :min="0" :max="100" :format-tooltip="(val: number) => val + '%'" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="音调">
<el-slider v-model="form.pitch" :min="-12" :max="12" :step="1" :format-tooltip="(val: number) => (val > 0 ? '+' : '') + val" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="采样率">
<el-select v-model="form.sampleRate" style="width: 100%">
<el-option label="16000Hz" :value="16000" />
<el-option label="22050Hz" :value="22050" />
<el-option label="44100Hz" :value="44100" />
<el-option label="48000Hz" :value="48000" />
<el-form-item label="音色类型" prop="voiceType">
<el-select v-model="uploadForm.voiceType" placeholder="请选择音色类型" style="width: 100%">
<el-option label="男声" value="male" />
<el-option label="女声" value="female" />
<el-option label="童声" value="child" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="上传文件" prop="file">
<el-upload
ref="uploadRef"
class="audio-uploader"
drag
action="#"
:auto-upload="false"
:limit="1"
accept=".mp3,.wav,.pcm,.flac"
:on-change="handleFileChange"
:on-exceed="handleExceed"
>
<el-icon class="el-icon--upload"><ele-UploadFilled /></el-icon>
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
<template #tip>
<div class="el-upload__tip">支持 mp3wavpcmflac 格式文件大小不超过 50MB</div>
</template>
</el-upload>
</el-form-item>
<!-- 上传预览 -->
<div v-if="uploadPreview.show" class="preview-section">
<div class="preview-header">
<span class="preview-title">文件信息</span>
<el-tag type="success" size="small">已选择</el-tag>
</div>
<div class="preview-info">
<span>文件名: {{ uploadPreview.fileName }}</span>
<span>文件大小: {{ formatFileSize(uploadPreview.fileSize) }}</span>
</div>
</div>
</el-form>
</el-tab-pane>
<el-form-item label="输出格式">
<el-radio-group v-model="form.format">
<el-radio label="mp3">MP3</el-radio>
<el-radio label="wav">WAV</el-radio>
<el-radio label="pcm">PCM</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<!-- 预览区域 -->
<div v-if="previewGenerated" class="preview-section">
<div class="preview-header">
<span class="preview-title">预览</span>
<el-tag type="success" size="small">已生成</el-tag>
</div>
<div class="preview-player">
<el-button
:type="isPlaying ? 'danger' : 'primary'"
circle
@click="togglePreview"
>
<el-icon>
<ele-VideoPlay v-if="!isPlaying" />
<ele-VideoPause v-else />
</el-icon>
</el-button>
<el-progress
:percentage="playProgress"
:show-text="false"
style="flex: 1; margin: 0 15px"
/>
<span class="duration">{{ formatDuration(previewDuration) }}</span>
</div>
<div class="preview-info">
<span>文件大小: {{ formatFileSize(previewFileSize) }}</span>
<span>时长: {{ formatDuration(previewDuration) }}</span>
<span>采样率: {{ form.sampleRate }}Hz</span>
</div>
</div>
<el-tab-pane label="文本转语音" name="tts">
<el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
<el-form-item label="音频名称" prop="name">
<el-input v-model="form.name" placeholder="请输入音频名称" maxlength="50" show-word-limit />
</el-form-item>
<el-form-item label="文本内容" prop="text">
<el-input
v-model="form.text"
type="textarea"
:rows="5"
placeholder="请输入要转换的文本内容"
maxlength="500"
show-word-limit
/>
</el-form-item>
<el-form-item label="音色选择" prop="voice">
<el-select v-model="form.voice" placeholder="请选择音色" style="width: 100%">
<el-option-group label="男声">
<el-option label="商务男声" value="male_business" />
<el-option label="磁性男声" value="male_magnetic" />
<el-option label="新闻男声" value="male_news" />
</el-option-group>
<el-option-group label="女声">
<el-option label="甜美女声" value="female_sweet" />
<el-option label="知性女声" value="female_intellectual" />
<el-option label="温柔女声" value="female_gentle" />
</el-option-group>
<el-option-group label="童声">
<el-option label="活泼童声" value="child_lively" />
<el-option label="可爱童声" value="child_cute" />
</el-option-group>
</el-select>
</el-form-item>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="语速">
<el-slider v-model="form.speed" :min="0.5" :max="2" :step="0.1" :format-tooltip="(val: number) => val + 'x'" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="音量">
<el-slider v-model="form.volume" :min="0" :max="100" :format-tooltip="(val: number) => val + '%'" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="音调">
<el-slider v-model="form.pitch" :min="-12" :max="12" :step="1" :format-tooltip="(val: number) => (val > 0 ? '+' : '') + val" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="采样率">
<el-select v-model="form.sampleRate" style="width: 100%">
<el-option label="16000Hz" :value="16000" />
<el-option label="22050Hz" :value="22050" />
<el-option label="44100Hz" :value="44100" />
<el-option label="48000Hz" :value="48000" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="输出格式">
<el-radio-group v-model="form.format">
<el-radio label="mp3">MP3</el-radio>
<el-radio label="wav">WAV</el-radio>
<el-radio label="pcm">PCM</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<!-- TTS预览区域 -->
<div v-if="previewGenerated" class="preview-section">
<div class="preview-header">
<span class="preview-title">预览</span>
<el-tag type="success" size="small">已生成</el-tag>
</div>
<div class="preview-player">
<el-button
:type="isPlaying ? 'danger' : 'primary'"
circle
@click="togglePreview"
>
<el-icon>
<ele-VideoPlay v-if="!isPlaying" />
<ele-VideoPause v-else />
</el-icon>
</el-button>
<el-progress
:percentage="playProgress"
:show-text="false"
style="flex: 1; margin: 0 15px"
/>
<span class="duration">{{ formatDuration(previewDuration) }}</span>
</div>
<div class="preview-info">
<span>文件大小: {{ formatFileSize(previewFileSize) }}</span>
<span>时长: {{ formatDuration(previewDuration) }}</span>
<span>采样率: {{ form.sampleRate }}Hz</span>
</div>
</div>
</el-tab-pane>
</el-tabs>
<template #footer>
<div class="dialog-footer">
<el-button @click="handleClose">取消</el-button>
<el-button type="warning" :loading="generating" @click="handleGenerate">
<el-icon><ele-Headset /></el-icon>
{{ generating ? '生成中...' : '生成预览' }}
</el-button>
<el-button type="primary" :disabled="!previewGenerated" :loading="saving" @click="handleSave">
<el-icon><ele-Check /></el-icon>
保存音频
</el-button>
<template v-if="activeTab === 'upload'">
<el-button type="primary" :loading="uploading" :disabled="!uploadPreview.show" @click="handleUpload">
<el-icon><ele-Upload /></el-icon>
{{ uploading ? '上传中...' : '上传音频' }}
</el-button>
</template>
<template v-else>
<el-button type="warning" :loading="generating" @click="handleGenerate">
<el-icon><ele-Headset /></el-icon>
{{ generating ? '生成中...' : '生成预览' }}
</el-button>
<el-button type="primary" :disabled="!previewGenerated" :loading="saving" @click="handleSave">
<el-icon><ele-Check /></el-icon>
保存音频
</el-button>
</template>
</div>
</template>
</el-dialog>
@@ -131,14 +191,18 @@
<script setup lang="ts">
import { ref, reactive, watch } from 'vue';
import { ElMessage } from 'element-plus';
import type { FormInstance, FormRules } from 'element-plus';
import type { FormInstance, FormRules, UploadInstance, UploadFile, UploadRawFile } from 'element-plus';
const emit = defineEmits(['success']);
const visible = ref(false);
const activeTab = ref('upload');
const formRef = ref<FormInstance>();
const uploadFormRef = ref<FormInstance>();
const uploadRef = ref<UploadInstance>();
const generating = ref(false);
const saving = ref(false);
const uploading = ref(false);
const previewGenerated = ref(false);
const isPlaying = ref(false);
const playProgress = ref(0);
@@ -146,6 +210,21 @@ const previewDuration = ref(0);
const previewFileSize = ref(0);
let progressTimer: ReturnType<typeof setInterval> | null = null;
// 上传表单
const uploadForm = reactive({
name: '',
voiceType: '',
file: null as File | null,
});
// 上传预览信息
const uploadPreview = reactive({
show: false,
fileName: '',
fileSize: 0,
});
// TTS 表单
const form = reactive({
name: '',
text: '',
@@ -157,6 +236,13 @@ const form = reactive({
format: 'mp3',
});
// 上传表单校验规则
const uploadRules = reactive<FormRules>({
name: [{ required: true, message: '请输入音频名称', trigger: 'blur' }],
voiceType: [{ required: true, message: '请选择音色类型', trigger: 'change' }],
});
// TTS 表单校验规则
const rules = reactive<FormRules>({
name: [{ required: true, message: '请输入音频名称', trigger: 'blur' }],
text: [
@@ -174,6 +260,16 @@ const openDialog = () => {
// 重置表单
const resetForm = () => {
activeTab.value = 'upload';
// 重置上传表单
uploadForm.name = '';
uploadForm.voiceType = '';
uploadForm.file = null;
uploadPreview.show = false;
uploadPreview.fileName = '';
uploadPreview.fileSize = 0;
uploadRef.value?.clearFiles();
// 重置 TTS 表单
form.name = '';
form.text = '';
form.voice = 'female_sweet';
@@ -189,6 +285,62 @@ const resetForm = () => {
previewFileSize.value = 0;
};
// 文件选择变化
const handleFileChange = (file: UploadFile) => {
if (file.raw) {
const maxSize = 50 * 1024 * 1024; // 50MB
if (file.raw.size > maxSize) {
ElMessage.error('文件大小不能超过 50MB');
uploadRef.value?.clearFiles();
return;
}
uploadForm.file = file.raw;
uploadPreview.show = true;
uploadPreview.fileName = file.name;
uploadPreview.fileSize = file.raw.size;
// 自动填充音频名称
if (!uploadForm.name) {
uploadForm.name = file.name.replace(/\.[^/.]+$/, '');
}
}
};
// 文件超出限制
const handleExceed = () => {
ElMessage.warning('只能上传一个文件,请先删除已选文件');
};
// 上传音频
const handleUpload = async () => {
if (!uploadFormRef.value) return;
await uploadFormRef.value.validate((valid) => {
if (!valid) return;
if (!uploadForm.file) {
ElMessage.warning('请选择要上传的音频文件');
return;
}
uploading.value = true;
// 模拟上传过程
setTimeout(() => {
uploading.value = false;
ElMessage.success('音频上传成功');
emit('success', {
name: uploadForm.name,
voiceType: uploadForm.voiceType,
duration: Math.floor(Math.random() * 180) + 30,
fileSize: uploadPreview.fileSize,
sampleRate: 44100,
format: uploadPreview.fileName.split('.').pop(),
});
handleClose();
}, 1500);
});
};
// 关闭弹窗
const handleClose = () => {
stopPreview();
@@ -310,6 +462,20 @@ defineExpose({
</script>
<style scoped lang="scss">
.audio-tabs {
:deep(.el-tabs__content) {
padding: 10px 0;
}
}
.audio-uploader {
width: 100%;
:deep(.el-upload-dragger) {
width: 100%;
}
}
.preview-section {
margin-top: 20px;
padding: 16px;