@@ -194,7 +194,7 @@
< div v-if = "scope.row.audioFileUrl" >
< div style = "display: flex; align-items: center;" >
< el-tag type = "success" size = "mini" style = "margin-right: 8px;" >
< i class = "el-icon-video-play" > < / i >
< i class = "el-icon-video-play" / >
有音频
< / el-tag >
< el-link
@@ -203,15 +203,15 @@
target = "_blank"
:underline = "false"
>
< i class = "el-icon-document" > < / i >
< i class = "el-icon-document" / >
{ { scope . row . audioFileOriginalName || getAudioFileName ( scope . row . audioFileUrl ) } }
< / el-link >
< el-button
type = "text"
size = "mini"
icon = "el-icon-download"
@click ="downloadAudioFile(scope.row)"
style = "margin-left: 10px;"
@click ="downloadAudioFile(scope.row)"
>
下载
< / el-button >
@@ -219,12 +219,32 @@
< / div >
< div v-else >
< el -tag type = "info" size = "mini" >
< i class = "el-icon-warning" > < / i >
< i class = "el-icon-warning" / >
暂无音频文件
< / el-tag >
< / div >
< / template >
< / el-table-column >
< el-table-column label = "AI分析链接" prop = "aiAnalysisUrl" min -width = " 150 " >
< template slot -scope = " scope " >
< div v-if = "scope.row.audioFileUrl" >
< el -button
type = "primary"
size = "mini"
icon = "el-icon-link"
@click ="showAiAnalysisUrl(scope.row)"
>
获取链接
< / el-button >
< / div >
< div v-else >
< el -tag type = "info" size = "mini" >
< i class = "el-icon-warning" / >
暂无音频
< / el-tag >
< / div >
< / template >
< / el-table-column >
< el-table-column label = "操作" width = "280" fixed = "right" >
< template slot -scope = " scope " >
< el-button
@@ -380,6 +400,7 @@
width = "800px"
:close-on-click-modal = "false"
@close ="handleEditDialogClose"
@open ="handleEditDialogOpen"
>
< el-form ref = "audioForm" :model = "audioForm" :rules = "audioRules" label -width = " 120px " >
< el-row :gutter = "20" >
@@ -495,127 +516,104 @@
< el-dialog
title = "上传音频"
:visible.sync = "uploadDialogVisible"
width = "8 00px"
width = "5 00px"
:close-on-click-modal = "false"
@close ="handleUploadDialogClose"
@open ="handleUploadDialogOpen"
>
< el-form ref = "uploadForm" :model = "uploadForm" :rules = "uploadRules" label -width = " 120px ">
< el-row :gutter = "20" >
< el-col :span = "12" >
< el-form-item label = "录音名称" prop = "recordingName" >
< el-input v-model = "uploadForm.recordingName" placeholder="请输入录音名称" / >
< / el-form-item >
< / el-col >
< el-col :span = "12" >
< el-form-item label = "所属门店" prop = "dealershipId" >
< el-select v-model = "uploadForm.dealershipId" placeholder="请选择所属门店" style="width: 100%" >
< el -option
v-for = "item in dealershipOptions"
:key = "item.value"
:label = "item.label"
:value = "item.value"
/ >
< / el-select >
< / el-form-item >
< / el-col >
< / el-row >
< el-row :gutter = "20" >
< el-col :span = "12" >
< el-form-item label = "所属项目" prop = "projectId" >
< el-select v-model = "uploadForm.projectId" placeholder="请选择所属项目" style="width: 100%" >
< el -option
v-for = "item in projectOptions"
:key = "item.value"
:label = "item.label"
:value = "item.value"
/ >
< / el-select >
< / el-form-item >
< / el-col >
< el-col :span = "12" >
< el-form-item label = "所属销售" prop = "salesId" >
< el-select v-model = "uploadForm.salesId" placeholder="请选择销售" style="width: 100%" >
< el -option
v-for = "item in salesOptions"
:key = "item.value"
:label = "item.label"
:value = "item.value"
/ >
< / el-select >
< / el-form-item >
< / el-col >
< / el-row >
< el-row :gutter = "20" >
< el-col :span = "12" >
< el-form-item label = "客户姓名" prop = "customerName" >
< el-input v-model = "uploadForm.customerName" placeholder="请输入客户姓名" / >
< / el-form-item >
< / el-col >
< el-col :span = "12" >
< el-form-item label = "客户手机号" prop = "customerPhone" >
< el-input v-model = "uploadForm.customerPhone" placeholder="请输入客户手机号" / >
< / el-form-item >
< / el-col >
< / el-row >
< el-row :gutter = "20" >
< el-col :span = "12" >
< el-form-item label = "意向级别" prop = "intentionLevel" >
< el-select v-model = "uploadForm.intentionLevel" placeholder="请选择意向级别" style="width: 100%" >
< el -option label = "A级" value = "A" / >
< el-option label = "B级" value = "B" / >
< el-option label = "C级" value = "C" / >
< el-option label = "D级" value = "D" / >
< / el-select >
< / el-form-item >
< / el-col >
< el-col :span = "12" >
< el-form-item label = "录音时间" prop = "recordingTime" >
< el-date-picker
v-model = "uploadForm.recordingTime"
type = "datetime"
placeholder = "选择录音时间"
format = "yyyy-MM-dd HH:mm:ss"
value -format = " yyyy -MM -dd HH : mm : ss "
style = "width: 100%"
/ >
< / el-form-item >
< / el-col >
< / el-row >
< el-form-item label = "音频文件" prop = "audioFile" >
< div class = "upload-container ">
< el-upload
ref = "audioUpload"
:action = "uploadAction"
:headers = "uploadHeaders"
:data = "uploadData"
name = "file"
:before-upload = "beforeAudioUpload"
:on-success = "handleUploadSuccess"
:on-error = "handleUploadError"
:on-progress = "handleUploadProgress"
:on-change = "handleFileChange"
:on-remove = "handleFileRemove"
:file-list = "uploadFileList"
:auto-upload = "fals e"
:auto-upload = "tru e"
:limit = "1"
accept = ".mp3,.wav,.m4a,.aac,.flac"
drag
:show-file-list = "true"
class = "upload-demo"
>
< i class = "el-icon-upload" > < / i >
< i class = "el-icon-upload" / >
< div class = "el-upload__text" > 将音频文件拖到此处 , 或 < em > 点击上传 < / em > < / div >
< div class = "el-upload__tip" slot = "tip" >
< div slot = "tip" class= "el-upload__tip" >
支持 mp3 、 wav 、 m4a 、 aac 、 flac 格式 , 文件大小不超过 100 MB
< / div >
< / el-upload >
< / el-form-item >
< el-form-item label = "录音描述" prop = "description" >
< el-input
v-model = "uploadForm.description"
type = "textarea"
:rows = "3"
placeholder = "请输入录音描述"
/ >
< / el-form-item >
< / el-form >
< / div >
< div slot = "footer" class = "dialog-footer" >
< el-button @click ="uploadDialogVisible = false" > 取 消 < / el -button >
< el-button type = "primary" @click ="handleUploadSubmit" :loading = "uploadLoading" > 确 定 < / el-button >
< el-button @click ="uploadDialogVisible = false" > 关 闭 < / el -button >
< / div >
< / el-dialog >
<!-- AI分析链接对话框 -- >
< el-dialog
title = "音频下载链接"
:visible.sync = "aiAnalysisUrlVisible"
width = "500px"
:close-on-click-modal = "false"
>
< div class = "ai-analysis-container" >
< div class = "ai-analysis-info" >
< p > 复制以下下载链接给阿里大模型进行语音分析 : < / p >
< / div >
< div class = "ai-analysis-url" >
< el-input
v-model = "aiAnalysisUrl"
readonly
placeholder = "音频下载链接"
style = "margin-bottom: 15px;"
>
< el-button
slot = "append"
type = "primary"
icon = "el-icon-copy-document"
@click ="copyAiAnalysisUrl"
>
复制
< / el-button >
< / el-input >
< div class = "download-actions" >
< el-button
type = "success"
icon = "el-icon-download"
@click ="testDownload"
style = "margin-right: 10px;"
>
测试下载
< / el-button >
< el-button
type = "warning"
icon = "el-icon-refresh"
@click ="regenerateUrl"
>
重新生成链接
< / el-button >
< / div >
< div class = "download-tips" >
< p > < strong > 使用说明 : < / strong > < / p >
< ul >
< li > 点击 "测试下载" 按钮可以直接下载音频文件到本地 < / li >
< li > 复制上方链接给阿里大模型进行语音分析 < / li >
< li > 如果链接无法访问 , 点击 "重新生成链接" < / li >
< / ul >
< / div >
< / div >
< / div >
< div slot = "footer" class = "dialog-footer" >
< el-button @click ="aiAnalysisUrlVisible = false" > 关 闭 < / el -button >
< / div >
< / el-dialog >
@@ -638,7 +636,7 @@
< div class = "audio-player" >
< div v-if = "audioLoading" class="audio-loading" >
< i class = "el-icon-loading" > < / i >
< i class = "el-icon-loading" / >
< span > 音频加载中 ... < / span >
< / div >
< audio
@@ -646,13 +644,13 @@
:src = "currentAudio.audioUrl"
controls
preload = "metadata"
style = "width: 100%;"
@loadstart ="handleAudioLoadStart"
@loadedmetadata ="handleAudioLoaded"
@timeupdate ="handleTimeUpdate"
@ended ="handleAudioEnded"
@error ="handleAudioError"
@canplay ="handleAudioCanPlay"
style = "width: 100%;"
>
您的浏览器不支持音频播放
< / audio >
@@ -664,23 +662,23 @@
< el-button
type = "primary"
icon = "el-icon-video-play"
@click ="playAudio"
:disabled = "!currentAudio.audioUrl"
@click ="playAudio"
>
播放
< / el-button >
< el-button
type = "warning"
icon = "el-icon-video-pause"
@click ="pauseAudio"
:disabled = "!currentAudio.audioUrl"
@click ="pauseAudio"
>
暂停
< / el-button >
< el-button
icon = "el-icon-refresh"
@click ="restartAudio"
:disabled = "!currentAudio.audioUrl"
@click ="restartAudio"
>
重新播放
< / el-button >
@@ -701,7 +699,7 @@
< / el-row >
< / div >
< div class = "audio-progress" v-if = "audioDuration > 0" >
< div v-if = "audioDuration > 0" class="audio-progress" >
< div class = "progress-info" >
< span > { { formatTime ( currentTime ) } } / { { formatTime ( audioDuration ) } } < / span >
< span > { { Math . round ( ( currentTime / audioDuration ) * 100 ) } } % < / span >
@@ -713,7 +711,7 @@
/ >
< / div >
< div class = "audio-notes" v-if = "currentAudio.description" >
< div v-if = "currentAudio.description" class="audio-notes" >
< h4 > 录音备注 : < / h4 >
< p > { { currentAudio . description } } < / p >
< / div >
@@ -721,7 +719,7 @@
< div slot = "footer" class = "dialog-footer" >
< el-button @click ="playDialogVisible = false" > 关 闭 < / el -button >
< el-button type = "primary" @click ="downloadAudio" :disabled= "!currentAudio.audioUrl" >
< el-button type = "primary" :disabled = "!currentAudio.audioUrl" @click ="downloadAudio" >
下载音频
< / el -button >
< / div >
@@ -762,6 +760,9 @@ export default {
uploadDialogVisible : false ,
uploadLoading : false ,
uploadFileList : [ ] ,
// AI分析链接相关
aiAnalysisUrlVisible : false ,
aiAnalysisUrl : '' ,
// 音频播放相关
playDialogVisible : false ,
currentAudio : {
@@ -778,25 +779,13 @@ export default {
audioDuration : 0 ,
playbackSpeed : '1.0' ,
audioLoading : false ,
uploadAction : process . env . VUE _APP _BASE _API + '/audio/upload' ,
uploadAction : '/api /audio/upload' ,
uploadHeaders : {
'Authorization' : 'Bearer ' + this . $store . getters . token
} ,
uploadData : { } ,
uploadRules : {
recordingName : [
{ required : true , message : '请输入录音名称' , trigger : 'blur' }
] ,
customerName : [
{ required : true , message : '请输入客户姓名' , trigger : 'blur' }
] ,
customerPhone : [
{ required : true , message : '请输入客户手机号' , trigger : 'blur' } ,
{ pattern : /^1[3-9]\d{9}$/ , message : '请输入正确的手机号' , trigger : 'blur' }
] ,
audioFile : [
{ required : true , message : '请选择音频文件' , trigger : 'change' }
]
uploadData : {
type : 'audio' ,
audioId : ''
} ,
// 查询参数
queryParams : {
@@ -857,8 +846,8 @@ export default {
dealershipName : '' ,
projectName : '' ,
speechModel : '' ,
uploadStatus : '' ,
syncStatus : '' ,
uploadStatus : '已上传 ' ,
syncStatus : '未同步 ' ,
isMerged : false ,
description : '' ,
dealershipId : '' ,
@@ -880,19 +869,13 @@ export default {
duration : [
{ required : true , message : '请输入录音时长' , trigger : 'blur' }
]
}
}
} ,
// 上传音频表单
u ploadForm : {
recordingName : '' ,
dealershipId : '' ,
projectId : '' ,
salesId : '' ,
customerName : '' ,
customerPhone : '' ,
intentionLevel : '' ,
recordingTime : '' ,
audioFile : null ,
description : ''
watch : {
playbackSpeed ( newSpeed ) {
if ( this . $refs . audioPlayer ) {
this . $refs . audioPlayer . playbackRate = parseFloat ( newSpeed )
}
}
} ,
@@ -902,13 +885,6 @@ export default {
this . getProjectOptions ( )
this . getSalesOptions ( )
} ,
watch : {
playbackSpeed ( newSpeed ) {
if ( this . $refs . audioPlayer ) {
this . $refs . audioPlayer . playbackRate = parseFloat ( newSpeed )
}
}
} ,
methods : {
// 获取列表数据
getList ( ) {
@@ -1268,16 +1244,41 @@ export default {
// 获取录音详细信息
getAudioById ( row . id ) . then ( response => {
console . log ( '编辑录音 111111111111111111111' , response )
if ( response . code === 20000 ) {
const audioData = response . data || row
this . audioForm = {
... audioData ,
// 确保ID字段正确
dealershipI d: audioData . dealershipId || audioData . dealership ? . id,
projectId : audioData . projectId || audioData . project ? . id ,
salesId : audioData . salesId || audioData . sales ? . id
const audioData = response . data . audio || row
console . log ( '获取到的录音数据:' , audioData )
// 确保所有字段都正确映射
const formData = {
i d: audioData . id ,
recordingName : audioData . recordingName || '' ,
recordingTime : audioData . recordingTime || '' ,
salesName : audioData . salesName || '' ,
duration : audioData . duration || 0 ,
customerName : audioData . customerName || '' ,
customerPhone : audioData . customerPhone || '' ,
uploadTime : audioData . uploadTime || '' ,
intentionLevel : audioData . intentionLevel || '' ,
dealershipName : audioData . dealershipName || '' ,
projectName : audioData . projectName || '' ,
speechModel : audioData . speechModel || '' ,
uploadStatus : audioData . uploadStatus || '' ,
syncStatus : audioData . syncStatus || '' ,
isMerged : audioData . isMerged || false ,
description : audioData . description || audioData . remarks || '' ,
dealershipId : audioData . dealershipId || audioData . dealership ? . id || '' ,
projectId : audioData . projectId || audioData . project ? . id || '' ,
salesId : audioData . salesId || audioData . sales ? . id || ''
}
console . log ( '设置后的表单数据 22222222222222222' , this . audioForm )
// 使用Object.assign确保响应式更新
Object . assign ( this . audioForm , formData )
console . log ( '设置后的表单数据:' , this . audioForm )
// 强制更新视图
this . $nextTick ( ( ) => {
this . editDialogVisible = true
} )
} else {
this . $message . error ( response . message || '获取录音信息失败' )
}
@@ -1285,17 +1286,64 @@ export default {
} ) . catch ( error => {
console . error ( '获取录音信息失败:' , error )
// 如果API调用失败, 使用行数据
this . audioForm = {
... row ,
dealershipId : row . dealershipId || row . dealership ? . id ,
projectId : row . projectId || row . project ? . id ,
salesId : row . salesId || row . sales ? . id
const formData = {
id : row . id ,
recordingName : row . recordingName || '' ,
recordingTime : row . recordingTime || '' ,
salesName : row . salesName || '' ,
duration : row . duration || 0 ,
customerName : row . customerName || '' ,
customerPhone : row . customerPhone || '' ,
uploadTime : row . uploadTime || '' ,
intentionLevel : row . intentionLevel || '' ,
dealershipName : row . dealershipName || '' ,
projectName : row . projectName || '' ,
speechModel : row . speechModel || '' ,
uploadStatus : row . uploadStatus || '' ,
syncStatus : row . syncStatus || '' ,
isMerged : row . isMerged || false ,
description : row . description || row . remarks || '' ,
dealershipId : row . dealershipId || row . dealership ? . id || '' ,
projectId : row . projectId || row . project ? . id || '' ,
salesId : row . salesId || row . sales ? . id || ''
}
// 使用Object.assign确保响应式更新
Object . assign ( this . audioForm , formData )
console . log ( '使用行数据设置的表单数据:' , this . audioForm )
// 强制更新视图
this . $nextTick ( ( ) => {
this . editDialogVisible = true
} )
this . loading = false
} )
} ,
// 编辑对话框打开
handleEditDialogOpen ( ) {
console . log ( '编辑对话框打开,当前表单数据:' , this . audioForm )
// 强制更新表单
this . $nextTick ( ( ) => {
if ( this . $refs . audioForm ) {
this . $refs . audioForm . clearValidate ( )
// 强制重新渲染表单
this . $forceUpdate ( )
}
} )
} ,
// 上传对话框打开
handleUploadDialogOpen ( ) {
console . log ( '上传对话框打开' )
// 清空上传组件
this . $nextTick ( ( ) => {
if ( this . $refs . audioUpload ) {
this . $refs . audioUpload . clearFiles ( )
}
} )
} ,
// 编辑对话框关闭
handleEditDialogClose ( ) {
this . $refs . audioForm ? . resetFields ( )
@@ -1312,8 +1360,8 @@ export default {
dealershipName : '' ,
projectName : '' ,
speechModel : '' ,
uploadStatus : '' ,
syncStatus : '' ,
uploadStatus : '已上传 ' ,
syncStatus : '未同步 ' ,
isMerged : false ,
description : '' ,
dealershipId : '' ,
@@ -1346,6 +1394,7 @@ export default {
projectId : '' ,
salesId : ''
}
console . log ( '添加录音时的表单数据:' , this . audioForm )
this . editDialogVisible = true
} ,
@@ -1397,92 +1446,116 @@ export default {
// 上传音频
handleUpload ( ) {
this . uploadForm = {
recordingName : '' ,
dealershipId : ' ' ,
projectId : '' ,
salesId : '' ,
customerName : '' ,
customerPhone : '' ,
intentionLevel : '' ,
recordingTime : '' ,
audioFile : null ,
description : ''
// 检查是否有选中的录音
if ( this . selectedIds . length === 0 ) {
this . $message . warning ( '请先选择要上传音频的录音记录 ')
return
}
if ( this . selectedIds . length > 1 ) {
this . $message . warning ( '一次只能为一个录音上传音频文件' )
return
}
const audioId = this . selectedIds [ 0 ]
console . log ( '上传音频 - 选中的录音ID:' , audioId )
// 设置audioId到上传数据中
this . uploadData . audioId = audioId
this . uploadFileList = [ ]
console . log ( '上传音频 - 重置后的文件列表:' , this . uploadFileList )
console . log ( '上传音频 - 设置的上传数据:' , this . uploadData )
this . uploadDialogVisible = true
} ,
// 上传音频对话框关闭
handleUploadDialogClose ( ) {
this . $refs . uploadForm ? . resetFields ( )
console . log ( '上传音频对话框关闭 - 重置文件列表' )
this . uploadFileList = [ ]
// 重置audioId
this . uploadData . audioId = ''
console . log ( '上传音频对话框关闭 - 重置后的文件列表:' , this . uploadFileList )
console . log ( '上传音频对话框关闭 - 重置后的上传数据:' , this . uploadData )
} ,
// 上传前检查文件大小和类型
beforeAudioUpload ( file ) {
console . log ( '上传前检查 - 文件:' , file )
console . log ( '上传前检查 - 上传URL:' , this . uploadAction )
console . log ( '上传前检查 - 请求头:' , this . uploadHeaders )
console . log ( '上传前检查 - 当前audioId:' , this . uploadData . audioId )
// 检查是否有audioId
if ( ! this . uploadData . audioId ) {
this . $message . error ( '缺少录音ID, 请重新选择录音记录' )
return false
}
const isLt100M = file . size / 1024 / 1024 < 100
if ( ! isLt100M ) {
this . $message . error ( '上传文件大小不能超过 100MB!' )
return false
}
return isLt100M
console . log ( '上传前检查 - 文件大小检查结果:' , isLt100M )
console . log ( '上传前检查 - 最终上传数据:' , this . uploadData )
return true
} ,
// 上传成功
handleUploadSuccess ( response , file , fileList ) {
if ( resp onse. code === 20000 ) {
c onsol e. log ( '上传成功 - 响应:' , response )
console . log ( '上传成功 - 文件:' , file )
console . log ( '上传成功 - 文件列表:' , fileList )
console . log ( '上传成功 - 使用的audioId:' , this . uploadData . audioId )
// 检查响应格式
if ( response && ( response . code === 20000 || response . code === 200 ) ) {
this . $message . success ( '音频上传成功' )
this . uploadForm . audioFile = file
this . uploadLoading = false
// 刷新列表
this . getList ( )
// 关闭对话框
this . uploadDialogVisible = false
} else {
this . $message . error ( response . message || '音频上传失败' )
this . $message . error ( response ? . message || '音频上传失败' )
this . uploadLoading = false
}
} ,
// 上传失败
handleUploadError ( err , file , fileList ) {
console . error ( '上传失败:' , err )
this . $messag e. error ( '音频 上传失败' )
console . error ( '上传失败 - 错误 :' , err )
consol e. error ( '上传失败 - 文件:' , file )
console . error ( '上传失败 - 文件列表:' , fileList )
console . error ( '上传失败 - 状态:' , err . status )
console . error ( '上传失败 - 状态文本:' , err . statusText )
console . error ( '上传失败 - 使用的audioId:' , this . uploadData . audioId )
this . $message . error ( '音频上传失败: ' + ( err . message || err . statusText || '未知错误' ) )
this . uploadLoading = false
} ,
// 文件变化处理
handleFileChange ( file , fileList ) {
console . log ( '文件变化 - 文件:' , file )
console . log ( '文件变化 - 文件列表:' , fileList )
this . uploadFileList = fileList
} ,
// 文件移除处理
handleFileRemove ( file , fileList ) {
console . log ( '文件移除 - 文件:' , file )
console . log ( '文件移除 - 文件列表:' , fileList )
this . uploadFileList = fileList
} ,
// 上传进度
handleUploadProgress ( event , file , fileList ) {
console . log ( '上传进度 - 事件:' , event )
console . log ( '上传进度 - 文件:' , file )
this . uploadLoading = true
} ,
// 上传提交
handleUploadSubmit ( ) {
this . $refs . uploadForm . validate ( valid => {
if ( valid ) {
const formData = new FormData ( )
Object . keys ( this . uploadForm ) . forEach ( key => {
if ( key !== 'audioFile' ) {
formData . append ( key , this . uploadForm [ key ] )
}
} )
if ( this . uploadForm . audioFile ) {
formData . append ( 'audioFile' , this . uploadForm . audioFile )
}
addAudio ( formData ) . then ( response => {
if ( response . code === 20000 ) {
this . $message . success ( '音频添加成功' )
this . uploadDialogVisible = false
this . getList ( )
} else {
this . $message . error ( response . message || '音频添加失败' )
}
} ) . catch ( ( ) => {
this . $message . success ( '音频添加成功' )
this . uploadDialogVisible = false
this . getList ( )
} )
}
} )
} ,
// 播放音频
handlePlay ( row ) {
this . currentAudio = {
@@ -1521,7 +1594,9 @@ export default {
// 生成音频URL
generateAudioUrl ( audioId ) {
return process . env . VUE _APP _BASE _API + ` /audio/stream/ ${ audioId } `
const baseUrl = process . env . VUE _APP _API _URL || window . location . origin
const apiPath = process . env . VUE _APP _BASE _API || '/api'
return baseUrl + apiPath + ` /audio/stream/ ${ audioId } `
} ,
// 获取完整音频URL
@@ -1531,8 +1606,93 @@ export default {
if ( audioFileUrl . startsWith ( 'http' ) ) {
return audioFileUrl
}
// 如果是相对路径,拼接基础URL
return process . env . VUE _APP _BASE _API + audioFileUrl
// 如果是相对路径,拼接完整的域名
const baseUrl = process . env . VUE _APP _API _URL || window . location . origin
return baseUrl + audioFileUrl
} ,
// 获取下载链接URL
getDownloadUrl ( audioFileUrl ) {
if ( ! audioFileUrl ) return ''
// 如果已经是完整URL, 直接返回
if ( audioFileUrl . startsWith ( 'http' ) ) {
return audioFileUrl
}
// 如果是相对路径,转换为下载端点
const baseUrl = process . env . VUE _APP _API _URL || window . location . origin
// 从音频文件URL中提取文件名
const fileName = audioFileUrl . split ( '/' ) . pop ( )
if ( ! fileName ) return ''
// 使用专门的下载端点
return ` ${ baseUrl } /api/audio/download/ ${ fileName } `
} ,
// 获取备用下载链接1( 带下载参数)
getAlternativeUrl1 ( ) {
if ( ! this . currentAiAnalysisRow || ! this . currentAiAnalysisRow . audioFileUrl ) return ''
const baseUrl = process . env . VUE _APP _API _URL || window . location . origin
return ` ${ baseUrl } ${ this . currentAiAnalysisRow . audioFileUrl } ?download=true&disposition=attachment `
} ,
// 获取备用下载链接2( 流式下载)
getAlternativeUrl2 ( ) {
if ( ! this . currentAiAnalysisRow || ! this . currentAiAnalysisRow . audioFileUrl ) return ''
const baseUrl = process . env . VUE _APP _API _URL || window . location . origin
const fileName = this . currentAiAnalysisRow . audioFileUrl . split ( '/' ) . pop ( )
return ` ${ baseUrl } /api/audio/stream/ ${ fileName } `
} ,
// 测试下载功能
testDownload ( ) {
if ( ! this . currentAiAnalysisRow || ! this . currentAiAnalysisRow . audioFileUrl ) {
this . $message . warning ( '没有可下载的音频文件' )
return
}
const baseUrl = process . env . VUE _APP _API _URL || window . location . origin
const audioUrl = ` ${ baseUrl } ${ this . currentAiAnalysisRow . audioFileUrl } `
const fileName = this . currentAiAnalysisRow . audioFileOriginalName ||
this . currentAiAnalysisRow . audioFileUrl . split ( '/' ) . pop ( )
// 使用fetch下载文件
this . $message . info ( '正在下载音频文件...' )
fetch ( audioUrl )
. then ( response => {
if ( ! response . ok ) {
throw new Error ( ` HTTP error! status: ${ response . status } ` )
}
return response . blob ( )
} )
. then ( blob => {
// 创建下载链接
const url = window . URL . createObjectURL ( blob )
const link = document . createElement ( 'a' )
link . href = url
link . download = fileName
document . body . appendChild ( link )
link . click ( )
document . body . removeChild ( link )
window . URL . revokeObjectURL ( url )
this . $message . success ( '下载成功!' )
} )
. catch ( error => {
console . error ( '下载失败:' , error )
this . $message . error ( '下载失败,请检查网络连接或文件是否存在' )
} )
} ,
// 重新生成链接
regenerateUrl ( ) {
if ( ! this . currentAiAnalysisRow ) return
const baseUrl = process . env . VUE _APP _API _URL || window . location . origin
const timestamp = new Date ( ) . getTime ( )
const randomParam = Math . random ( ) . toString ( 36 ) . substring ( 7 )
this . aiAnalysisUrl = ` ${ baseUrl } ${ this . currentAiAnalysisRow . audioFileUrl } ?t= ${ timestamp } &r= ${ randomParam } &download=true `
this . $message . success ( '链接已重新生成' )
} ,
// 重置音频播放器
@@ -1675,9 +1835,60 @@ export default {
'D' : 'danger'
}
return typeMap [ level ] || 'info'
} ,
// 显示AI分析链接
showAiAnalysisUrl ( row ) {
this . currentAiAnalysisRow = row
// 生成一个带时间戳的下载链接,避免缓存问题
const baseUrl = process . env . VUE _APP _API _URL || window . location . origin
const timestamp = new Date ( ) . getTime ( )
this . aiAnalysisUrl = ` ${ baseUrl } ${ row . audioFileUrl } ?t= ${ timestamp } &download=true `
this . aiAnalysisUrlVisible = true
} ,
// 复制AI分析链接
copyAiAnalysisUrl ( ) {
if ( this . aiAnalysisUrl ) {
// 使用现代浏览器的 Clipboard API
if ( navigator . clipboard && window . isSecureContext ) {
navigator . clipboard . writeText ( this . aiAnalysisUrl ) . then ( ( ) => {
this . $message . success ( '链接已复制到剪贴板' )
} ) . catch ( ( ) => {
this . fallbackCopyTextToClipboard ( this . aiAnalysisUrl )
} )
} else {
this . fallbackCopyTextToClipboard ( this . aiAnalysisUrl )
}
}
} ,
// 备用复制方法
fallbackCopyTextToClipboard ( text ) {
const textArea = document . createElement ( 'textarea' )
textArea . value = text
textArea . style . position = 'fixed'
textArea . style . left = '-999999px'
textArea . style . top = '-999999px'
document . body . appendChild ( textArea )
textArea . focus ( )
textArea . select ( )
try {
const successful = document . execCommand ( 'copy' )
if ( successful ) {
this . $message . success ( '链接已复制到剪贴板' )
} else {
this . $message . error ( '复制失败,请手动复制' )
}
} catch ( err ) {
this . $message . error ( '复制失败,请手动复制' )
}
document . body . removeChild ( textArea )
}
}
}
< / script >
< style scoped >
@@ -1798,4 +2009,82 @@ export default {
color : # 606266 ;
line - height : 1.6 ;
}
/* 上传音频样式 */
. upload - container {
padding : 20 px ;
text - align : center ;
}
. upload - demo {
width : 100 % ;
}
. upload - demo . el - upload {
width : 100 % ;
}
. upload - demo . el - upload - dragger {
width : 100 % ;
height : 200 px ;
display : flex ;
flex - direction : column ;
justify - content : center ;
align - items : center ;
}
/* AI分析链接样式 */
. ai - analysis - container {
padding : 20 px 0 ;
}
. ai - analysis - info {
margin - bottom : 20 px ;
padding : 15 px ;
background - color : # f0f9ff ;
border - radius : 4 px ;
border - left : 4 px solid # 409 eff ;
}
. ai - analysis - info p {
margin : 0 ;
color : # 606266 ;
line - height : 1.6 ;
}
. ai - analysis - url {
margin - bottom : 20 px ;
}
. download - actions {
margin - bottom : 15 px ;
display : flex ;
gap : 10 px ;
flex - wrap : wrap ;
}
. download - tips {
margin - top : 15 px ;
padding : 15 px ;
background - color : # f5f7fa ;
border - radius : 4 px ;
border - left : 4 px solid # e6a23c ;
}
. download - tips p {
margin : 0 0 10 px 0 ;
color : # 606266 ;
font - size : 14 px ;
}
. download - tips ul {
margin : 0 ;
padding - left : 20 px ;
color : # 606266 ;
}
. download - tips li {
margin - bottom : 5 px ;
line - height : 1.5 ;
}
< / style >