录音文件上传和列表
This commit is contained in:
@@ -130,6 +130,7 @@
|
||||
<!-- 操作按钮区域 -->
|
||||
<el-card class="action-container" shadow="never">
|
||||
<el-button type="primary" icon="el-icon-plus" @click="handleAdd">添加录音</el-button>
|
||||
<el-button type="success" icon="el-icon-upload" @click="handleUpload">上传音频</el-button>
|
||||
<el-button type="primary" @click="handleAllocateProject">分配项目</el-button>
|
||||
<el-button type="primary" @click="handleAllocateSales">分配销售</el-button>
|
||||
<el-button type="primary" @click="handleMergeRecordings">录音合并</el-button>
|
||||
@@ -188,8 +189,61 @@
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="200" fixed="right">
|
||||
<el-table-column label="音频文件" prop="audioFileUrl" min-width="250">
|
||||
<template slot-scope="scope">
|
||||
<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>
|
||||
有音频
|
||||
</el-tag>
|
||||
<el-link
|
||||
type="primary"
|
||||
:href="getFullAudioUrl(scope.row.audioFileUrl)"
|
||||
target="_blank"
|
||||
:underline="false"
|
||||
>
|
||||
<i class="el-icon-document"></i>
|
||||
{{ 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;"
|
||||
>
|
||||
下载
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<el-tag type="info" size="mini">
|
||||
<i class="el-icon-warning"></i>
|
||||
暂无音频文件
|
||||
</el-tag>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="280" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
v-if="scope.row.audioFileUrl"
|
||||
type="text"
|
||||
icon="el-icon-video-play"
|
||||
@click="handlePlay(scope.row)"
|
||||
>
|
||||
播放
|
||||
</el-button>
|
||||
<el-button
|
||||
v-else
|
||||
type="text"
|
||||
icon="el-icon-video-play"
|
||||
disabled
|
||||
style="color: #c0c4cc;"
|
||||
>
|
||||
播放
|
||||
</el-button>
|
||||
<el-button type="text" @click="handleView(scope.row)">查看</el-button>
|
||||
<el-button type="text" @click="handleEdit(scope.row)">编辑</el-button>
|
||||
<el-button type="text" @click="handleDelete(scope.row)">删除</el-button>
|
||||
@@ -436,6 +490,242 @@
|
||||
<el-button type="primary" @click="handleEditSubmit">确 定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 上传音频对话框 -->
|
||||
<el-dialog
|
||||
title="上传音频"
|
||||
:visible.sync="uploadDialogVisible"
|
||||
width="800px"
|
||||
:close-on-click-modal="false"
|
||||
@close="handleUploadDialogClose"
|
||||
>
|
||||
<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">
|
||||
<el-upload
|
||||
ref="audioUpload"
|
||||
:action="uploadAction"
|
||||
:headers="uploadHeaders"
|
||||
:data="uploadData"
|
||||
:before-upload="beforeAudioUpload"
|
||||
:on-success="handleUploadSuccess"
|
||||
:on-error="handleUploadError"
|
||||
:on-progress="handleUploadProgress"
|
||||
:file-list="uploadFileList"
|
||||
:auto-upload="false"
|
||||
:limit="1"
|
||||
accept=".mp3,.wav,.m4a,.aac,.flac"
|
||||
drag
|
||||
>
|
||||
<i class="el-icon-upload"></i>
|
||||
<div class="el-upload__text">将音频文件拖到此处,或<em>点击上传</em></div>
|
||||
<div class="el-upload__tip" slot="tip">
|
||||
支持 mp3、wav、m4a、aac、flac 格式,文件大小不超过 100MB
|
||||
</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 slot="footer" class="dialog-footer">
|
||||
<el-button @click="uploadDialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="handleUploadSubmit" :loading="uploadLoading">确 定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 音频播放器对话框 -->
|
||||
<el-dialog
|
||||
title="音频播放"
|
||||
:visible.sync="playDialogVisible"
|
||||
width="600px"
|
||||
:close-on-click-modal="false"
|
||||
@close="handlePlayDialogClose"
|
||||
>
|
||||
<div class="audio-player-container">
|
||||
<div class="audio-info">
|
||||
<h3>{{ currentAudio.recordingName }}</h3>
|
||||
<p><strong>客户:</strong>{{ currentAudio.customerName }} ({{ currentAudio.customerPhone }})</p>
|
||||
<p><strong>销售:</strong>{{ currentAudio.salesName }}</p>
|
||||
<p><strong>录音时间:</strong>{{ currentAudio.recordingTime }}</p>
|
||||
<p><strong>时长:</strong>{{ currentAudio.duration }} 分钟</p>
|
||||
</div>
|
||||
|
||||
<div class="audio-player">
|
||||
<div v-if="audioLoading" class="audio-loading">
|
||||
<i class="el-icon-loading"></i>
|
||||
<span>音频加载中...</span>
|
||||
</div>
|
||||
<audio
|
||||
ref="audioPlayer"
|
||||
:src="currentAudio.audioUrl"
|
||||
controls
|
||||
preload="metadata"
|
||||
@loadstart="handleAudioLoadStart"
|
||||
@loadedmetadata="handleAudioLoaded"
|
||||
@timeupdate="handleTimeUpdate"
|
||||
@ended="handleAudioEnded"
|
||||
@error="handleAudioError"
|
||||
@canplay="handleAudioCanPlay"
|
||||
style="width: 100%;"
|
||||
>
|
||||
您的浏览器不支持音频播放
|
||||
</audio>
|
||||
</div>
|
||||
|
||||
<div class="audio-controls">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-video-play"
|
||||
@click="playAudio"
|
||||
:disabled="!currentAudio.audioUrl"
|
||||
>
|
||||
播放
|
||||
</el-button>
|
||||
<el-button
|
||||
type="warning"
|
||||
icon="el-icon-video-pause"
|
||||
@click="pauseAudio"
|
||||
:disabled="!currentAudio.audioUrl"
|
||||
>
|
||||
暂停
|
||||
</el-button>
|
||||
<el-button
|
||||
icon="el-icon-refresh"
|
||||
@click="restartAudio"
|
||||
:disabled="!currentAudio.audioUrl"
|
||||
>
|
||||
重新播放
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="playback-speed">
|
||||
<span>播放速度:</span>
|
||||
<el-select v-model="playbackSpeed" size="small" style="width: 100px;">
|
||||
<el-option label="0.5x" value="0.5" />
|
||||
<el-option label="0.75x" value="0.75" />
|
||||
<el-option label="1.0x" value="1.0" />
|
||||
<el-option label="1.25x" value="1.25" />
|
||||
<el-option label="1.5x" value="1.5" />
|
||||
<el-option label="2.0x" value="2.0" />
|
||||
</el-select>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<div class="audio-progress" v-if="audioDuration > 0">
|
||||
<div class="progress-info">
|
||||
<span>{{ formatTime(currentTime) }} / {{ formatTime(audioDuration) }}</span>
|
||||
<span>{{ Math.round((currentTime / audioDuration) * 100) }}%</span>
|
||||
</div>
|
||||
<el-progress
|
||||
:percentage="(currentTime / audioDuration) * 100"
|
||||
:show-text="false"
|
||||
:stroke-width="8"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="audio-notes" v-if="currentAudio.description">
|
||||
<h4>录音备注:</h4>
|
||||
<p>{{ currentAudio.description }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="playDialogVisible = false">关 闭</el-button>
|
||||
<el-button type="primary" @click="downloadAudio" :disabled="!currentAudio.audioUrl">
|
||||
下载音频
|
||||
</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -469,6 +759,45 @@ export default {
|
||||
viewDialogVisible: false,
|
||||
editDialogVisible: false,
|
||||
isEdit: false,
|
||||
uploadDialogVisible: false,
|
||||
uploadLoading: false,
|
||||
uploadFileList: [],
|
||||
// 音频播放相关
|
||||
playDialogVisible: false,
|
||||
currentAudio: {
|
||||
recordingName: '',
|
||||
customerName: '',
|
||||
customerPhone: '',
|
||||
salesName: '',
|
||||
recordingTime: '',
|
||||
duration: 0,
|
||||
audioUrl: '',
|
||||
description: ''
|
||||
},
|
||||
currentTime: 0,
|
||||
audioDuration: 0,
|
||||
playbackSpeed: '1.0',
|
||||
audioLoading: false,
|
||||
uploadAction: process.env.VUE_APP_BASE_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' }
|
||||
]
|
||||
},
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
current: 1,
|
||||
@@ -551,6 +880,19 @@ export default {
|
||||
duration: [
|
||||
{ required: true, message: '请输入录音时长', trigger: 'blur' }
|
||||
]
|
||||
},
|
||||
// 上传音频表单
|
||||
uploadForm: {
|
||||
recordingName: '',
|
||||
dealershipId: '',
|
||||
projectId: '',
|
||||
salesId: '',
|
||||
customerName: '',
|
||||
customerPhone: '',
|
||||
intentionLevel: '',
|
||||
recordingTime: '',
|
||||
audioFile: null,
|
||||
description: ''
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -560,6 +902,13 @@ export default {
|
||||
this.getProjectOptions()
|
||||
this.getSalesOptions()
|
||||
},
|
||||
watch: {
|
||||
playbackSpeed(newSpeed) {
|
||||
if (this.$refs.audioPlayer) {
|
||||
this.$refs.audioPlayer.playbackRate = parseFloat(newSpeed)
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 获取列表数据
|
||||
getList() {
|
||||
@@ -608,7 +957,12 @@ export default {
|
||||
speechModel: 'GPT-4',
|
||||
uploadStatus: '已上传',
|
||||
syncStatus: '已同步',
|
||||
isMerged: false
|
||||
isMerged: false,
|
||||
audioFileUrl: '/api/audio/1953606689980715010_db3c711d-e581-49bb-814f-4709c0431021.mp3',
|
||||
audioFileOriginalName: 'kk 2024-08-29 10-活动.mp3',
|
||||
audioFileExtension: 'mp3',
|
||||
audioFileSize: 79488,
|
||||
remarks: '客户对产品价格比较关注,需要进一步跟进'
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
@@ -625,7 +979,12 @@ export default {
|
||||
speechModel: 'GPT-3.5',
|
||||
uploadStatus: '已上传',
|
||||
syncStatus: '已同步',
|
||||
isMerged: true
|
||||
isMerged: true,
|
||||
audioFileUrl: '/api/audio/1953606689980715011_db3c711d-e581-49bb-814f-4709c0431022.mp3',
|
||||
audioFileOriginalName: '客户咨询录音_002.mp3',
|
||||
audioFileExtension: 'mp3',
|
||||
audioFileSize: 125000,
|
||||
remarks: '客户对产品功能很感兴趣,有购买意向'
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
@@ -642,7 +1001,12 @@ export default {
|
||||
speechModel: 'GPT-4',
|
||||
uploadStatus: '上传中',
|
||||
syncStatus: '未同步',
|
||||
isMerged: false
|
||||
isMerged: false,
|
||||
audioFileUrl: '',
|
||||
audioFileOriginalName: '',
|
||||
audioFileExtension: '',
|
||||
audioFileSize: 0,
|
||||
remarks: '客户需要更多产品详细信息'
|
||||
}
|
||||
]
|
||||
this.total = this.audioList.length
|
||||
@@ -1031,18 +1395,289 @@ export default {
|
||||
})
|
||||
},
|
||||
|
||||
// 获取意向级别标签类型
|
||||
getIntentionLevelType(level) {
|
||||
const typeMap = {
|
||||
'A': 'success',
|
||||
'B': 'warning',
|
||||
'C': 'info',
|
||||
'D': 'danger'
|
||||
// 上传音频
|
||||
handleUpload() {
|
||||
this.uploadForm = {
|
||||
recordingName: '',
|
||||
dealershipId: '',
|
||||
projectId: '',
|
||||
salesId: '',
|
||||
customerName: '',
|
||||
customerPhone: '',
|
||||
intentionLevel: '',
|
||||
recordingTime: '',
|
||||
audioFile: null,
|
||||
description: ''
|
||||
}
|
||||
this.uploadFileList = []
|
||||
this.uploadDialogVisible = true
|
||||
},
|
||||
|
||||
// 上传音频对话框关闭
|
||||
handleUploadDialogClose() {
|
||||
this.$refs.uploadForm?.resetFields()
|
||||
this.uploadFileList = []
|
||||
},
|
||||
|
||||
// 上传前检查文件大小和类型
|
||||
beforeAudioUpload(file) {
|
||||
const isLt100M = file.size / 1024 / 1024 < 100
|
||||
if (!isLt100M) {
|
||||
this.$message.error('上传文件大小不能超过 100MB!')
|
||||
}
|
||||
return typeMap[level] || 'info'
|
||||
}
|
||||
}
|
||||
}
|
||||
return isLt100M
|
||||
},
|
||||
|
||||
// 上传成功
|
||||
handleUploadSuccess(response, file, fileList) {
|
||||
if (response.code === 20000) {
|
||||
this.$message.success('音频上传成功')
|
||||
this.uploadForm.audioFile = file
|
||||
this.uploadLoading = false
|
||||
} else {
|
||||
this.$message.error(response.message || '音频上传失败')
|
||||
this.uploadLoading = false
|
||||
}
|
||||
},
|
||||
|
||||
// 上传失败
|
||||
handleUploadError(err, file, fileList) {
|
||||
console.error('上传失败:', err)
|
||||
this.$message.error('音频上传失败')
|
||||
this.uploadLoading = false
|
||||
},
|
||||
|
||||
// 上传进度
|
||||
handleUploadProgress(event, file, fileList) {
|
||||
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 = {
|
||||
recordingName: row.recordingName,
|
||||
customerName: row.customerName,
|
||||
customerPhone: row.customerPhone,
|
||||
salesName: row.salesName,
|
||||
recordingTime: row.recordingTime,
|
||||
duration: row.duration,
|
||||
audioUrl: row.audioFileUrl ? this.getFullAudioUrl(row.audioFileUrl) : this.generateAudioUrl(row.id),
|
||||
description: row.remarks || row.description
|
||||
}
|
||||
this.playDialogVisible = true
|
||||
this.$nextTick(() => {
|
||||
this.resetAudioPlayer()
|
||||
// 验证音频URL是否可访问
|
||||
this.validateAudioUrl()
|
||||
})
|
||||
},
|
||||
|
||||
// 验证音频URL
|
||||
validateAudioUrl() {
|
||||
if (!this.currentAudio.audioUrl) {
|
||||
this.$message.warning('该录音暂无音频文件')
|
||||
return
|
||||
}
|
||||
|
||||
// 检查URL格式
|
||||
try {
|
||||
new URL(this.currentAudio.audioUrl)
|
||||
} catch (e) {
|
||||
this.$message.error('音频文件URL格式错误')
|
||||
return
|
||||
}
|
||||
},
|
||||
|
||||
// 生成音频URL
|
||||
generateAudioUrl(audioId) {
|
||||
return process.env.VUE_APP_BASE_API + `/audio/stream/${audioId}`
|
||||
},
|
||||
|
||||
// 获取完整音频URL
|
||||
getFullAudioUrl(audioFileUrl) {
|
||||
if (!audioFileUrl) return ''
|
||||
// 如果已经是完整URL,直接返回
|
||||
if (audioFileUrl.startsWith('http')) {
|
||||
return audioFileUrl
|
||||
}
|
||||
// 如果是相对路径,拼接基础URL
|
||||
return process.env.VUE_APP_BASE_API + audioFileUrl
|
||||
},
|
||||
|
||||
// 重置音频播放器
|
||||
resetAudioPlayer() {
|
||||
this.currentTime = 0
|
||||
this.audioDuration = 0
|
||||
this.playbackSpeed = '1.0'
|
||||
this.audioLoading = false
|
||||
if (this.$refs.audioPlayer) {
|
||||
this.$refs.audioPlayer.currentTime = 0
|
||||
this.$refs.audioPlayer.playbackRate = 1.0
|
||||
}
|
||||
},
|
||||
|
||||
// 音频开始加载
|
||||
handleAudioLoadStart() {
|
||||
this.audioLoading = true
|
||||
},
|
||||
|
||||
// 音频可以播放
|
||||
handleAudioCanPlay() {
|
||||
this.audioLoading = false
|
||||
},
|
||||
|
||||
// 播放音频
|
||||
playAudio() {
|
||||
if (this.$refs.audioPlayer) {
|
||||
this.$refs.audioPlayer.play()
|
||||
}
|
||||
},
|
||||
|
||||
// 暂停音频
|
||||
pauseAudio() {
|
||||
if (this.$refs.audioPlayer) {
|
||||
this.$refs.audioPlayer.pause()
|
||||
}
|
||||
},
|
||||
|
||||
// 重新播放
|
||||
restartAudio() {
|
||||
if (this.$refs.audioPlayer) {
|
||||
this.$refs.audioPlayer.currentTime = 0
|
||||
this.$refs.audioPlayer.play()
|
||||
}
|
||||
},
|
||||
|
||||
// 音频加载完成
|
||||
handleAudioLoaded() {
|
||||
if (this.$refs.audioPlayer) {
|
||||
this.audioDuration = this.$refs.audioPlayer.duration
|
||||
}
|
||||
},
|
||||
|
||||
// 音频播放进度更新
|
||||
handleTimeUpdate() {
|
||||
if (this.$refs.audioPlayer) {
|
||||
this.currentTime = this.$refs.audioPlayer.currentTime
|
||||
}
|
||||
},
|
||||
|
||||
// 音频播放结束
|
||||
handleAudioEnded() {
|
||||
this.currentTime = 0
|
||||
this.$message.info('音频播放完成')
|
||||
},
|
||||
|
||||
// 音频播放错误
|
||||
handleAudioError() {
|
||||
console.error('音频加载失败:', this.currentAudio.audioUrl)
|
||||
this.$message.error('音频加载失败,请检查文件是否存在或网络连接')
|
||||
// 如果是模拟数据,提供提示
|
||||
if (this.currentAudio.audioUrl && (this.currentAudio.audioUrl.includes('soundjay.com') || this.currentAudio.audioUrl.includes('learningcontainer.com'))) {
|
||||
this.$message.warning('当前为演示数据,实际环境中请上传真实的音频文件')
|
||||
}
|
||||
},
|
||||
|
||||
// 播放对话框关闭
|
||||
handlePlayDialogClose() {
|
||||
if (this.$refs.audioPlayer) {
|
||||
this.$refs.audioPlayer.pause()
|
||||
}
|
||||
this.resetAudioPlayer()
|
||||
},
|
||||
|
||||
// 下载音频
|
||||
downloadAudio() {
|
||||
if (this.currentAudio.audioUrl) {
|
||||
const link = document.createElement('a')
|
||||
link.href = this.currentAudio.audioUrl
|
||||
link.download = this.currentAudio.recordingName + '.mp3'
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
}
|
||||
},
|
||||
|
||||
// 格式化时间
|
||||
formatTime(seconds) {
|
||||
if (!seconds || isNaN(seconds)) return '00:00'
|
||||
const mins = Math.floor(seconds / 60)
|
||||
const secs = Math.floor(seconds % 60)
|
||||
return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`
|
||||
},
|
||||
|
||||
// 获取音频文件名
|
||||
getAudioFileName(url) {
|
||||
if (!url) return '未知文件'
|
||||
try {
|
||||
const urlObj = new URL(url)
|
||||
const pathname = urlObj.pathname
|
||||
const fileName = pathname.split('/').pop()
|
||||
return fileName || '音频文件'
|
||||
} catch (e) {
|
||||
return '音频文件'
|
||||
}
|
||||
},
|
||||
|
||||
// 下载音频文件
|
||||
downloadAudioFile(row) {
|
||||
if (!row.audioFileUrl) {
|
||||
this.$message.warning('该录音暂无音频文件')
|
||||
return
|
||||
}
|
||||
|
||||
const link = document.createElement('a')
|
||||
link.href = this.getFullAudioUrl(row.audioFileUrl)
|
||||
link.download = row.audioFileOriginalName || this.getAudioFileName(row.audioFileUrl)
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
this.$message.success('开始下载音频文件')
|
||||
},
|
||||
|
||||
// 获取意向级别标签类型
|
||||
getIntentionLevelType(level) {
|
||||
const typeMap = {
|
||||
'A': 'success',
|
||||
'B': 'warning',
|
||||
'C': 'info',
|
||||
'D': 'danger'
|
||||
}
|
||||
return typeMap[level] || 'info'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -1072,4 +1707,95 @@ export default {
|
||||
margin: 5px 0;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
/* 音频播放器样式 */
|
||||
.audio-player-container {
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.audio-info {
|
||||
margin-bottom: 20px;
|
||||
padding: 15px;
|
||||
background-color: #f5f7fa;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.audio-info h3 {
|
||||
margin: 0 0 10px 0;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.audio-info p {
|
||||
margin: 5px 0;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.audio-player {
|
||||
margin-bottom: 20px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.audio-loading {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.audio-loading i {
|
||||
font-size: 24px;
|
||||
color: #409eff;
|
||||
margin-bottom: 10px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.audio-loading span {
|
||||
color: #606266;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.audio-controls {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.playback-speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.audio-progress {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.progress-info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.audio-notes {
|
||||
padding: 15px;
|
||||
background-color: #f0f9ff;
|
||||
border-radius: 4px;
|
||||
border-left: 4px solid #409eff;
|
||||
}
|
||||
|
||||
.audio-notes h4 {
|
||||
margin: 0 0 10px 0;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.audio-notes p {
|
||||
margin: 0;
|
||||
color: #606266;
|
||||
line-height: 1.6;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user