mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-25 00:23:53 +08:00
@@ -79,7 +79,7 @@ export default {
|
||||
},
|
||||
|
||||
// 复刻音频
|
||||
cloneAudio(params, callback) {
|
||||
cloneAudio(params, callback, errorCallback) {
|
||||
RequestService.sendRequest()
|
||||
.url(`${getServiceUrl()}/voiceClone/cloneAudio`)
|
||||
.method('POST')
|
||||
@@ -88,10 +88,19 @@ export default {
|
||||
RequestService.clearRequestTime();
|
||||
callback(res);
|
||||
})
|
||||
.fail((res) => {
|
||||
// 业务失败回调
|
||||
RequestService.clearRequestTime();
|
||||
if (errorCallback) {
|
||||
errorCallback(res);
|
||||
} else {
|
||||
callback(res);
|
||||
}
|
||||
})
|
||||
.networkFail((err) => {
|
||||
console.error('上传失败:', err);
|
||||
RequestService.reAjaxFun(() => {
|
||||
this.cloneAudio(params, callback);
|
||||
this.cloneAudio(params, callback, errorCallback);
|
||||
});
|
||||
}).send();
|
||||
}
|
||||
|
||||
@@ -876,6 +876,8 @@ export default {
|
||||
'ttsModel.operationClosed': 'Operation closed',
|
||||
'ttsModel.edit': 'Edit',
|
||||
'ttsModel.delete': 'Delete',
|
||||
'ttsModel.deleteVoiceSuccess': 'Successfully deleted {count} voices',
|
||||
'ttsModel.deleteFailed': 'Failed to delete voices',
|
||||
'ttsModel.save': 'Save',
|
||||
'ttsModel.deselectAll': 'Deselect All',
|
||||
'ttsModel.selectAll': 'Select All',
|
||||
@@ -885,6 +887,8 @@ export default {
|
||||
'ttsModel.unnamedVoice': 'Unnamed Voice',
|
||||
'ttsModel.finishEditingFirst': 'Please finish current editing first',
|
||||
'ttsModel.selectVoiceToDelete': 'Please select voices to delete',
|
||||
'ttsModel.warning': 'Warning',
|
||||
'ttsModel.confirmDeleteVoice': 'Are you sure to delete {count} voices?',
|
||||
|
||||
// OTA Management Page Text
|
||||
'otaManagement.firmwareManagement': 'Firmware Management',
|
||||
@@ -1019,7 +1023,7 @@ export default {
|
||||
'warning': 'Warning',
|
||||
'info': 'Info',
|
||||
'common.networkError': 'Network request failed',
|
||||
|
||||
|
||||
// SM2 Encryption
|
||||
'sm2.publicKeyNotConfigured': 'SM2 public key not configured, please contact administrator',
|
||||
'sm2.encryptionFailed': 'Password encryption failed',
|
||||
@@ -1095,6 +1099,8 @@ export default {
|
||||
'voiceClone.play': 'Play',
|
||||
'voiceClone.pause': 'Pause',
|
||||
'voiceClone.cancel': 'Cancel',
|
||||
'voiceClone.warning': 'Warning',
|
||||
'voiceClone.ok': 'OK',
|
||||
'voiceClone.nextStep': 'Next',
|
||||
'voiceClone.prevStep': 'Previous',
|
||||
'voiceClone.upload': 'Upload Audio',
|
||||
@@ -1108,4 +1114,5 @@ export default {
|
||||
'voiceClone.updateNameSuccess': 'Name updated successfully',
|
||||
'voiceClone.updateNameFailed': 'Failed to update name',
|
||||
'voiceClone.playFailed': 'Play failed',
|
||||
'voiceClone.Details': 'Error Details',
|
||||
}
|
||||
@@ -466,7 +466,7 @@ export default {
|
||||
'chatHistory.downloadCurrentSession': '下载本会话聊天记录',
|
||||
'chatHistory.downloadCurrentWithPreviousSessions': '下载本会话及前20条会话聊天记录',
|
||||
'chatHistory.downloadLinkFailed': '获取下载链接失败',
|
||||
|
||||
|
||||
'cache.status': '缓存状态',
|
||||
'cache.cdnEnabled': 'CDN模式已启用',
|
||||
'cache.cdnDisabled': 'CDN模式已禁用',
|
||||
@@ -876,6 +876,8 @@ export default {
|
||||
'ttsModel.operationClosed': '操作已关闭',
|
||||
'ttsModel.edit': '编辑',
|
||||
'ttsModel.delete': '删除',
|
||||
'ttsModel.deleteVoiceSuccess': '成功删除{count}个音色',
|
||||
'ttsModel.deleteFailed': '删除音色失败',
|
||||
'ttsModel.save': '保存',
|
||||
'ttsModel.deselectAll': '取消全选',
|
||||
'ttsModel.selectAll': '全选',
|
||||
@@ -885,6 +887,8 @@ export default {
|
||||
'ttsModel.unnamedVoice': '未命名音色',
|
||||
'ttsModel.finishEditingFirst': '请先完成当前编辑',
|
||||
'ttsModel.selectVoiceToDelete': '请选择要删除的音色',
|
||||
'ttsModel.warning': '警告',
|
||||
'ttsModel.confirmDeleteVoice': '确定要删除{count}个音色吗?',
|
||||
|
||||
// OTA管理页面文本
|
||||
'otaManagement.firmwareManagement': '固件管理',
|
||||
@@ -1019,7 +1023,7 @@ export default {
|
||||
'warning': '警告',
|
||||
'info': '提示',
|
||||
'common.networkError': '网络请求失败',
|
||||
|
||||
|
||||
// SM2加密相关错误消息
|
||||
'sm2.publicKeyNotConfigured': 'SM2公钥未配置,请联系管理员',
|
||||
'sm2.encryptionFailed': '密码加密失败',
|
||||
@@ -1095,6 +1099,8 @@ export default {
|
||||
'voiceClone.play': '播放',
|
||||
'voiceClone.pause': '暂停',
|
||||
'voiceClone.cancel': '取消',
|
||||
'voiceClone.warning': '警告',
|
||||
'voiceClone.ok': '确定',
|
||||
'voiceClone.nextStep': '下一步',
|
||||
'voiceClone.prevStep': '上一步',
|
||||
'voiceClone.upload': '上传音频',
|
||||
@@ -1108,4 +1114,5 @@ export default {
|
||||
'voiceClone.updateNameSuccess': '名称更新成功',
|
||||
'voiceClone.updateNameFailed': '名称更新失败',
|
||||
'voiceClone.playFailed': '播放失败',
|
||||
'voiceClone.Details': '错误详情',
|
||||
}
|
||||
@@ -16,7 +16,7 @@ export default {
|
||||
'header.paramDictionary': '參數字典',
|
||||
'header.paramManagement': '參數管理',
|
||||
'header.dictManagement': '字典管理',
|
||||
'header.agentTemplate': '預設角色模板',
|
||||
'header.agentTemplate': '預設角色模板',
|
||||
'header.searchHistory': '搜索歷史',
|
||||
'header.clearHistory': '清空歷史',
|
||||
'header.providerManagement': '字段管理',
|
||||
@@ -874,6 +874,8 @@ export default {
|
||||
'ttsModel.operation': '操作',
|
||||
'ttsModel.edit': '編輯',
|
||||
'ttsModel.delete': '刪除',
|
||||
'ttsModel.deleteVoiceSuccess': '成功刪除{count}個音色',
|
||||
'ttsModel.deleteFailed': '刪除音色失敗',
|
||||
'ttsModel.save': '保存',
|
||||
'ttsModel.deselectAll': '取消全選',
|
||||
'ttsModel.selectAll': '全選',
|
||||
@@ -883,6 +885,8 @@ export default {
|
||||
'ttsModel.unnamedVoice': '未命名音色',
|
||||
'ttsModel.finishEditingFirst': '請先完成目前編輯',
|
||||
'ttsModel.selectVoiceToDelete': '請選擇要刪除的音色',
|
||||
'ttsModel.warning': '警告',
|
||||
'ttsModel.confirmDeleteVoice': '確定要刪除{count}個音色嗎?',
|
||||
'ttsModel.operationFailed': '操作失敗',
|
||||
'ttsModel.operationClosed': '操作已關閉',
|
||||
|
||||
@@ -1095,6 +1099,8 @@ export default {
|
||||
'voiceClone.play': '播放',
|
||||
'voiceClone.pause': '暫停',
|
||||
'voiceClone.cancel': '取消',
|
||||
'voiceClone.warning': '警告',
|
||||
'voiceClone.ok': '確定',
|
||||
'voiceClone.nextStep': '下一步',
|
||||
'voiceClone.prevStep': '上一步',
|
||||
'voiceClone.upload': '上傳音頻',
|
||||
@@ -1108,4 +1114,5 @@ export default {
|
||||
'voiceClone.updateNameSuccess': '名稱更新成功',
|
||||
'voiceClone.updateNameFailed': '名稱更新失敗',
|
||||
'voiceClone.playFailed': '播放失敗',
|
||||
'voiceClone.Details': '錯誤詳情',
|
||||
}
|
||||
@@ -36,9 +36,22 @@
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('voiceClone.trainStatus')" prop="trainStatus" align="center">
|
||||
<template slot-scope="scope">
|
||||
{{ getTrainStatusText(scope.row) }}
|
||||
<div class="status-button" :class="getStatusButtonClass(scope.row)">
|
||||
<span>{{ getTrainStatusText(scope.row) }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column :label="$t('voiceClone.Details')" align="center" width="80">
|
||||
<template slot-scope="scope">
|
||||
<el-tooltip :content="getTooltipContent(scope.row)" placement="top">
|
||||
<el-button size="mini" type="text" icon="el-icon-info"
|
||||
@click="handleViewDetails(scope.row)">
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column :label="$t('voiceClone.action')" align="center" width="180">
|
||||
<template slot-scope="scope">
|
||||
<el-button v-if="scope.row.hasVoice" size="mini" type="text"
|
||||
@@ -164,6 +177,29 @@ export default {
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
getTooltipContent(row) {
|
||||
if (!row.hasVoice) {
|
||||
return '待上传';
|
||||
}
|
||||
switch (row.trainStatus) {
|
||||
case 0:
|
||||
return '待复刻';
|
||||
case 2:
|
||||
return '训练成功';
|
||||
case 3:
|
||||
// 训练失败时,根据错误信息智能展示
|
||||
if (row.trainError) {
|
||||
return `训练失败:${row.trainError}`;
|
||||
}
|
||||
return '训练失败';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
},
|
||||
handleViewDetails(row) {
|
||||
console.log('查看详情:', row);
|
||||
// 可以在这里添加查看详情的逻辑
|
||||
},
|
||||
handlePageSizeChange(val) {
|
||||
this.pageSize = val;
|
||||
this.currentPage = 1;
|
||||
@@ -226,8 +262,6 @@ export default {
|
||||
switch (row.trainStatus) {
|
||||
case 0:
|
||||
return this.$t('voiceClone.waitingTraining');
|
||||
case 1:
|
||||
return this.$t('voiceClone.training');
|
||||
case 2:
|
||||
return this.$t('voiceClone.trainSuccess');
|
||||
case 3:
|
||||
@@ -236,19 +270,100 @@ export default {
|
||||
return '';
|
||||
}
|
||||
},
|
||||
// 获取状态按钮样式
|
||||
getStatusButtonClass(row) {
|
||||
if (!row.hasVoice || row.trainStatus === 0) {
|
||||
return 'status-waiting';
|
||||
} else if (row.trainStatus === 2) {
|
||||
return 'status-success';
|
||||
} else if (row.trainStatus === 3) {
|
||||
return 'status-failed';
|
||||
}
|
||||
return '';
|
||||
},
|
||||
// 处理复刻操作
|
||||
handleClone(row) {
|
||||
// 防止重复提交
|
||||
if (row._submitting) {
|
||||
return;
|
||||
}
|
||||
row._submitting = true;
|
||||
|
||||
const params = {
|
||||
cloneId: row.id
|
||||
};
|
||||
Api.voiceClone.cloneAudio(params, (res) => {
|
||||
res = res.data;
|
||||
if (res.code === 0) {
|
||||
this.$message.success(this.$t('message.success'));
|
||||
} else {
|
||||
this.$message.error(res.msg || this.$t('message.error'));
|
||||
try {
|
||||
Api.voiceClone.cloneAudio(params, (res) => {
|
||||
try {
|
||||
res = res.data;
|
||||
if (res.code === 0) {
|
||||
this.$message.success(this.$t('message.success'));
|
||||
// 复刻成功后刷新列表
|
||||
this.fetchVoiceCloneList();
|
||||
} else {
|
||||
// 复刻失败时刷新列表以获取完整的错误信息
|
||||
console.log('API返回错误,刷新列表获取详细错误信息');
|
||||
this.$message.error(res.msg || this.$t('message.error'));
|
||||
// 刷新列表以获取后端保存的完整错误详情
|
||||
this.fetchVoiceCloneList();
|
||||
}
|
||||
} catch (error) {
|
||||
// 处理响应时出错,刷新列表
|
||||
console.error('处理响应时出错:', error);
|
||||
this.$message.error('处理响应时出错');
|
||||
this.fetchVoiceCloneList();
|
||||
} finally {
|
||||
row._submitting = false;
|
||||
}
|
||||
}, (error) => {
|
||||
// API调用失败,刷新列表以获取最新状态
|
||||
console.error('API调用失败:', error);
|
||||
this.$message.error('克隆失败,请将鼠标悬停在错误提示上,查看错误详情');
|
||||
this.fetchVoiceCloneList();
|
||||
row._submitting = false;
|
||||
});
|
||||
} catch (error) {
|
||||
// 调用API时出错,刷新列表
|
||||
console.error('调用API时出错:', error);
|
||||
this.$message.error('调用API时出错');
|
||||
this.fetchVoiceCloneList();
|
||||
row._submitting = false;
|
||||
}
|
||||
},
|
||||
|
||||
// 更新行状态并触发视图更新
|
||||
updateRowStatus(row, status, statusCode = null) {
|
||||
// 在Vue中直接修改数组中的对象属性可能不会触发视图更新
|
||||
const index = this.voiceCloneList.findIndex(item => item.id === row.id);
|
||||
const updateData = {
|
||||
trainStatus: status
|
||||
};
|
||||
|
||||
// 如果提供了状态码,也更新状态码信息
|
||||
if (statusCode !== null) {
|
||||
updateData.statusCode = statusCode;
|
||||
}
|
||||
|
||||
if (index !== -1) {
|
||||
// 使用Vue.set来确保响应式更新
|
||||
this.$set(this.voiceCloneList, index, {
|
||||
...this.voiceCloneList[index],
|
||||
...updateData
|
||||
});
|
||||
// 强制表格重新渲染
|
||||
if (this.$refs.paramsTable) {
|
||||
this.$refs.paramsTable.doLayout();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// 如果找不到索引,直接更新row对象
|
||||
row.trainStatus = status;
|
||||
if (statusCode !== null) {
|
||||
row.statusCode = statusCode;
|
||||
}
|
||||
// 强制整个表格重新渲染
|
||||
this.$forceUpdate();
|
||||
}
|
||||
console.log('更新行状态:', row.id, '状态:', status, '状态码:', statusCode);
|
||||
},
|
||||
// 复刻成功后的回调
|
||||
handleCloneSuccess() {
|
||||
@@ -619,6 +734,35 @@ export default {
|
||||
color: #5a64b5 !important;
|
||||
}
|
||||
|
||||
/* 状态按钮样式 */
|
||||
.status-button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 4px 10px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.status-waiting {
|
||||
background-color: #f5f7fa;
|
||||
color: #909399;
|
||||
border: 1px solid #e4e7ed;
|
||||
}
|
||||
|
||||
.status-success {
|
||||
background-color: #f6ffed;
|
||||
color: #52c41a;
|
||||
border: 1px solid #b7eb8f;
|
||||
}
|
||||
|
||||
.status-failed {
|
||||
background-color: #fff2f0;
|
||||
color: #ff4d4f;
|
||||
border: 1px solid #ffccc7;
|
||||
}
|
||||
|
||||
.name-view {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -249,9 +249,9 @@ export default {
|
||||
}
|
||||
|
||||
const itemCount = items.length;
|
||||
this.$confirm(this.$t('voiceClone.confirmDelete', { count: itemCount }), 'Warning', {
|
||||
confirmButtonText: 'OK',
|
||||
cancelButtonText: 'Cancel',
|
||||
this.$confirm(this.$t('voiceClone.confirmDelete', { count: itemCount }), this.$t('voiceClone.warning'), {
|
||||
confirmButtonText: this.$t('voiceClone.ok'),
|
||||
cancelButtonText: this.$t('voiceClone.cancel'),
|
||||
type: 'warning',
|
||||
distinguishCancelAndClose: true
|
||||
}).then(() => {
|
||||
|
||||
Reference in New Issue
Block a user