feat: 功能

This commit is contained in:
2026-02-04 01:18:16 +08:00
parent f8e40c039d
commit 0e1b6fe643
19 changed files with 1472 additions and 1008 deletions

View File

@@ -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>