feat: 功能
This commit is contained in:
@@ -105,15 +105,15 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, computed, onMounted, nextTick } from 'vue'
|
||||
import { ref, reactive, computed, onMounted } from 'vue'
|
||||
import { message, Modal } from 'ant-design-vue'
|
||||
import { PlusOutlined, SearchOutlined, UploadOutlined, PlayCircleOutlined } from '@ant-design/icons-vue'
|
||||
import { VoiceService } from '@/api/voice'
|
||||
import { MaterialService } from '@/api/material'
|
||||
import { useUpload } from '@/composables/useUpload'
|
||||
import useVoiceText from '@gold/hooks/web/useVoiceText'
|
||||
import dayjs from 'dayjs'
|
||||
import BasicLayout from '@/layouts/components/BasicLayout.vue'
|
||||
import { MaterialService } from '@/api/material'
|
||||
import { VoiceService } from '@/api/voice'
|
||||
import { useUpload } from '@/composables/useUpload'
|
||||
import useVoiceText from '@gold/hooks/web/useVoiceText'
|
||||
|
||||
// ========== 常量 ==========
|
||||
|
||||
@@ -125,10 +125,14 @@ const DEFAULT_FORM_DATA = {
|
||||
language: 'zh-CN',
|
||||
gender: 'female',
|
||||
note: '',
|
||||
text: '', // 音频文本
|
||||
fileUrl: '' // 文件URL(用于获取音频文本)
|
||||
text: '',
|
||||
fileUrl: ''
|
||||
}
|
||||
|
||||
const MAX_FILE_SIZE = 50 * 1024 * 1024
|
||||
const VALID_AUDIO_TYPES = ['audio/mpeg', 'audio/wav', 'audio/wave', 'audio/x-wav', 'audio/aac', 'audio/mp4', 'audio/flac', 'audio/ogg']
|
||||
const VALID_AUDIO_EXTENSIONS = ['.mp3', '.wav', '.aac', '.m4a', '.flac', '.ogg']
|
||||
|
||||
// ========== 响应式数据 ==========
|
||||
const loading = ref(false)
|
||||
const submitting = ref(false)
|
||||
@@ -216,31 +220,31 @@ const loadVoiceList = async () => {
|
||||
}
|
||||
|
||||
// ========== 搜索和分页 ==========
|
||||
const handleSearch = () => {
|
||||
function handleSearch() {
|
||||
pagination.current = 1
|
||||
loadVoiceList()
|
||||
}
|
||||
|
||||
const handleReset = () => {
|
||||
function handleReset() {
|
||||
searchParams.name = ''
|
||||
pagination.current = 1
|
||||
loadVoiceList()
|
||||
}
|
||||
|
||||
const handleTableChange = (pag) => {
|
||||
function handleTableChange(pag) {
|
||||
pagination.current = pag.current
|
||||
pagination.pageSize = pag.pageSize
|
||||
loadVoiceList()
|
||||
}
|
||||
|
||||
// ========== CRUD 操作 ==========
|
||||
const handleCreate = () => {
|
||||
function handleCreate() {
|
||||
formMode.value = 'create'
|
||||
resetForm()
|
||||
modalVisible.value = true
|
||||
}
|
||||
|
||||
const handleEdit = async (record) => {
|
||||
async function handleEdit(record) {
|
||||
formMode.value = 'edit'
|
||||
try {
|
||||
const res = await VoiceService.get(record.id)
|
||||
@@ -252,13 +256,13 @@ const handleEdit = async (record) => {
|
||||
modalVisible.value = true
|
||||
}
|
||||
|
||||
const handleDelete = (record) => {
|
||||
function handleDelete(record) {
|
||||
Modal.confirm({
|
||||
title: '确认删除',
|
||||
content: `确定要删除配音「${record.name}」吗?此操作不可恢复。`,
|
||||
okButtonProps: { danger: true },
|
||||
centered: true,
|
||||
onOk: async () => {
|
||||
onOk: async function() {
|
||||
try {
|
||||
const res = await VoiceService.delete(record.id)
|
||||
if (res.code !== 0) return message.error(res.msg || '删除失败')
|
||||
@@ -274,7 +278,7 @@ const handleDelete = (record) => {
|
||||
}
|
||||
|
||||
// ========== 音频播放 ==========
|
||||
const handlePlayAudio = (record) => {
|
||||
function handlePlayAudio(record) {
|
||||
if (record.fileUrl && audioPlayer.value) {
|
||||
audioPlayer.value.src = record.fileUrl
|
||||
audioPlayer.value.play()
|
||||
@@ -284,20 +288,20 @@ const handlePlayAudio = (record) => {
|
||||
}
|
||||
|
||||
// ========== 文件上传 ==========
|
||||
const handleBeforeUpload = (file) => {
|
||||
const MAX_FILE_SIZE = 50 * 1024 * 1024
|
||||
function handleBeforeUpload(file) {
|
||||
if (file.size > MAX_FILE_SIZE) {
|
||||
message.error('文件大小不能超过 50MB')
|
||||
return false
|
||||
}
|
||||
|
||||
const validTypes = ['audio/mpeg', 'audio/wav', 'audio/wave', 'audio/x-wav', 'audio/aac', 'audio/mp4', 'audio/flac', 'audio/ogg']
|
||||
const validExtensions = ['.mp3', '.wav', '.aac', '.m4a', '.flac', '.ogg']
|
||||
const fileName = file.name.toLowerCase()
|
||||
const fileType = file.type.toLowerCase()
|
||||
|
||||
const isValidType = validTypes.some(type => fileType.includes(type)) ||
|
||||
validExtensions.some(ext => fileName.endsWith(ext))
|
||||
const isValidType = VALID_AUDIO_TYPES.some(function(type) {
|
||||
return fileType.includes(type)
|
||||
}) || VALID_AUDIO_EXTENSIONS.some(function(ext) {
|
||||
return fileName.endsWith(ext)
|
||||
})
|
||||
|
||||
if (!isValidType) {
|
||||
message.error('请上传音频文件(MP3、WAV、AAC、M4A、FLAC、OGG)')
|
||||
@@ -307,7 +311,8 @@ const handleBeforeUpload = (file) => {
|
||||
return true
|
||||
}
|
||||
|
||||
const handleCustomUpload = async (options) => {
|
||||
// ========== 文件上传相关 ==========
|
||||
async function handleCustomUpload(options) {
|
||||
const { file, onSuccess, onError } = options
|
||||
|
||||
try {
|
||||
@@ -315,17 +320,14 @@ const handleCustomUpload = async (options) => {
|
||||
fileCategory: 'voice',
|
||||
groupId: null,
|
||||
coverBase64: null,
|
||||
onStart: () => {},
|
||||
onProgress: () => {},
|
||||
onSuccess: async (id, fileUrl) => {
|
||||
onSuccess: async function(id, fileUrl) {
|
||||
formData.fileId = id
|
||||
formData.fileUrl = fileUrl // 保存文件URL
|
||||
formData.fileUrl = fileUrl
|
||||
message.success('文件上传成功')
|
||||
// 通过fileId获取播放URL用于语音识别
|
||||
await fetchAudioTextById(id)
|
||||
onSuccess?.({ code: 0, data: id }, file)
|
||||
},
|
||||
onError: (error) => {
|
||||
onError: function(error) {
|
||||
const errorMsg = error.message || '上传失败,请稍后重试'
|
||||
message.error(errorMsg)
|
||||
onError?.(error)
|
||||
@@ -339,13 +341,10 @@ const handleCustomUpload = async (options) => {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 通过fileId获取音频文本
|
||||
const fetchAudioTextById = async (fileId) => {
|
||||
async function fetchAudioTextById(fileId) {
|
||||
if (!fileId) return
|
||||
try {
|
||||
// 获取音频播放URL
|
||||
const res = await MaterialService.getAudioPlayUrl(fileId)
|
||||
if (res.code === 0 && res.data) {
|
||||
const rawFileUrl = res.data
|
||||
@@ -363,39 +362,22 @@ const fetchAudioTextById = async (fileId) => {
|
||||
}
|
||||
}
|
||||
|
||||
// 获取音频文本
|
||||
const fetchAudioText = async (fileUrl) => {
|
||||
if (!fileUrl) return
|
||||
try {
|
||||
// 阿里云语音识别服务无法访问预签名URL,使用原始URL
|
||||
const rawFileUrl = extractRawUrl(fileUrl)
|
||||
const results = await getVoiceText([{ audio_url: rawFileUrl }])
|
||||
if (results && results.length > 0) {
|
||||
const text = results[0].value
|
||||
formData.text = text
|
||||
if (text) {
|
||||
message.success('音频文本获取成功')
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取音频文本失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
const handleFileListChange = (info) => {
|
||||
function handleFileListChange(info) {
|
||||
const { fileList: newFileList } = info
|
||||
if (newFileList) {
|
||||
fileList.value = newFileList.filter(item => item.status !== 'removed')
|
||||
fileList.value = newFileList.filter(function(item) {
|
||||
return item.status !== 'removed'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const handleRemoveFile = () => {
|
||||
function handleRemoveFile() {
|
||||
formData.fileId = null
|
||||
fileList.value = []
|
||||
}
|
||||
|
||||
// ========== 表单操作 ==========
|
||||
const handleSubmit = async () => {
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
await formRef.value.validate()
|
||||
} catch {
|
||||
@@ -412,7 +394,7 @@ const handleSubmit = async () => {
|
||||
language: formData.language,
|
||||
gender: formData.gender,
|
||||
note: formData.note,
|
||||
text: formData.text // 传入音频文本
|
||||
text: formData.text
|
||||
}
|
||||
: {
|
||||
id: formData.id,
|
||||
@@ -443,19 +425,19 @@ const handleSubmit = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const handleCancel = () => {
|
||||
function handleCancel() {
|
||||
modalVisible.value = false
|
||||
resetForm()
|
||||
}
|
||||
|
||||
const resetForm = () => {
|
||||
function resetForm() {
|
||||
Object.assign(formData, { ...DEFAULT_FORM_DATA })
|
||||
fileList.value = []
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
|
||||
// ========== 生命周期 ==========
|
||||
onMounted(() => {
|
||||
onMounted(function() {
|
||||
loadVoiceList()
|
||||
})
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user