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') }} +
+ + + - - - - - - -
+ /> @@ -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 @@
-
-
- - - - -
-
- {{ $t("header.searchHistory") }} - - {{ $t("header.clearHistory") }} - -
-
-
- {{ item }} - -
-
-
-
-
- {{ userInfo.username || "加载中..." }} @@ -220,12 +196,6 @@ export default { voiceCloneDropdownVisible: false, userMenuVisible: false, // 添加用户菜单可见状态 menuVisibleTimer: null, // 菜单显示定时器,防止够快触发 - isSmallScreen: false, - // 搜索历史相关 - searchHistory: [], - showHistory: false, - SEARCH_HISTORY_KEY: "xiaozhi_search_history", - MAX_HISTORY_COUNT: 3, // Cascader 配置 cascaderProps: { expandTrigger: "click", @@ -351,17 +321,9 @@ export default { }, }, async mounted() { - this.checkScreenSize(); - window.addEventListener("resize", this.checkScreenSize); - // 从localStorage加载搜索历史 - this.loadSearchHistory(); // 等待featureManager初始化完成后再加载功能状态 await this.loadFeatureStatus(); }, - //移除事件监听器 - beforeDestroy() { - window.removeEventListener("resize", this.checkScreenSize); - }, methods: { handleRouter(type) { this.$router.push(this.routerPaths[type]); @@ -371,104 +333,6 @@ export default { // 等待featureManager初始化完成 await featureManager.waitForInitialization(); }, - checkScreenSize() { - this.isSmallScreen = window.innerWidth <= 1386; - }, - // 处理搜索 - handleSearch() { - const searchValue = this.search.trim(); - - // 如果搜索内容为空,触发重置事件 - if (!searchValue) { - this.$emit("search-reset"); - return; - } - - // 保存搜索历史 - this.saveSearchHistory(searchValue); - - // 触发搜索事件,将搜索关键词传递给父组件 - this.$emit("search", searchValue); - - // 搜索完成后让输入框失去焦点,从而触发blur事件隐藏搜索历史 - if (this.$refs.searchInput) { - this.$refs.searchInput.blur(); - } - }, - - // 显示搜索历史 - showSearchHistory() { - this.showHistory = true; - }, - - // 隐藏搜索历史 - hideSearchHistory() { - // 延迟隐藏,以便点击事件能够执行 - setTimeout(() => { - this.showHistory = false; - }, 200); - }, - - // 加载搜索历史 - loadSearchHistory() { - try { - const history = localStorage.getItem(this.SEARCH_HISTORY_KEY); - if (history) { - this.searchHistory = JSON.parse(history); - } - } catch (error) { - console.error("加载搜索历史失败:", error); - this.searchHistory = []; - } - }, - - // 保存搜索历史 - saveSearchHistory(keyword) { - if (!keyword || this.searchHistory.includes(keyword)) { - return; - } - - // 添加到历史记录开头 - this.searchHistory.unshift(keyword); - - // 限制历史记录数量 - if (this.searchHistory.length > this.MAX_HISTORY_COUNT) { - this.searchHistory = this.searchHistory.slice(0, this.MAX_HISTORY_COUNT); - } - - // 保存到localStorage - try { - localStorage.setItem(this.SEARCH_HISTORY_KEY, JSON.stringify(this.searchHistory)); - } catch (error) { - console.error("保存搜索历史失败:", error); - } - }, - - // 选择搜索历史项 - selectSearchHistory(keyword) { - this.search = keyword; - this.handleSearch(); - }, - - // 移除单个搜索历史项 - removeSearchHistory(index) { - this.searchHistory.splice(index, 1); - try { - localStorage.setItem(this.SEARCH_HISTORY_KEY, JSON.stringify(this.searchHistory)); - } catch (error) { - console.error("更新搜索历史失败:", error); - } - }, - - // 清空所有搜索历史 - clearSearchHistory() { - this.searchHistory = []; - try { - localStorage.removeItem(this.SEARCH_HISTORY_KEY); - } catch (error) { - console.error("清空搜索历史失败:", error); - } - }, // 显示修改密码弹窗 showChangePasswordDialog() { this.isChangePasswordDialogVisible = true; @@ -631,11 +495,9 @@ export default {