feat: 唤醒语料文件上传和试听
This commit is contained in:
parent
e3e2e4a0d4
commit
de09c4cef9
38
src/hooks/usePlayAudio.ts
Normal file
38
src/hooks/usePlayAudio.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { computed } from "vue";
|
||||
|
||||
/**
|
||||
* 播放音频
|
||||
*/
|
||||
export function usePlayAudio() {
|
||||
let audio: HTMLAudioElement | null = new Audio();
|
||||
// 播放结束后自动清理
|
||||
// 先定义cleanup,再绑定事件(避免作用域问题)
|
||||
let cleanup: any;
|
||||
cleanup = () => {
|
||||
// 1. 先移除所有事件监听器,切断循环触发链路
|
||||
audio!.removeEventListener("ended", cleanup);
|
||||
audio!.removeEventListener("error", cleanup);
|
||||
// 2. 停止播放(而非清空src),避免触发新error
|
||||
audio!.pause();
|
||||
// 可选:解除引用,帮助GC(若后续无其他引用)
|
||||
audio = null;
|
||||
console.log("音频资源已清理");
|
||||
};
|
||||
|
||||
audio.addEventListener("ended", cleanup);
|
||||
|
||||
// 播放错误时也清理
|
||||
audio.addEventListener("error", cleanup);
|
||||
|
||||
const playAudio = (url: string) => {
|
||||
audio!.src = url
|
||||
audio!.play().catch((error) => {
|
||||
console.error("播放失败:", error);
|
||||
cleanup();
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
playAudio
|
||||
}
|
||||
}
|
||||
@ -388,3 +388,20 @@ export function isNumberStr(str) {
|
||||
return /^[+-]?(0|([1-9]\d*))(\.\d+)?$/g.test(str)
|
||||
}
|
||||
|
||||
export function isAudioByMimeType(mimeType) {
|
||||
const audioMimeTypes = [
|
||||
'audio/mpeg',
|
||||
'audio/wav',
|
||||
'audio/x-wav',
|
||||
'audio/flac',
|
||||
'audio/aac',
|
||||
'audio/ogg',
|
||||
'audio/mp4',
|
||||
'audio/webm',
|
||||
'audio/x-m4a',
|
||||
'audio/amr',
|
||||
'audio/midi'
|
||||
];
|
||||
|
||||
return audioMimeTypes.includes(mimeType.toLowerCase());
|
||||
}
|
||||
65
src/views/vi/components/UploadFile.vue
Normal file
65
src/views/vi/components/UploadFile.vue
Normal file
@ -0,0 +1,65 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-button link type="primary" icon="Upload" @click="handleUpload">上传</el-button>
|
||||
<el-dialog
|
||||
v-model="fileDialog"
|
||||
title="上传音频"
|
||||
append-to-body
|
||||
class="file-upload"
|
||||
width="500px"
|
||||
>
|
||||
<el-upload
|
||||
:action="`${baseUrl}/system/file/upload`"
|
||||
:before-upload="beforeAvatarUpload"
|
||||
:data="{ fileType: 3 }"
|
||||
:headers="{
|
||||
authorization: getToken(),
|
||||
}"
|
||||
:limit="1"
|
||||
:show-file-list="false"
|
||||
:on-error="uploadError"
|
||||
:on-success="uploadSuccess"
|
||||
class="file-uploader"
|
||||
>
|
||||
<template #trigger>
|
||||
<el-button type="primary">选择文件</el-button>
|
||||
</template>
|
||||
<template #tip>
|
||||
<div class="el-upload__tip">支持格式:wav / mp3</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import { getToken } from "@/utils/auth.js";
|
||||
import { isAudioByMimeType } from "@/utils/index.js";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
const emits = defineEmits(['success', 'error'])
|
||||
|
||||
const baseUrl = ref(import.meta.env.VITE_APP_BASE_API);
|
||||
const fileDialog = ref(false);
|
||||
const handleUpload = () => {
|
||||
fileDialog.value = true;
|
||||
};
|
||||
|
||||
const beforeAvatarUpload = (rawFile) => {
|
||||
if (isAudioByMimeType(rawFile.type)) {
|
||||
return true
|
||||
}
|
||||
ElMessage.error(`不支持上传格式为${rawFile.type}的文件`)
|
||||
return false
|
||||
}
|
||||
|
||||
const uploadSuccess = (e) => {
|
||||
fileDialog.value = false
|
||||
ElMessage.success('音频文件上传成功')
|
||||
emits('success', e.msg)
|
||||
}
|
||||
const uploadError = (e) => {
|
||||
console.log("uploadError", e)
|
||||
emits('error')
|
||||
}
|
||||
</script>
|
||||
@ -116,12 +116,12 @@
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
height="100%"
|
||||
style="width: 100%"
|
||||
:data="corpusList"
|
||||
table-layout="auto"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="语料名称" align="center" prop="corpusName" />
|
||||
<el-table-column label="语料名称" width="200" align="center" prop="corpusName" />
|
||||
<el-table-column
|
||||
label="语音文本"
|
||||
align="center"
|
||||
@ -129,6 +129,13 @@
|
||||
min-width="200"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
label="音频地址"
|
||||
align="center"
|
||||
prop="minioUrl"
|
||||
min-width="200"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
label="语音文件路径"
|
||||
align="center"
|
||||
@ -136,8 +143,8 @@
|
||||
min-width="150"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column label="音色" align="center" prop="voiceType" />
|
||||
<el-table-column label="语种" align="center" prop="dialect" />
|
||||
<el-table-column width="80" label="音色" align="center" prop="voiceType" />
|
||||
<el-table-column width="100" label="语种" align="center" prop="dialect" />
|
||||
<el-table-column
|
||||
label="预期结果"
|
||||
align="center"
|
||||
@ -145,8 +152,8 @@
|
||||
min-width="150"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column label="顺序" align="center" prop="sortOrder" />
|
||||
<el-table-column label="状态" align="center" prop="status" />
|
||||
<el-table-column width="80" label="顺序" align="center" prop="sortOrder" />
|
||||
<el-table-column width="80" label="状态" align="center" prop="status" />
|
||||
<el-table-column
|
||||
label="备注"
|
||||
align="center"
|
||||
@ -156,9 +163,8 @@
|
||||
/>
|
||||
<el-table-column
|
||||
label="操作"
|
||||
align="center"
|
||||
class-name="small-padding fixed-width"
|
||||
width="280"
|
||||
width="200"
|
||||
fixed="right"
|
||||
>
|
||||
<template #default="scope">
|
||||
@ -180,17 +186,10 @@
|
||||
>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
icon="Upload"
|
||||
@click="handleUpload(scope.row)"
|
||||
v-hasPermi="['vi:corpus:upload']"
|
||||
>上传</el-button
|
||||
>
|
||||
<el-button
|
||||
link
|
||||
v-if="scope.row.minioUrl"
|
||||
type="primary"
|
||||
icon="VideoPlay"
|
||||
@click="handlePlay(scope.row)"
|
||||
@click="handlePlay(scope.row.minioUrl)"
|
||||
v-hasPermi="['vi:corpus:play']"
|
||||
>试听</el-button
|
||||
>
|
||||
@ -220,6 +219,17 @@
|
||||
placeholder="请输入内容"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="上传文件">
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
effect="dark"
|
||||
:content="form.minioUrl"
|
||||
placement="top-start"
|
||||
>
|
||||
<span class="minioUrl" v-if="form.minioUrl">{{ form.minioUrl }}</span>
|
||||
</el-tooltip>
|
||||
<UploadFile @success="uploadSuccess" @error="uploadError" />
|
||||
</el-form-item>
|
||||
<el-form-item label="文件路径" prop="audioPath">
|
||||
<el-input
|
||||
v-model="form.audioPath"
|
||||
@ -294,6 +304,8 @@ import {
|
||||
updateCorpus,
|
||||
} from "@/api/vi/corpus";
|
||||
import { useContainerHeight } from "@/hooks/tableHeight";
|
||||
import { usePlayAudio } from '@/hooks/usePlayAudio'
|
||||
import UploadFile from "../../components/UploadFile.vue"
|
||||
|
||||
const topContainerRef = ref();
|
||||
const containerHeight = useContainerHeight(topContainerRef);
|
||||
@ -367,6 +379,7 @@ function reset() {
|
||||
form.value = {
|
||||
corpusId: null,
|
||||
corpusName: null,
|
||||
minioUrl: null,
|
||||
type: "WAKEUP",
|
||||
textContent: null,
|
||||
audioPath: null,
|
||||
@ -457,12 +470,17 @@ function handleDelete(row) {
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
const handleUpload = (row) => {
|
||||
console.log("上传");
|
||||
};
|
||||
const uploadSuccess = (data) => {
|
||||
form.value.minioUrl = data
|
||||
}
|
||||
|
||||
const handlePlay = () => {
|
||||
console.log("播放");
|
||||
const uploadError = () => {
|
||||
console.log("uploadError")
|
||||
}
|
||||
|
||||
const handlePlay = (url) => {
|
||||
const { playAudio } = usePlayAudio()
|
||||
playAudio(url)
|
||||
};
|
||||
|
||||
/** 导出按钮操作 */
|
||||
@ -478,3 +496,12 @@ function handleExport() {
|
||||
|
||||
getList();
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.minioUrl {
|
||||
display: inline-block;
|
||||
width: 80%;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
@ -116,8 +116,8 @@
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
height="100%"
|
||||
style="width: 100%"
|
||||
:data="corpusList"
|
||||
table-layout="auto"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
@ -129,6 +129,13 @@
|
||||
min-width="200"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
label="音频地址"
|
||||
align="center"
|
||||
prop="minioUrl"
|
||||
min-width="200"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
label="语音文件路径"
|
||||
align="center"
|
||||
@ -155,9 +162,8 @@
|
||||
/>
|
||||
<el-table-column
|
||||
label="操作"
|
||||
align="center"
|
||||
class-name="small-padding fixed-width"
|
||||
width="280"
|
||||
width="200"
|
||||
fixed="right"
|
||||
>
|
||||
<template #default="scope">
|
||||
@ -180,8 +186,9 @@
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-if="scope.row.minioUrl"
|
||||
icon="VideoPlay"
|
||||
@click="handlePlay(scope.row)"
|
||||
@click="handlePlay(scope.row.minioUrl)"
|
||||
v-hasPermi="['vi:corpus:play']"
|
||||
>试听</el-button
|
||||
>
|
||||
@ -211,6 +218,17 @@
|
||||
placeholder="请输入内容"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="上传文件">
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
effect="dark"
|
||||
:content="form.minioUrl"
|
||||
placement="top-start"
|
||||
>
|
||||
<span class="minioUrl" v-if="form.minioUrl">{{ form.minioUrl }}</span>
|
||||
</el-tooltip>
|
||||
<UploadFile @success="uploadSuccess" @error="uploadError" />
|
||||
</el-form-item>
|
||||
<el-form-item label="文件路径" prop="audioPath">
|
||||
<el-input
|
||||
v-model="form.audioPath"
|
||||
@ -285,6 +303,8 @@ import {
|
||||
updateCorpus,
|
||||
} from "@/api/vi/corpus";
|
||||
import { useContainerHeight } from "@/hooks/tableHeight";
|
||||
import { usePlayAudio } from '@/hooks/usePlayAudio'
|
||||
import UploadFile from "../../../components/UploadFile.vue";
|
||||
|
||||
const topContainerRef = ref();
|
||||
const containerHeight = useContainerHeight(topContainerRef);
|
||||
@ -361,6 +381,7 @@ function reset() {
|
||||
form.value = {
|
||||
corpusId: null,
|
||||
corpusName: null,
|
||||
minioUrl: null,
|
||||
type: "TEST",
|
||||
textContent: null,
|
||||
audioPath: null,
|
||||
@ -451,8 +472,17 @@ function handleDelete(row) {
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
const handlePlay = () => {
|
||||
console.log("播放");
|
||||
const uploadSuccess = (data) => {
|
||||
form.value.minioUrl = data
|
||||
}
|
||||
|
||||
const uploadError = () => {
|
||||
console.log("uploadError")
|
||||
}
|
||||
|
||||
const handlePlay = (url) => {
|
||||
const { playAudio } = usePlayAudio()
|
||||
playAudio(url)
|
||||
};
|
||||
|
||||
/** 导出按钮操作 */
|
||||
@ -478,4 +508,12 @@ defineExpose({
|
||||
.app-container {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.minioUrl {
|
||||
display: inline-block;
|
||||
width: 80%;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in New Issue
Block a user