From 0f4f0f01fda4000a6139401b20bc111dd288d77e Mon Sep 17 00:00:00 2001
From: zhuoqinglian <1035449612@qq.com>
Date: Fri, 26 Jun 2026 14:45:23 +0800
Subject: [PATCH 1/5] =?UTF-8?q?update:=20=E9=9F=B3=E8=89=B2=E5=85=8B?=
=?UTF-8?q?=E9=9A=86=E9=A1=B5=E9=9D=A2=E5=B1=95=E7=8E=B0=E5=BD=A2=E5=BC=8F?=
=?UTF-8?q?=E4=BF=AE=E6=94=B9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../src/views/VoiceCloneManagement.vue | 346 +++++++++++++-----
1 file changed, 254 insertions(+), 92 deletions(-)
diff --git a/main/manager-web/src/views/VoiceCloneManagement.vue b/main/manager-web/src/views/VoiceCloneManagement.vue
index 0fa67db5..1d2978ef 100644
--- a/main/manager-web/src/views/VoiceCloneManagement.vue
+++ b/main/manager-web/src/views/VoiceCloneManagement.vue
@@ -13,59 +13,91 @@
{{ $t('voiceClone.search') }}
-
+
+
+

+
+
+
+
+ {{ item.name || '-' }}
+
+
+
+ {{ $t('voiceClone.voiceId') }}
+ {{ item.voiceId || '-' }}
+
+
+ {{ $t('voiceClone.languages') }}
+ {{ item.languages || '-' }}
+
+
+
+
+
+
+ {{ getTrainStatusText(item) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ playingRowId === item.id ? $t('voiceClone.stop') : $t('voiceClone.play') }}
+
+
+ {{ $t('voiceClone.upload') }}
+
+
+ {{ $t('voiceClone.clone') }}
+
+
+ {{ item.isEdit ? '保存' : '编辑' }}
+
+
+
+
+
+ {{ $t('voiceClone.noVoiceCloneAssigned') }}
+
+
+
+
-
-
-
-
-
- {{ scope.row.name || '-' }}
-
-
-
-
-
- {{ getTrainStatusText(scope.row) }}
-
-
-
-
-
-
-
-
-
-
- {{ playingRowId === scope.row.id ? $t('voiceClone.stop') : $t('voiceClone.play') }}
-
-
- {{ $t('voiceClone.upload') }}
-
-
- {{ $t('voiceClone.clone') }}
-
-
-
-
-
-
-
+ />
@@ -86,11 +118,11 @@ import HeaderBar from "@/components/HeaderBar.vue";
import VersionFooter from "@/components/VersionFooter.vue";
import VoiceCloneDialog from "@/components/VoiceCloneDialog.vue";
import CustomButton from "@/components/CustomButton.vue";
-import CustomTable from "@/components/CustomTable.vue";
+import CustomPagination from "@/components/CustomPagination.vue";
import { formatDate } from "@/utils/format";
export default {
- components: { HeaderBar, VersionFooter, VoiceCloneDialog, CustomButton, CustomTable },
+ components: { HeaderBar, VersionFooter, VoiceCloneDialog, CustomButton, CustomPagination },
data() {
return {
searchName: "",
@@ -108,25 +140,13 @@ export default {
userId: null
},
currentAudio: null,
- playingRowId: null,
- tableColumns: []
+ playingRowId: null
};
},
created() {
- this.initTableColumns();
this.fetchVoiceCloneList();
},
methods: {
- initTableColumns() {
- this.tableColumns = [
- { prop: 'voiceId', label: this.$t('voiceClone.voiceId'), align: 'center' },
- { prop: 'name', label: this.$t('voiceClone.name'), align: 'center' },
- { prop: 'languages', label: this.$t('voiceClone.languages'), align: 'center' },
- { prop: 'trainStatus', label: this.$t('voiceClone.trainStatus'), align: 'center' },
- { prop: 'Details', label: this.$t('voiceClone.Details'), align: 'center', width: 120 },
- { prop: 'action', label: this.$t('voiceClone.action'), align: 'center', width: 230 }
- ];
- },
getTooltipContent(row) {
if (!row.hasVoice) {
return '待上传';
@@ -287,11 +307,13 @@ export default {
row._submitting = false;
});
},
- onNameBlur(row) {
- row.isEdit = false;
- setTimeout(() => {
+ handleEditButtonClick(row) {
+ if (row.isEdit) {
+ row.isEdit = false;
this.submitName(row);
- }, 100);
+ } else {
+ this.handleEditName(row);
+ }
},
onNameEnter(row) {
row.isEdit = false;
@@ -430,28 +452,6 @@ export default {
}
}
-.name-view {
- display: inline-flex;
- align-items: center;
- gap: 6px;
- cursor: pointer;
-
- i {
- color: #909399;
- font-size: 14px;
-
- &:hover {
- color: #5a64b5;
- }
- }
-
- span {
- &:hover {
- color: #5a64b5;
- }
- }
-}
-
.status-button {
display: inline-flex;
align-items: center;
@@ -480,11 +480,173 @@ export default {
border: 1px solid #ffccc7;
}
-:deep(.el-table .el-button--text) {
- color: #7079aa;
+.voice-clone-grid {
+ height: calc(100% - 90px);
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(360px, 1fr));
+ gap: 16px;
+ align-content: start;
+ min-height: 200px;
}
-:deep(.el-table .el-button--text:hover) {
- color: #5a64b5;
+.voice-clone-card {
+ display: flex;
+ flex-direction: column;
+ gap: 14px;
+ padding: 16px 16px 8px;
+ background: #fff;
+ border: 1px solid #ebeef5;
+ border-radius: 8px;
+ transition: box-shadow 0.2s, border-color 0.2s;
+
+ &:hover {
+ border-color: #c0caff;
+ box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
+ }
+}
+
+.card-top {
+ display: flex;
+ gap: 12px;
+}
+
+.voice-wave {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 5px;
+ flex: 1;
+ min-height: 40px;
+ width: 100%;
+ padding: 0 4px;
+
+ .wave-bar {
+ width: 3px;
+ height: 50%;
+ max-height: 80px;
+ background: linear-gradient(to top, #a5b4fc 0%, #c4b5fd 100%);
+ border-radius: 2px;
+ transform-origin: center;
+ animation: voice-wave 1.2s ease-in-out infinite paused;
+
+ &:nth-child(1) { animation-delay: -0s; }
+ &:nth-child(2) { animation-delay: -0.12s; }
+ &:nth-child(3) { animation-delay: -0.24s; }
+ &:nth-child(4) { animation-delay: -0.36s; }
+ &:nth-child(5) { animation-delay: -0.48s; }
+ &:nth-child(6) { animation-delay: -0.6s; }
+ &:nth-child(7) { animation-delay: -0.72s; }
+ &:nth-child(8) { animation-delay: -0.84s; }
+ &:nth-child(9) { animation-delay: -0.96s; }
+ &:nth-child(10) { animation-delay: -1.08s; }
+ &:nth-child(11) { animation-delay: -1.2s; }
+ }
+
+ &.is-playing .wave-bar {
+ animation-play-state: running;
+ }
+}
+
+@keyframes voice-wave {
+ 0%, 100% { transform: scaleY(0.2); }
+ 50% { transform: scaleY(0.95); }
+}
+
+.card-info {
+ flex: 1;
+ min-width: 0;
+ display: flex;
+ gap: 12px;
+}
+
+.info-left {
+ flex: 1;
+ min-width: 0;
+ display: flex;
+ flex-direction: column;
+ justify-content: space-evenly;
+}
+
+.info-right {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-end;
+ gap: 10px;
+ flex-shrink: 0;
+ min-width: 100px;
+}
+
+.info-title {
+ min-width: 0;
+ font-size: 16px;
+ font-weight: 500;
+ color: #303133;
+
+ .name-text {
+ line-height: 28px;
+ text-align: left;
+ display: block;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+}
+
+.info-grid {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+}
+
+.info-row {
+ display: flex;
+ align-items: center;
+ font-size: 13px;
+ line-height: 1.5;
+}
+
+.info-label {
+ margin-right: 10px;
+ color: #909399;
+ flex-shrink: 0;
+}
+
+.info-value {
+ color: #303133;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.card-actions {
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ gap: 4px 8px;
+ padding-top: 10px;
+ border-top: 1px solid #f0f2f5;
+
+ ::v-deep .el-button {
+ display: flex;
+ align-items: center;
+ }
+
+ ::v-deep .el-button--text {
+ color: #7079aa;
+ }
+
+ ::v-deep .el-button--text:hover {
+ color: #5a64b5;
+ }
+}
+
+.empty-state {
+ grid-column: 1 / -1;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 60px 0;
+ color: #909399;
+ font-size: 14px;
}
\ No newline at end of file
From 979ef24b795818117ac899e680e1dcfff9501616 Mon Sep 17 00:00:00 2001
From: zhuoqinglian <1035449612@qq.com>
Date: Fri, 26 Jun 2026 15:32:48 +0800
Subject: [PATCH 2/5] =?UTF-8?q?update:=20=E9=9F=B3=E8=89=B2=E5=85=8B?=
=?UTF-8?q?=E9=9A=86=E5=A4=9A=E8=AF=AD=E8=A8=80=E9=85=8D=E7=BD=AE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
main/manager-web/src/i18n/de.js | 3 ++
main/manager-web/src/i18n/en.js | 3 ++
main/manager-web/src/i18n/pt_BR.js | 5 ++-
main/manager-web/src/i18n/vi.js | 3 ++
main/manager-web/src/i18n/zh_CN.js | 3 ++
main/manager-web/src/i18n/zh_TW.js | 3 ++
.../src/views/VoiceCloneManagement.vue | 38 ++++++++-----------
7 files changed, 35 insertions(+), 23 deletions(-)
diff --git a/main/manager-web/src/i18n/de.js b/main/manager-web/src/i18n/de.js
index 18011fd3..d1a42bb9 100644
--- a/main/manager-web/src/i18n/de.js
+++ b/main/manager-web/src/i18n/de.js
@@ -1205,6 +1205,8 @@ export default {
'voiceClone.operationClosed': 'Popup geschlossen',
'voiceClone.cloneSuccess': 'Klonen erfolgreich',
'voiceClone.cloneFailed': 'Klonen fehlgeschlagen',
+ 'voiceClone.cloneErrorTip': 'Klonen fehlgeschlagen, bitte bewegen Sie die Maus über die Fehlermeldung für Details',
+ 'voiceClone.apiError': 'API-Aufruf Fehler',
'voiceClone.confirmClone': 'Sind Sie sicher, dass Sie diese Stimme klonen möchten?',
'voiceClone.onlySuccessCanClone': 'Nur erfolgreich trainierte Stimmen können geklont werden',
'common.insufficient': 'Unzureichend',
@@ -1222,6 +1224,7 @@ export default {
'voiceClone.training': 'Training',
'voiceClone.trainSuccess': 'Training erfolgreich',
'voiceClone.trainFailed': 'Training fehlgeschlagen',
+ 'voiceClone.trainFailedWithError': 'Training fehlgeschlagen: {error}',
'voiceClone.itemsPerPage': '{items} Einträge/Seite',
'voiceClone.firstPage': 'Erste Seite',
'voiceClone.prevPage': 'Vorherige Seite',
diff --git a/main/manager-web/src/i18n/en.js b/main/manager-web/src/i18n/en.js
index 93fc69e7..0248a653 100644
--- a/main/manager-web/src/i18n/en.js
+++ b/main/manager-web/src/i18n/en.js
@@ -1205,6 +1205,8 @@ export default {
'voiceClone.operationClosed': 'Popup closed',
'voiceClone.cloneSuccess': 'Clone successful',
'voiceClone.cloneFailed': 'Clone failed',
+ 'voiceClone.cloneErrorTip': 'Clone failed, please hover over the error tip to view error details',
+ 'voiceClone.apiError': 'API call error',
'voiceClone.confirmClone': 'Are you sure you want to clone this voice?',
'voiceClone.onlySuccessCanClone': 'Only successfully trained voices can be cloned',
'common.insufficient': 'Insufficient',
@@ -1222,6 +1224,7 @@ export default {
'voiceClone.training': 'Training',
'voiceClone.trainSuccess': 'Training successful',
'voiceClone.trainFailed': 'Training failed',
+ 'voiceClone.trainFailedWithError': 'Training failed: {error}',
'voiceClone.itemsPerPage': '{items} items/page',
'voiceClone.firstPage': 'First Page',
'voiceClone.prevPage': 'Previous Page',
diff --git a/main/manager-web/src/i18n/pt_BR.js b/main/manager-web/src/i18n/pt_BR.js
index 63f63803..90e9fc87 100644
--- a/main/manager-web/src/i18n/pt_BR.js
+++ b/main/manager-web/src/i18n/pt_BR.js
@@ -1203,8 +1203,10 @@ export default {
'voiceClone.selectFirst': 'Por favor, selecione recursos de voz para excluir primeiro',
'voiceClone.operationCancelled': 'Cancelar',
'voiceClone.operationClosed': 'Popup fechado',
- 'voiceClone.cloneSuccess': 'Clonado com sucesso',
+ 'voiceClone.cloneSuccess': 'Clonagem bem-sucedida',
'voiceClone.cloneFailed': 'Falha ao clonar',
+ 'voiceClone.cloneErrorTip': 'Falha na clonagem, passe o mouse sobre a dica de erro para ver os detalhes',
+ 'voiceClone.apiError': 'Erro na chamada da API',
'voiceClone.confirmClone': 'Tem certeza de que deseja clonar esta voz?',
'voiceClone.onlySuccessCanClone': 'Apenas vozes treinadas com sucesso podem ser clonadas',
'common.insufficient': 'Insuficiente',
@@ -1222,6 +1224,7 @@ export default {
'voiceClone.training': 'Treinando',
'voiceClone.trainSuccess': 'Treinamento bem-sucedido',
'voiceClone.trainFailed': 'Treinamento falhou',
+ 'voiceClone.trainFailedWithError': 'Treinamento falhou: {error}',
'voiceClone.itemsPerPage': '{items} itens/página',
'voiceClone.firstPage': 'Primeira Página',
'voiceClone.prevPage': 'Página Anterior',
diff --git a/main/manager-web/src/i18n/vi.js b/main/manager-web/src/i18n/vi.js
index 7009d347..b58023aa 100644
--- a/main/manager-web/src/i18n/vi.js
+++ b/main/manager-web/src/i18n/vi.js
@@ -1205,6 +1205,8 @@ export default {
'voiceClone.operationClosed': 'Đã đóng popup',
'voiceClone.cloneSuccess': 'Nhân bản thành công',
'voiceClone.cloneFailed': 'Nhân bản thất bại',
+ 'voiceClone.cloneErrorTip': 'Nhân bản thất bại, hãy di chuột qua thông báo lỗi để xem chi tiết',
+ 'voiceClone.apiError': 'Lỗi gọi API',
'voiceClone.confirmClone': 'Bạn có chắc chắn muốn nhân bản giọng này?',
'voiceClone.onlySuccessCanClone': 'Chỉ có thể nhân bản giọng đã huấn luyện thành công',
'common.insufficient': 'Không đủ',
@@ -1222,6 +1224,7 @@ export default {
'voiceClone.training': 'Đang huấn luyện',
'voiceClone.trainSuccess': 'Huấn luyện thành công',
'voiceClone.trainFailed': 'Huấn luyện thất bại',
+ 'voiceClone.trainFailedWithError': 'Huấn luyện thất bại: {error}',
'voiceClone.itemsPerPage': '{items} mục/trang',
'voiceClone.firstPage': 'Trang đầu',
'voiceClone.prevPage': 'Trang trước',
diff --git a/main/manager-web/src/i18n/zh_CN.js b/main/manager-web/src/i18n/zh_CN.js
index 57768382..badd75f3 100644
--- a/main/manager-web/src/i18n/zh_CN.js
+++ b/main/manager-web/src/i18n/zh_CN.js
@@ -1205,6 +1205,8 @@ export default {
'voiceClone.action': '操作',
'voiceClone.cloneSuccess': '复刻成功',
'voiceClone.cloneFailed': '复刻失败',
+ 'voiceClone.cloneErrorTip': '克隆失败,请将鼠标悬停在错误提示上,查看错误详情',
+ 'voiceClone.apiError': '调用API时出错',
'voiceClone.confirmClone': '确定要复刻此音色吗?',
'voiceClone.onlySuccessCanClone': '只有训练成功的音色才能复刻',
'common.insufficient': '不足',
@@ -1222,6 +1224,7 @@ export default {
'voiceClone.training': '训练中',
'voiceClone.trainSuccess': '训练成功',
'voiceClone.trainFailed': '训练失败',
+ 'voiceClone.trainFailedWithError': '训练失败:{error}',
'voiceClone.itemsPerPage': '{items}条/页',
'voiceClone.firstPage': '首页',
'voiceClone.prevPage': '上一页',
diff --git a/main/manager-web/src/i18n/zh_TW.js b/main/manager-web/src/i18n/zh_TW.js
index e8c5e8b4..cc89ba2f 100644
--- a/main/manager-web/src/i18n/zh_TW.js
+++ b/main/manager-web/src/i18n/zh_TW.js
@@ -1205,6 +1205,8 @@ export default {
'voiceClone.operationClosed': '已關閉彈窗',
'voiceClone.cloneSuccess': '複刻成功',
'voiceClone.cloneFailed': '複刻失敗',
+ 'voiceClone.cloneErrorTip': '複刻失敗,請將鼠標懸停在錯誤提示上,查看錯誤詳情',
+ 'voiceClone.apiError': '調用API時出錯',
'voiceClone.confirmClone': '確定要複刻此音色嗎?',
'voiceClone.onlySuccessCanClone': '只有訓練成功的音色才能複刻',
'common.insufficient': '不足',
@@ -1222,6 +1224,7 @@ export default {
'voiceClone.training': '訓練中',
'voiceClone.trainSuccess': '訓練成功',
'voiceClone.trainFailed': '訓練失敗',
+ 'voiceClone.trainFailedWithError': '訓練失敗:{error}',
'voiceClone.itemsPerPage': '{items}條/頁',
'voiceClone.firstPage': '首頁',
'voiceClone.prevPage': '上一頁',
diff --git a/main/manager-web/src/views/VoiceCloneManagement.vue b/main/manager-web/src/views/VoiceCloneManagement.vue
index 1d2978ef..fcae466a 100644
--- a/main/manager-web/src/views/VoiceCloneManagement.vue
+++ b/main/manager-web/src/views/VoiceCloneManagement.vue
@@ -34,20 +34,14 @@
{{ $t('voiceClone.languages') }}
{{ item.languages || '-' }}
-
-
- {{ getTrainStatusText(item) }}
-
+
+
+ {{ getTrainStatusText(item) }}
+
+
@@ -80,7 +74,7 @@
- {{ item.isEdit ? '保存' : '编辑' }}
+ {{ item.isEdit ? $t('button.save') : $t('common.edit') }}
@@ -149,18 +143,18 @@ export default {
methods: {
getTooltipContent(row) {
if (!row.hasVoice) {
- return '待上传';
+ return this.$t('voiceClone.waitingUpload');
}
switch (row.trainStatus) {
case 0:
- return '待复刻';
+ return this.$t('voiceClone.waitingTraining');
case 2:
- return '训练成功';
+ return this.$t('voiceClone.trainSuccess');
case 3:
if (row.trainError) {
- return `训练失败:${row.trainError}`;
+ return this.$t('voiceClone.trainFailedWithError', { error: row.trainError });
}
- return '训练失败';
+ return this.$t('voiceClone.trainFailed');
default:
return '';
}
@@ -260,13 +254,13 @@ export default {
}
}, (error) => {
console.error('API调用失败:', error);
- this.$message.error('克隆失败,请将鼠标悬停在错误提示上,查看错误详情');
+ this.$message.error(this.$t('voiceClone.cloneErrorTip'));
this.fetchVoiceCloneList();
this.$set(row, '_cloning', false);
});
} catch (error) {
console.error('调用API时出错:', error);
- this.$message.error('调用API时出错');
+ this.$message.error(this.$t('voiceClone.apiError'));
this.fetchVoiceCloneList();
this.$set(row, '_cloning', false);
}
@@ -377,7 +371,7 @@ export default {
position: relative;
flex-direction: column;
background-size: cover;
- background: linear-gradient(to bottom right, #dce8ff, #e4eeff, #e6cbfd) center;
+ background: #eff4ff;
-webkit-background-size: cover;
-o-background-size: cover;
overflow: hidden;
@@ -491,11 +485,11 @@ export default {
.voice-clone-card {
display: flex;
- flex-direction: column;
+ flex-direction: column;
gap: 14px;
padding: 16px 16px 8px;
background: #fff;
- border: 1px solid #ebeef5;
+ box-shadow: 0 0 10px 0 #e8ecf5;
border-radius: 8px;
transition: box-shadow 0.2s, border-color 0.2s;
From b052158cee91abc78e5994b8fd7334eca9a7d9ee Mon Sep 17 00:00:00 2001
From: zhuoqinglian <1035449612@qq.com>
Date: Fri, 26 Jun 2026 17:10:49 +0800
Subject: [PATCH 3/5] =?UTF-8?q?feat:=20=E5=AE=8C=E6=88=90=E5=AF=BC?=
=?UTF-8?q?=E8=88=AA=E8=8F=9C=E5=8D=95=E7=BB=84=E4=BB=B6=E6=A0=B7=E5=BC=8F?=
=?UTF-8?q?=E4=BC=98=E5=8C=96?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
main/manager-web/src/components/HeaderBar.vue | 298 ++----------------
1 file changed, 28 insertions(+), 270 deletions(-)
diff --git a/main/manager-web/src/components/HeaderBar.vue b/main/manager-web/src/components/HeaderBar.vue
index 476358db..552beb56 100644
--- a/main/manager-web/src/components/HeaderBar.vue
+++ b/main/manager-web/src/components/HeaderBar.vue
@@ -101,7 +101,8 @@
$route.path === '/agent-template-management' ||
$route.path === '/ota-management' ||
$route.path === '/user-management' ||
- $route.path === '/feature-management',
+ $route.path === '/feature-management' ||
+ $route.path === '/replacement-word-management'
}" @visible-change="handleParamDropdownVisibleChange">
@@ -154,32 +156,6 @@