From d1f09c6afb09c015e6213ceeec5aa8372e3ae0a4 Mon Sep 17 00:00:00 2001 From: ZF sun <34314687@qq.com> Date: Thu, 7 May 2026 13:55:48 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E5=A4=9A=E8=AF=AD=E8=A8=80):=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E6=8F=90=E7=A4=BA=E6=96=87=E6=9C=AC=E7=9A=84=E5=A4=9A?= =?UTF-8?q?=E8=AF=AD=E8=A8=80=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在状态管理中新增当前语言和可用语言列表字段 - 为提示文本添加多语言输入框生成功能 - 实现根据当前语言自动更新提示文本显示 - 重构重启对话逻辑为独立函数并添加重试机制 - 将提示文本多语言配置保存到本地存储 --- frontend/index.html | 3 + frontend/scripts/main.js | 192 ++++++++++++++++++++++++++++++++------- 2 files changed, 162 insertions(+), 33 deletions(-) diff --git a/frontend/index.html b/frontend/index.html index 6db64c3..3114751 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -146,6 +146,9 @@ +
+ +
diff --git a/frontend/scripts/main.js b/frontend/scripts/main.js index 19593ed..4fc3c47 100644 --- a/frontend/scripts/main.js +++ b/frontend/scripts/main.js @@ -52,7 +52,10 @@ const state = { isMuted: false, jobInfo: null, currentInteractionMode: InteractionMode.AUDIO, - keepAliveActive: false + keepAliveActive: false, + currentLanguage: 'zh_CN', + availableLanguages: [], + tipTextValues: {} }; const elements = { @@ -86,6 +89,7 @@ const elements = { tipBgOpacity: document.getElementById('tipBgOpacity'), tipBorderRadius: document.getElementById('tipBorderRadius'), tipPadding: document.getElementById('tipPadding'), + tipTextFieldsContainer: document.getElementById('tipTextFieldsContainer'), messageFontSize: document.getElementById('messageFontSize'), messageFontWeight: document.getElementById('messageFontWeight'), @@ -220,6 +224,15 @@ const eventListeners = { [ICSEventKey.ERROR]: (error) => { logger.error('SDK 错误', error); updateConnectionStatus(false); + if (error?.error_code === '999400003' || error?.errorMsg?.includes('MUTE_TIMEOUT')) { + logger.warn('检测到静默超时,正在重新开始对话...'); + state.isChatting = false; + state.jobInfo = null; + updateUIState(); + if (state.keepAliveActive) { + setTimeout(() => restartChat(), 2000); + } + } }, [ICSEventKey.JOB_INFO_CHANGE]: (jobInfo) => { @@ -270,38 +283,7 @@ const eventListeners = { state.jobInfo = null; updateUIState(); if (state.keepAliveActive) { - setTimeout(async () => { - try { - logger.info('正在调用 startChat 重新开始对话...'); - const chatResult = await HwICSUiSdk.startChat({ - interactionMode: InteractionMode.AUDIO, - wakeupType: WakeupType.VOICE, - preCheckPermissions: false - }); - if (chatResult.result) { - logger.info('重新开始对话成功', chatResult); - state.isChatting = true; - updateUIState(); - } else { - logger.warn('重新开始对话返回失败', chatResult); - if (chatResult.errorCode === '999400004') { - logger.info('对话已存在,尝试 activeInteractionMode'); - await HwICSUiSdk.activeInteractionMode(); - state.isChatting = true; - updateUIState(); - } - } - } catch (error) { - logger.error('重新激活失败', error); - try { - await HwICSUiSdk.activeInteractionMode(); - state.isChatting = true; - updateUIState(); - } catch (e) { - logger.error('activeInteractionMode 也失败', e); - } - } - }, 2000); + setTimeout(() => restartChat(), 2000); } } }; @@ -309,6 +291,48 @@ const eventListeners = { let keepAliveTimer = null; const KEEP_ALIVE_INTERVAL = 10000; +async function restartChat(retryCount = 0) { + const MAX_RETRIES = 3; + try { + logger.info('正在调用 startChat 重新开始对话...'); + const chatResult = await HwICSUiSdk.startChat({ + interactionMode: InteractionMode.AUDIO, + wakeupType: WakeupType.VOICE, + preCheckPermissions: false + }); + if (chatResult.result) { + logger.info('重新开始对话成功', chatResult); + state.isChatting = true; + updateUIState(); + } else { + logger.warn('重新开始对话返回失败', chatResult); + if (chatResult.errorCode === '999400004') { + logger.info('对话已存在,尝试 activeInteractionMode'); + await HwICSUiSdk.activeInteractionMode(); + state.isChatting = true; + updateUIState(); + } else if (retryCount < MAX_RETRIES) { + logger.info(`将在 3 秒后重试 (${retryCount + 1}/${MAX_RETRIES})`); + setTimeout(() => restartChat(retryCount + 1), 3000); + } + } + } catch (error) { + logger.error('重新激活失败', error); + if (retryCount < MAX_RETRIES) { + logger.info(`将在 3 秒后重试 (${retryCount + 1}/${MAX_RETRIES})`); + setTimeout(() => restartChat(retryCount + 1), 3000); + } else { + try { + await HwICSUiSdk.activeInteractionMode(); + state.isChatting = true; + updateUIState(); + } catch (e) { + logger.error('activeInteractionMode 也失败', e); + } + } + } +} + function startKeepAlive() { if (keepAliveTimer) return; state.keepAliveActive = true; @@ -409,6 +433,8 @@ async function loadLanguageOptions() { if (langInfo.currentLanguage) { elements.languageSelect.value = langInfo.currentLanguage; } + + generateTipTextFields(langInfo.languageList); } else { elements.languageSelect.innerHTML = ''; } @@ -429,10 +455,14 @@ function updateLanguageSelect(langInfo) { option.textContent = item.language_desc; elements.languageSelect.appendChild(option); }); + + generateTipTextFields(langInfo.languageList); } if (langInfo.currentLanguage) { elements.languageSelect.value = langInfo.currentLanguage; + state.currentLanguage = langInfo.currentLanguage; + updateTipTextFromState(); } } @@ -611,6 +641,94 @@ function clearLogs() { logger.info('日志已清空'); } +async function loadTipTextFields() { + if (state.availableLanguages.length > 0) { + generateTipTextFields(state.availableLanguages); + return; + } + + if (!state.isInitialized) return; + + try { + const langInfo = await HwICSUiSdk.getLanguageInfo(); + if (langInfo.languageList && langInfo.languageList.length > 0) { + state.availableLanguages = langInfo.languageList; + generateTipTextFields(langInfo.languageList); + } + } catch (error) { + logger.warn('获取语言信息用于提示文本失败', error); + } +} + +function generateTipTextFields(languageList) { + if (!elements.tipTextFieldsContainer) return; + + elements.tipTextFieldsContainer.innerHTML = ''; + + if (!languageList || languageList.length === 0) { + elements.tipTextFieldsContainer.innerHTML = '

暂无可用的语言信息

'; + return; + } + + state.availableLanguages = languageList; + + languageList.forEach(item => { + const langCode = item.language; + const langDesc = item.language_desc; + + if (!state.tipTextValues[langCode]) { + const defaultTexts = { + 'zh_CN': '点击开始对话', + 'en_US': 'Click to start conversation' + }; + state.tipTextValues[langCode] = defaultTexts[langCode] || ''; + } + + const group = document.createElement('div'); + group.className = 'form-group'; + group.dataset.langCode = langCode; + + const label = document.createElement('label'); + label.setAttribute('for', `tipText_${langCode}`); + label.textContent = `提示文本 (${langDesc})`; + + const input = document.createElement('input'); + input.type = 'text'; + input.id = `tipText_${langCode}`; + input.value = state.tipTextValues[langCode]; + input.placeholder = `请输入${langDesc}提示文本`; + + input.addEventListener('input', () => { + state.tipTextValues[langCode] = input.value; + const tipEl = document.querySelector('.caption-container-hub .layout-hub-tip'); + if (tipEl && state.currentLanguage === langCode) { + tipEl.textContent = input.value; + } + saveCaptionStyleToStorage(); + }); + + group.appendChild(label); + group.appendChild(input); + elements.tipTextFieldsContainer.appendChild(group); + }); +} + +function updateTipText(tipEl) { + const currentLang = state.currentLanguage || 'zh_CN'; + const text = state.tipTextValues[currentLang] || state.tipTextValues['zh_CN'] || ''; + if (tipEl) { + tipEl.textContent = text; + } +} + +function updateTipTextFromState() { + const tipEl = document.querySelector('.caption-container-hub .layout-hub-tip'); + if (!tipEl) return; + const currentLang = state.currentLanguage || 'zh_CN'; + const text = state.tipTextValues[currentLang] || state.tipTextValues['zh_CN'] || ''; + tipEl.textContent = text; +} + function applyCaptionStyle() { const captionContainer = document.querySelector('.caption-container-hub'); if (!captionContainer) { @@ -636,6 +754,7 @@ function applyCaptionStyle() { tipEl.style.backgroundColor = hexToRgba(elements.tipBgColor.value, elements.tipBgOpacity.value / 100); tipEl.style.borderRadius = `${elements.tipBorderRadius.value}px`; tipEl.style.padding = `${elements.tipPadding.value}px`; + updateTipText(tipEl); } const messageItems = captionContainer.querySelectorAll('.message-item .item-content'); @@ -896,6 +1015,7 @@ function saveCaptionStyleToStorage() { tipBgOpacity: elements.tipBgOpacity.value, tipBorderRadius: elements.tipBorderRadius.value, tipPadding: elements.tipPadding.value, + tipTextValues: state.tipTextValues, messageFontSize: elements.messageFontSize.value, messageFontWeight: elements.messageFontWeight.value, messageColor: elements.messageColor.value, @@ -978,6 +1098,9 @@ function applySavedCaptionStyle(savedData) { if (savedData.hotIssuesBgOpacity) elements.hotIssuesBgOpacity.value = savedData.hotIssuesBgOpacity; if (savedData.hotIssuesBorderRadius) elements.hotIssuesBorderRadius.value = savedData.hotIssuesBorderRadius; if (savedData.hotIssuesPadding) elements.hotIssuesPadding.value = savedData.hotIssuesPadding; + if (savedData.tipTextValues) { + state.tipTextValues = savedData.tipTextValues; + } document.querySelectorAll('#captionPanel input[type="range"]').forEach(slider => { const valueSpan = slider.nextElementSibling; @@ -1397,6 +1520,7 @@ function bindEvents() { elements.toggleConfigBtn.addEventListener('click', () => togglePanel('configPanel')); elements.toggleCaptionBtn.addEventListener('click', () => { togglePanel('captionPanel'); + loadTipTextFields(); if (!captionStylePromptShown && hasSavedCaptionStyle()) { captionStylePromptShown = true; showCaptionStylePrompt((applySaved) => { @@ -1428,6 +1552,8 @@ function bindEvents() { loadLanguageOptions(); }); elements.languageSelect.addEventListener('change', (e) => { + state.currentLanguage = e.target.value; + updateTipTextFromState(); changeLanguage(e.target.value); }); elements.fullscreenBtn.addEventListener('click', toggleFullscreen);