Compare commits
2 Commits
6209b691ab
...
c93cf465e8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c93cf465e8 | ||
|
|
1a4a921677 |
@@ -42,3 +42,12 @@ export function getVideoSynthesisStatus(taskId) {
|
|||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 播放视频
|
||||||
|
export function playVideo(minioPath, expires = 900) {
|
||||||
|
return request({
|
||||||
|
url: `/video-synthesis/play/${minioPath}`,
|
||||||
|
method: 'get',
|
||||||
|
params: { expires }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -213,7 +213,7 @@ export const constantRoutes = [
|
|||||||
path: 'audio-generation',
|
path: 'audio-generation',
|
||||||
name: 'AudioGeneration',
|
name: 'AudioGeneration',
|
||||||
component: () => import('@/views/audio-statistics/audio-generation'),
|
component: () => import('@/views/audio-statistics/audio-generation'),
|
||||||
meta: { title: '音频生成', icon: 'el-icon-microphone' }
|
meta: { title: '音频列表', icon: 'el-icon-microphone' }
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -66,6 +66,10 @@
|
|||||||
<div slot="header">
|
<div slot="header">
|
||||||
<span>TTS请求日志列表</span>
|
<span>TTS请求日志列表</span>
|
||||||
<div style="float: right;">
|
<div style="float: right;">
|
||||||
|
<el-button type="info" size="small" @click="handleOpenUploadDialog">
|
||||||
|
<i class="el-icon-upload" />
|
||||||
|
上传音频
|
||||||
|
</el-button>
|
||||||
<el-button type="success" size="small" @click="handleOpenGenerateDialog">
|
<el-button type="success" size="small" @click="handleOpenGenerateDialog">
|
||||||
<i class="el-icon-plus" />
|
<i class="el-icon-plus" />
|
||||||
音频生成
|
音频生成
|
||||||
@@ -207,6 +211,68 @@
|
|||||||
</div>
|
</div>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 上传音频对话框 -->
|
||||||
|
<el-dialog
|
||||||
|
title="上传音频"
|
||||||
|
:visible.sync="uploadDialogVisible"
|
||||||
|
width="600px"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
@close="handleUploadDialogClose"
|
||||||
|
>
|
||||||
|
<el-form
|
||||||
|
ref="uploadForm"
|
||||||
|
:model="uploadForm"
|
||||||
|
:rules="uploadFormRules"
|
||||||
|
label-width="100px"
|
||||||
|
>
|
||||||
|
<el-form-item label="选择音频" prop="audioFile">
|
||||||
|
<el-upload
|
||||||
|
ref="audioUpload"
|
||||||
|
:file-list="uploadFileList"
|
||||||
|
:on-change="handleFileChange"
|
||||||
|
:on-remove="handleFileRemove"
|
||||||
|
:auto-upload="false"
|
||||||
|
:limit="1"
|
||||||
|
accept="audio/*"
|
||||||
|
drag
|
||||||
|
>
|
||||||
|
<i class="el-icon-upload" />
|
||||||
|
<div class="el-upload__text">将音频文件拖到此处,或<em>点击上传</em></div>
|
||||||
|
<div slot="tip" class="el-upload__tip">只能上传音频文件,且不超过50MB</div>
|
||||||
|
</el-upload>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="音频名称" prop="audioName">
|
||||||
|
<el-input
|
||||||
|
v-model="uploadForm.audioName"
|
||||||
|
placeholder="请输入音频名称"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="创建人姓名" prop="creatorName">
|
||||||
|
<el-input
|
||||||
|
v-model="uploadForm.creatorName"
|
||||||
|
placeholder="请输入创建人姓名"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="创建人电话" prop="creatorPhone">
|
||||||
|
<el-input
|
||||||
|
v-model="uploadForm.creatorPhone"
|
||||||
|
placeholder="请输入创建人电话"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="uploadDialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" :loading="uploadLoading" @click="handleUploadSubmit">确认提交</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
<!-- 音频生成对话框 -->
|
<!-- 音频生成对话框 -->
|
||||||
<el-dialog
|
<el-dialog
|
||||||
title="音频生成"
|
title="音频生成"
|
||||||
@@ -408,6 +474,27 @@ export default {
|
|||||||
sortOrder: '',
|
sortOrder: '',
|
||||||
detailDialogVisible: false,
|
detailDialogVisible: false,
|
||||||
currentRow: {},
|
currentRow: {},
|
||||||
|
// 上传音频对话框相关
|
||||||
|
uploadDialogVisible: false,
|
||||||
|
uploadLoading: false,
|
||||||
|
uploadFileList: [],
|
||||||
|
uploadForm: {
|
||||||
|
audioName: '',
|
||||||
|
creatorName: '',
|
||||||
|
creatorPhone: ''
|
||||||
|
},
|
||||||
|
uploadFormRules: {
|
||||||
|
audioName: [
|
||||||
|
{ required: true, message: '请输入音频名称', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
creatorName: [
|
||||||
|
{ required: true, message: '请输入创建人姓名', trigger: 'blur' }
|
||||||
|
],
|
||||||
|
creatorPhone: [
|
||||||
|
{ required: true, message: '请输入创建人电话', trigger: 'blur' },
|
||||||
|
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur' }
|
||||||
|
]
|
||||||
|
},
|
||||||
// 音频生成对话框相关
|
// 音频生成对话框相关
|
||||||
generateDialogVisible: false,
|
generateDialogVisible: false,
|
||||||
generateLoading: false,
|
generateLoading: false,
|
||||||
@@ -607,6 +694,173 @@ export default {
|
|||||||
return statusMap[status] || status
|
return statusMap[status] || status
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 打开上传音频对话框
|
||||||
|
handleOpenUploadDialog() {
|
||||||
|
this.uploadDialogVisible = true
|
||||||
|
this.uploadForm = {
|
||||||
|
audioName: '',
|
||||||
|
creatorName: this.name || '',
|
||||||
|
creatorPhone: this.phone || ''
|
||||||
|
}
|
||||||
|
this.uploadFileList = []
|
||||||
|
},
|
||||||
|
|
||||||
|
// 上传音频对话框关闭
|
||||||
|
handleUploadDialogClose() {
|
||||||
|
this.uploadDialogVisible = false
|
||||||
|
this.uploadFileList = []
|
||||||
|
this.$refs.uploadForm.resetFields()
|
||||||
|
},
|
||||||
|
|
||||||
|
// 文件变化处理
|
||||||
|
handleFileChange(file, fileList) {
|
||||||
|
console.log('文件变化:', file.name)
|
||||||
|
this.uploadFileList = fileList
|
||||||
|
|
||||||
|
// 文件验证
|
||||||
|
if (file.size > 50 * 1024 * 1024) {
|
||||||
|
this.$message.error('文件大小不能超过50MB')
|
||||||
|
this.uploadFileList = []
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const allowedTypes = ['audio/mpeg', 'audio/mp3', 'audio/wav', 'audio/ogg', 'audio/m4a', 'audio/aac']
|
||||||
|
if (!allowedTypes.includes(file.raw?.type || file.type)) {
|
||||||
|
this.$message.error('只能上传音频文件')
|
||||||
|
this.uploadFileList = []
|
||||||
|
return
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 文件移除处理
|
||||||
|
handleFileRemove(file, fileList) {
|
||||||
|
console.log('文件移除:', file.name)
|
||||||
|
this.uploadFileList = fileList
|
||||||
|
},
|
||||||
|
|
||||||
|
// 上传音频提交
|
||||||
|
handleUploadSubmit() {
|
||||||
|
if (this.uploadLoading) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$refs.uploadForm.validate(valid => {
|
||||||
|
if (valid) {
|
||||||
|
if (this.uploadFileList.length === 0) {
|
||||||
|
this.$message.error('请先选择要上传的音频文件')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.uploadLoading = true
|
||||||
|
this.uploadAudioFile()
|
||||||
|
} else {
|
||||||
|
this.$message.error('请完善表单信息')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 上传音频文件
|
||||||
|
async uploadAudioFile() {
|
||||||
|
try {
|
||||||
|
const file = this.uploadFileList[0].raw || this.uploadFileList[0]
|
||||||
|
console.log('准备上传音频文件:', file)
|
||||||
|
|
||||||
|
// 创建FormData
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('file', file)
|
||||||
|
formData.append('audioName', this.uploadForm.audioName)
|
||||||
|
formData.append('creatorName', this.uploadForm.creatorName)
|
||||||
|
formData.append('creatorPhone', this.uploadForm.creatorPhone)
|
||||||
|
|
||||||
|
console.log('FormData内容:', {
|
||||||
|
file: file.name,
|
||||||
|
audioName: this.uploadForm.audioName,
|
||||||
|
creatorName: this.uploadForm.creatorName,
|
||||||
|
creatorPhone: this.uploadForm.creatorPhone
|
||||||
|
})
|
||||||
|
|
||||||
|
// 手动上传文件
|
||||||
|
const uploadResponse = await this.uploadFile(formData)
|
||||||
|
console.log('上传响应:', uploadResponse)
|
||||||
|
|
||||||
|
if (uploadResponse && uploadResponse.success) {
|
||||||
|
this.$message.success(uploadResponse.message || '音频上传成功')
|
||||||
|
this.uploadDialogVisible = false
|
||||||
|
this.fetchData()
|
||||||
|
|
||||||
|
// 打印上传成功的详细信息
|
||||||
|
console.log('上传成功详情:', {
|
||||||
|
id: uploadResponse.id,
|
||||||
|
audioName: uploadResponse.audioName,
|
||||||
|
minioUrl: uploadResponse.minioUrl,
|
||||||
|
shortUrl: uploadResponse.shortUrl,
|
||||||
|
shortUrlExpireTime: uploadResponse.shortUrlExpireTime,
|
||||||
|
creatorName: uploadResponse.creatorName,
|
||||||
|
creatorPhone: uploadResponse.creatorPhone,
|
||||||
|
audioSizeBytes: uploadResponse.audioSizeBytes,
|
||||||
|
status: uploadResponse.status,
|
||||||
|
createTime: uploadResponse.createTime
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.$message.error(uploadResponse?.message || '音频上传失败')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('上传过程出错:', error)
|
||||||
|
this.$message.error('上传失败: ' + (error.message || '未知错误'))
|
||||||
|
} finally {
|
||||||
|
this.uploadLoading = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 手动上传文件的方法
|
||||||
|
async uploadFile(formData) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const xhr = new XMLHttpRequest()
|
||||||
|
|
||||||
|
xhr.onload = () => {
|
||||||
|
console.log('上传完成,状态码:', xhr.status)
|
||||||
|
console.log('上传响应:', xhr.responseText)
|
||||||
|
|
||||||
|
if (xhr.status === 200) {
|
||||||
|
try {
|
||||||
|
const response = JSON.parse(xhr.responseText)
|
||||||
|
resolve(response)
|
||||||
|
} catch (e) {
|
||||||
|
console.error('解析响应失败:', e)
|
||||||
|
reject(new Error('响应格式错误'))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reject(new Error(`上传失败,状态码: ${xhr.status}`))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
xhr.onerror = () => {
|
||||||
|
console.error('上传请求失败')
|
||||||
|
reject(new Error('网络请求失败'))
|
||||||
|
}
|
||||||
|
|
||||||
|
xhr.ontimeout = () => {
|
||||||
|
console.error('上传超时')
|
||||||
|
reject(new Error('上传超时'))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置超时时间
|
||||||
|
xhr.timeout = 60000
|
||||||
|
|
||||||
|
// 先打开连接
|
||||||
|
xhr.open('POST', '/api/tts/upload-audio')
|
||||||
|
|
||||||
|
// 然后设置请求头
|
||||||
|
const token = this.$store.getters.token
|
||||||
|
if (token) {
|
||||||
|
xhr.setRequestHeader('Authorization', 'Bearer ' + token)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 最后发送请求
|
||||||
|
xhr.send(formData)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
// 格式化文件大小
|
// 格式化文件大小
|
||||||
formatFileSize(bytes) {
|
formatFileSize(bytes) {
|
||||||
if (!bytes) return '-'
|
if (!bytes) return '-'
|
||||||
|
|||||||
@@ -114,8 +114,12 @@
|
|||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="模型供应商" prop="modelProvider">
|
<el-form-item label="模型供应商" prop="modelProvider">
|
||||||
<el-select v-model="form.modelProvider" placeholder="请选择模型供应商" style="width: 100%">
|
<el-select v-model="form.modelProvider" placeholder="请选择模型供应商" style="width: 100%">
|
||||||
<el-option label="dashscope" value="dashscope" />
|
<el-option
|
||||||
<el-option label="其他" value="other" />
|
v-for="option in modelProviderOptions"
|
||||||
|
:key="option.value"
|
||||||
|
:label="option.label"
|
||||||
|
:value="option.value"
|
||||||
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
@@ -243,7 +247,28 @@ export default {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
imageOptions: [],
|
imageOptions: [],
|
||||||
audioOptions: []
|
audioOptions: [],
|
||||||
|
selectedImageModel: '' // 存储选中头像的模型名
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
// 根据选中头像的模型名动态计算模型供应商选项
|
||||||
|
modelProviderOptions() {
|
||||||
|
if (this.selectedImageModel === 'emo-detect-v1') {
|
||||||
|
return [
|
||||||
|
{ label: 'emo-v1', value: 'emo-v1' }
|
||||||
|
]
|
||||||
|
} else if (this.selectedImageModel === 'liveportrait-detect') {
|
||||||
|
return [
|
||||||
|
{ label: 'liveportrait', value: 'liveportrait' }
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
// 默认选项
|
||||||
|
return [
|
||||||
|
{ label: 'dashscope', value: 'dashscope' },
|
||||||
|
{ label: '其他', value: 'other' }
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
@@ -272,7 +297,8 @@ export default {
|
|||||||
url: item.imageUrl,
|
url: item.imageUrl,
|
||||||
avatarName: item.avatarName,
|
avatarName: item.avatarName,
|
||||||
ownerName: item.ownerName,
|
ownerName: item.ownerName,
|
||||||
ownerPhone: item.ownerPhone
|
ownerPhone: item.ownerPhone,
|
||||||
|
modelName: item.modelName || item.model || '' // 获取模型名信息
|
||||||
}))
|
}))
|
||||||
console.log('Image options set:', this.imageOptions)
|
console.log('Image options set:', this.imageOptions)
|
||||||
} else if (response && response.success === false) {
|
} else if (response && response.success === false) {
|
||||||
@@ -280,18 +306,18 @@ export default {
|
|||||||
this.$message.error(`获取头像列表失败: ${response.message || '未知错误'}`)
|
this.$message.error(`获取头像列表失败: ${response.message || '未知错误'}`)
|
||||||
// 使用模拟数据
|
// 使用模拟数据
|
||||||
this.imageOptions = [
|
this.imageOptions = [
|
||||||
{ id: 'img-001', name: '头像1', url: 'http://example.com/images/avatar1.jpg', ownerName: '张三', ownerPhone: '13800138000' },
|
{ id: 'img-001', name: '头像1', url: 'http://example.com/images/avatar1.jpg', ownerName: '张三', ownerPhone: '13800138000', modelName: 'emo-detect-v1' },
|
||||||
{ id: 'img-002', name: '头像2', url: 'http://example.com/images/avatar2.jpg', ownerName: '李四', ownerPhone: '13900139000' },
|
{ id: 'img-002', name: '头像2', url: 'http://example.com/images/avatar2.jpg', ownerName: '李四', ownerPhone: '13900139000', modelName: 'liveportrait-detect' },
|
||||||
{ id: 'img-003', name: '头像3', url: 'http://example.com/images/avatar3.jpg', ownerName: '王五', ownerPhone: '13700137000' }
|
{ id: 'img-003', name: '头像3', url: 'http://example.com/images/avatar3.jpg', ownerName: '王五', ownerPhone: '13700137000', modelName: 'other-model' }
|
||||||
]
|
]
|
||||||
} else {
|
} else {
|
||||||
console.error('Unexpected face detect response format:', response)
|
console.error('Unexpected face detect response format:', response)
|
||||||
this.$message.error('获取头像列表失败: 响应数据格式不正确')
|
this.$message.error('获取头像列表失败: 响应数据格式不正确')
|
||||||
// 使用模拟数据
|
// 使用模拟数据
|
||||||
this.imageOptions = [
|
this.imageOptions = [
|
||||||
{ id: 'img-001', name: '头像1', url: 'http://example.com/images/avatar1.jpg', ownerName: '张三', ownerPhone: '13800138000' },
|
{ id: 'img-001', name: '头像1', url: 'http://example.com/images/avatar1.jpg', ownerName: '张三', ownerPhone: '13800138000', modelName: 'emo-detect-v1' },
|
||||||
{ id: 'img-002', name: '头像2', url: 'http://example.com/images/avatar2.jpg', ownerName: '李四', ownerPhone: '13900139000' },
|
{ id: 'img-002', name: '头像2', url: 'http://example.com/images/avatar2.jpg', ownerName: '李四', ownerPhone: '13900139000', modelName: 'liveportrait-detect' },
|
||||||
{ id: 'img-003', name: '头像3', url: 'http://example.com/images/avatar3.jpg', ownerName: '王五', ownerPhone: '13700137000' }
|
{ id: 'img-003', name: '头像3', url: 'http://example.com/images/avatar3.jpg', ownerName: '王五', ownerPhone: '13700137000', modelName: 'other-model' }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
@@ -300,9 +326,9 @@ export default {
|
|||||||
this.$message.error(`网络请求失败: ${error.message || '请检查网络连接'}`)
|
this.$message.error(`网络请求失败: ${error.message || '请检查网络连接'}`)
|
||||||
// 开发环境使用模拟数据
|
// 开发环境使用模拟数据
|
||||||
this.imageOptions = [
|
this.imageOptions = [
|
||||||
{ id: 'img-001', name: '头像1', url: 'http://example.com/images/avatar1.jpg', ownerName: '张三', ownerPhone: '13800138000' },
|
{ id: 'img-001', name: '头像1', url: 'http://example.com/images/avatar1.jpg', ownerName: '张三', ownerPhone: '13800138000', modelName: 'emo-detect-v1' },
|
||||||
{ id: 'img-002', name: '头像2', url: 'http://example.com/images/avatar2.jpg', ownerName: '李四', ownerPhone: '13900139000' },
|
{ id: 'img-002', name: '头像2', url: 'http://example.com/images/avatar2.jpg', ownerName: '李四', ownerPhone: '13900139000', modelName: 'liveportrait-detect' },
|
||||||
{ id: 'img-003', name: '头像3', url: 'http://example.com/images/avatar3.jpg', ownerName: '王五', ownerPhone: '13700137000' }
|
{ id: 'img-003', name: '头像3', url: 'http://example.com/images/avatar3.jpg', ownerName: '王五', ownerPhone: '13700137000', modelName: 'other-model' }
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@@ -374,6 +400,19 @@ export default {
|
|||||||
if (selectedImage.ownerPhone) {
|
if (selectedImage.ownerPhone) {
|
||||||
this.form.ownerPhone = selectedImage.ownerPhone
|
this.form.ownerPhone = selectedImage.ownerPhone
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取头像的模型名信息
|
||||||
|
this.selectedImageModel = selectedImage.modelName || ''
|
||||||
|
|
||||||
|
// 根据模型名自动设置模型供应商
|
||||||
|
if (this.selectedImageModel === 'emo-detect-v1') {
|
||||||
|
this.form.modelProvider = 'emo-v1'
|
||||||
|
} else if (this.selectedImageModel === 'liveportrait-detect') {
|
||||||
|
this.form.modelProvider = 'liveportrait'
|
||||||
|
} else {
|
||||||
|
// 默认设置
|
||||||
|
this.form.modelProvider = 'dashscope'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -442,6 +481,8 @@ export default {
|
|||||||
pasteBack: true,
|
pasteBack: true,
|
||||||
headMoveStrength: 0.7
|
headMoveStrength: 0.7
|
||||||
}
|
}
|
||||||
|
// 重置选中的头像模型
|
||||||
|
this.selectedImageModel = ''
|
||||||
},
|
},
|
||||||
|
|
||||||
// 返回
|
// 返回
|
||||||
|
|||||||
@@ -99,7 +99,11 @@
|
|||||||
<el-table-column label="检测数量" prop="faceCount" width="100" align="center" />
|
<el-table-column label="检测数量" prop="faceCount" width="100" align="center" />
|
||||||
<el-table-column label="状态" prop="success" width="80" align="center">
|
<el-table-column label="状态" prop="success" width="80" align="center">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-tag :type="scope.row.success ? 'success' : 'danger'">
|
<el-tag
|
||||||
|
:type="scope.row.success ? 'success' : 'danger'"
|
||||||
|
:class="{ 'clickable-failure': !scope.row.success }"
|
||||||
|
@click="!scope.row.success ? handleFailureClick(scope.row) : null"
|
||||||
|
>
|
||||||
{{ scope.row.success ? '成功' : '失败' }}
|
{{ scope.row.success ? '成功' : '失败' }}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
</template>
|
</template>
|
||||||
@@ -179,6 +183,25 @@
|
|||||||
</div>
|
</div>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 失败原因弹窗 -->
|
||||||
|
<el-dialog
|
||||||
|
title="失败原因"
|
||||||
|
:visible.sync="failureDialogVisible"
|
||||||
|
width="600px"
|
||||||
|
@close="handleFailureDialogClose"
|
||||||
|
>
|
||||||
|
<div class="failure-content">
|
||||||
|
<p><strong>请求ID:</strong> {{ currentFailureRecord && currentFailureRecord.requestId }}</p>
|
||||||
|
<p><strong>失败原因:</strong></p>
|
||||||
|
<div class="failure-reason">
|
||||||
|
<pre>{{ failureReason }}</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="failureDialogVisible = false">关闭</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
<!-- 上传并检测对话框 -->
|
<!-- 上传并检测对话框 -->
|
||||||
<el-dialog
|
<el-dialog
|
||||||
title="上传并检测"
|
title="上传并检测"
|
||||||
@@ -255,6 +278,10 @@ export default {
|
|||||||
detectLoading: false,
|
detectLoading: false,
|
||||||
detailVisible: false,
|
detailVisible: false,
|
||||||
currentRecord: null,
|
currentRecord: null,
|
||||||
|
// 失败原因弹窗相关
|
||||||
|
failureDialogVisible: false,
|
||||||
|
currentFailureRecord: null,
|
||||||
|
failureReason: '',
|
||||||
// 上传头像相关 - 简化版
|
// 上传头像相关 - 简化版
|
||||||
uploadAvatarDialogVisible: false,
|
uploadAvatarDialogVisible: false,
|
||||||
uploadFileList: [],
|
uploadFileList: [],
|
||||||
@@ -414,6 +441,7 @@ export default {
|
|||||||
const formData = new FormData()
|
const formData = new FormData()
|
||||||
formData.append('file', file)
|
formData.append('file', file)
|
||||||
formData.append('type', 'avatar')
|
formData.append('type', 'avatar')
|
||||||
|
formData.append('model', this.uploadForm.model || 'liveportrait-detect')
|
||||||
formData.append('avatarName', this.uploadForm.avatarName || '')
|
formData.append('avatarName', this.uploadForm.avatarName || '')
|
||||||
formData.append('ownerName', this.uploadForm.ownerName || '')
|
formData.append('ownerName', this.uploadForm.ownerName || '')
|
||||||
formData.append('ownerPhone', this.uploadForm.ownerPhone || '')
|
formData.append('ownerPhone', this.uploadForm.ownerPhone || '')
|
||||||
@@ -421,6 +449,7 @@ export default {
|
|||||||
console.log('FormData内容:', {
|
console.log('FormData内容:', {
|
||||||
file: file.name,
|
file: file.name,
|
||||||
type: 'avatar',
|
type: 'avatar',
|
||||||
|
model: this.uploadForm.model,
|
||||||
avatarName: this.uploadForm.avatarName,
|
avatarName: this.uploadForm.avatarName,
|
||||||
ownerName: this.uploadForm.ownerName,
|
ownerName: this.uploadForm.ownerName,
|
||||||
ownerPhone: this.uploadForm.ownerPhone
|
ownerPhone: this.uploadForm.ownerPhone
|
||||||
@@ -438,6 +467,7 @@ export default {
|
|||||||
// 调用检测API
|
// 调用检测API
|
||||||
const requestData = {
|
const requestData = {
|
||||||
imageUrl: imageUrl,
|
imageUrl: imageUrl,
|
||||||
|
model: this.uploadForm.model || 'liveportrait-detect',
|
||||||
ownerName: this.uploadForm.ownerName || '',
|
ownerName: this.uploadForm.ownerName || '',
|
||||||
ownerPhone: this.uploadForm.ownerPhone || '',
|
ownerPhone: this.uploadForm.ownerPhone || '',
|
||||||
avatarName: this.uploadForm.avatarName || ''
|
avatarName: this.uploadForm.avatarName || ''
|
||||||
@@ -574,6 +604,7 @@ export default {
|
|||||||
// 调用检测API
|
// 调用检测API
|
||||||
const requestData = {
|
const requestData = {
|
||||||
imageUrl: imageUrl,
|
imageUrl: imageUrl,
|
||||||
|
model: this.uploadForm.model || 'liveportrait-detect',
|
||||||
ownerName: this.uploadForm.ownerName || '',
|
ownerName: this.uploadForm.ownerName || '',
|
||||||
ownerPhone: this.uploadForm.ownerPhone || '',
|
ownerPhone: this.uploadForm.ownerPhone || '',
|
||||||
avatarName: this.uploadForm.avatarName || ''
|
avatarName: this.uploadForm.avatarName || ''
|
||||||
@@ -598,6 +629,7 @@ export default {
|
|||||||
// 使用文件URL调用检测API
|
// 使用文件URL调用检测API
|
||||||
const requestData = {
|
const requestData = {
|
||||||
imageUrl: fileUrl,
|
imageUrl: fileUrl,
|
||||||
|
model: this.uploadForm.model || 'liveportrait-detect',
|
||||||
ownerName: this.uploadForm.ownerName || '',
|
ownerName: this.uploadForm.ownerName || '',
|
||||||
ownerPhone: this.uploadForm.ownerPhone || '',
|
ownerPhone: this.uploadForm.ownerPhone || '',
|
||||||
avatarName: this.uploadForm.avatarName || ''
|
avatarName: this.uploadForm.avatarName || ''
|
||||||
@@ -917,15 +949,18 @@ export default {
|
|||||||
this.detectLoading = true
|
this.detectLoading = true
|
||||||
const requestData = {
|
const requestData = {
|
||||||
imageUrl: this.detectForm.imageUrl,
|
imageUrl: this.detectForm.imageUrl,
|
||||||
|
model: this.detectForm.model || 'liveportrait-detect',
|
||||||
ownerName: this.detectForm.ownerName,
|
ownerName: this.detectForm.ownerName,
|
||||||
ownerPhone: this.detectForm.ownerPhone,
|
ownerPhone: this.detectForm.ownerPhone,
|
||||||
avatarName: this.detectForm.avatarName
|
avatarName: this.detectForm.avatarName
|
||||||
}
|
}
|
||||||
console.log('头像检测请求参数:', requestData)
|
console.log('头像检测请求参数:', requestData)
|
||||||
console.log('头像检测参数验证:', {
|
console.log('头像检测参数验证:', {
|
||||||
|
hasModel: !!requestData.model,
|
||||||
hasOwnerName: !!requestData.ownerName,
|
hasOwnerName: !!requestData.ownerName,
|
||||||
hasOwnerPhone: !!requestData.ownerPhone,
|
hasOwnerPhone: !!requestData.ownerPhone,
|
||||||
hasAvatarName: !!requestData.avatarName,
|
hasAvatarName: !!requestData.avatarName,
|
||||||
|
modelValue: requestData.model,
|
||||||
ownerNameValue: requestData.ownerName,
|
ownerNameValue: requestData.ownerName,
|
||||||
ownerPhoneValue: requestData.ownerPhone,
|
ownerPhoneValue: requestData.ownerPhone,
|
||||||
avatarNameValue: requestData.avatarName
|
avatarNameValue: requestData.avatarName
|
||||||
@@ -961,6 +996,50 @@ export default {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 处理失败状态点击
|
||||||
|
handleFailureClick(row) {
|
||||||
|
console.log('点击失败状态,记录:', row)
|
||||||
|
this.currentFailureRecord = row
|
||||||
|
|
||||||
|
// 解析responseData中的output内容
|
||||||
|
let failureReason = '未知错误'
|
||||||
|
if (row.responseData) {
|
||||||
|
try {
|
||||||
|
const responseData = JSON.parse(row.responseData)
|
||||||
|
if (responseData.output) {
|
||||||
|
// 提取output中的内容作为失败原因
|
||||||
|
if (responseData.output.message) {
|
||||||
|
failureReason = responseData.output.message
|
||||||
|
} else if (responseData.output.error) {
|
||||||
|
failureReason = responseData.output.error
|
||||||
|
} else if (responseData.output.reason) {
|
||||||
|
failureReason = responseData.output.reason
|
||||||
|
} else {
|
||||||
|
// 如果没有具体的错误信息,显示整个output对象
|
||||||
|
failureReason = JSON.stringify(responseData.output, null, 2)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
failureReason = JSON.stringify(responseData, null, 2)
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('解析responseData失败:', e)
|
||||||
|
failureReason = row.responseData
|
||||||
|
}
|
||||||
|
} else if (row.errorMessage) {
|
||||||
|
failureReason = row.errorMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
this.failureReason = failureReason
|
||||||
|
this.failureDialogVisible = true
|
||||||
|
},
|
||||||
|
|
||||||
|
// 失败原因弹窗关闭
|
||||||
|
handleFailureDialogClose() {
|
||||||
|
this.failureDialogVisible = false
|
||||||
|
this.currentFailureRecord = null
|
||||||
|
this.failureReason = ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1054,4 +1133,44 @@ export default {
|
|||||||
text-align: left;
|
text-align: left;
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 失败状态可点击样式 */
|
||||||
|
.clickable-failure {
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clickable-failure:hover {
|
||||||
|
opacity: 0.8;
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 失败原因弹窗样式 */
|
||||||
|
.failure-content {
|
||||||
|
padding: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.failure-content p {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
|
||||||
|
.failure-reason {
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
border: 1px solid #e4e7ed;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 15px;
|
||||||
|
max-height: 300px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.failure-reason pre {
|
||||||
|
margin: 0;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-all;
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.4;
|
||||||
|
color: #606266;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -97,9 +97,17 @@
|
|||||||
<el-table-column label="处理时间(ms)" prop="processingTimeMs" width="120" align="center" />
|
<el-table-column label="处理时间(ms)" prop="processingTimeMs" width="120" align="center" />
|
||||||
<el-table-column label="模型提供方" prop="modelProvider" width="120" />
|
<el-table-column label="模型提供方" prop="modelProvider" width="120" />
|
||||||
<el-table-column label="创建时间" prop="createdAt" width="160" />
|
<el-table-column label="创建时间" prop="createdAt" width="160" />
|
||||||
<el-table-column label="操作" width="200" fixed="right">
|
<el-table-column label="操作" width="250" fixed="right">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-button type="text" @click="handleView(scope.row)">查看</el-button>
|
<el-button type="text" @click="handleView(scope.row)">查看</el-button>
|
||||||
|
<el-button
|
||||||
|
v-if="scope.row.success"
|
||||||
|
type="text"
|
||||||
|
icon="el-icon-video-play"
|
||||||
|
@click="handlePlay(scope.row)"
|
||||||
|
>
|
||||||
|
播放
|
||||||
|
</el-button>
|
||||||
<el-button type="text" @click="handleDelete(scope.row)">删除</el-button>
|
<el-button type="text" @click="handleDelete(scope.row)">删除</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@@ -243,6 +251,40 @@
|
|||||||
<el-button @click="detailVisible = false">关闭</el-button>
|
<el-button @click="detailVisible = false">关闭</el-button>
|
||||||
</div>
|
</div>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 视频播放对话框 -->
|
||||||
|
<el-dialog
|
||||||
|
title="视频播放"
|
||||||
|
:visible.sync="playVisible"
|
||||||
|
width="800px"
|
||||||
|
@close="handlePlayClose"
|
||||||
|
>
|
||||||
|
<div v-if="currentRecord" class="play-content">
|
||||||
|
<div class="video-info">
|
||||||
|
<p><strong>视频名称:</strong> {{ currentRecord.videoName }}</p>
|
||||||
|
<p><strong>视频时长:</strong> {{ currentRecord.videoDuration }}秒</p>
|
||||||
|
<p><strong>视频比例:</strong> {{ currentRecord.videoRatio }}</p>
|
||||||
|
</div>
|
||||||
|
<div v-loading="playLoading" class="video-player">
|
||||||
|
<video
|
||||||
|
v-if="currentVideoUrl && !playLoading"
|
||||||
|
:src="currentVideoUrl"
|
||||||
|
controls
|
||||||
|
style="width: 100%; max-height: 400px;"
|
||||||
|
preload="metadata"
|
||||||
|
>
|
||||||
|
您的浏览器不支持视频播放
|
||||||
|
</video>
|
||||||
|
<div v-else-if="playLoading" class="loading-placeholder">
|
||||||
|
<i class="el-icon-loading" />
|
||||||
|
<p>正在加载视频...</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="playVisible = false">关闭</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -259,6 +301,9 @@ export default {
|
|||||||
selectedIds: [],
|
selectedIds: [],
|
||||||
detailVisible: false,
|
detailVisible: false,
|
||||||
currentRecord: null,
|
currentRecord: null,
|
||||||
|
playVisible: false,
|
||||||
|
currentVideoUrl: '',
|
||||||
|
playLoading: false,
|
||||||
// 查询参数
|
// 查询参数
|
||||||
queryParams: {
|
queryParams: {
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
@@ -409,6 +454,32 @@ export default {
|
|||||||
// 头像合成视频
|
// 头像合成视频
|
||||||
handleAvatarSynthesis() {
|
handleAvatarSynthesis() {
|
||||||
this.$router.push('/video/avatar-synthesis')
|
this.$router.push('/video/avatar-synthesis')
|
||||||
|
},
|
||||||
|
|
||||||
|
// 播放视频
|
||||||
|
handlePlay(row) {
|
||||||
|
// 直接使用videoTempUrl,无需调用后端接口
|
||||||
|
const videoUrl = row.videoTempUrl || row.videoUrl
|
||||||
|
|
||||||
|
if (!videoUrl) {
|
||||||
|
this.$message.warning('视频链接不存在,无法播放')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.playLoading = true
|
||||||
|
this.currentRecord = row
|
||||||
|
this.currentVideoUrl = videoUrl
|
||||||
|
this.playVisible = true
|
||||||
|
this.playLoading = false
|
||||||
|
this.$message.success('视频加载成功')
|
||||||
|
},
|
||||||
|
|
||||||
|
// 关闭播放对话框
|
||||||
|
handlePlayClose() {
|
||||||
|
this.playVisible = false
|
||||||
|
this.currentVideoUrl = ''
|
||||||
|
this.currentRecord = null
|
||||||
|
this.playLoading = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -449,4 +520,43 @@ export default {
|
|||||||
color: #303133;
|
color: #303133;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.play-content {
|
||||||
|
padding: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-info {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding: 15px;
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-info p {
|
||||||
|
margin: 8px 0;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #606266;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-player {
|
||||||
|
text-align: center;
|
||||||
|
min-height: 200px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-placeholder {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: #909399;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-placeholder i {
|
||||||
|
font-size: 24px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user