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') }} +
+ + + - - - - - - -
+ /> @@ -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