From bb83ac7d9e2707917ec00ffaa2331aabf23588cf Mon Sep 17 00:00:00 2001 From: memeflyfly <55984574+1624318455@users.noreply.github.com> Date: Thu, 14 May 2026 15:47:17 +0800 Subject: [PATCH] feat(settings): add debounce to NInputNumber in Memory/Agent/Session settings (#718) --- .../hermes/settings/AgentSettings.vue | 35 ++++++++++++++----- .../hermes/settings/MemorySettings.vue | 33 +++++++++++++---- .../hermes/settings/SessionSettings.vue | 33 +++++++++++++---- packages/client/src/stores/hermes/settings.ts | 31 +++++++++++++++- 4 files changed, 109 insertions(+), 23 deletions(-) diff --git a/packages/client/src/components/hermes/settings/AgentSettings.vue b/packages/client/src/components/hermes/settings/AgentSettings.vue index 8e2bc425..535f94c3 100644 --- a/packages/client/src/components/hermes/settings/AgentSettings.vue +++ b/packages/client/src/components/hermes/settings/AgentSettings.vue @@ -8,13 +8,32 @@ const settingsStore = useSettingsStore() const message = useMessage() const { t } = useI18n() -async function save(values: Record) { - try { - await settingsStore.saveSection('agent', values) +// 防抖保存:每个字段独立定时器,300ms 内只发最后一次 HTTP 请求 +const debounceTimers: Record> = {} + +function save(values: Record) { + // NSelect 等一次性操作,直接保存,不需要防抖 + settingsStore.updateLocal('agent', values) + settingsStore.saveSection('agent', values).then(() => { message.success(t('settings.saved')) - } catch (err: any) { + }).catch(() => { message.error(t('settings.saveFailed')) - } + }) +} + +function debouncedSave(key: string, value: any) { + // 先立即更新本地 store(UI 即时响应) + settingsStore.updateLocal('agent', { [key]: value }) + // 再防抖发 HTTP 保存 + if (debounceTimers[key]) clearTimeout(debounceTimers[key]) + debounceTimers[key] = setTimeout(async () => { + try { + await settingsStore.saveSection('agent', { [key]: value }) + message.success(t('settings.saved')) + } catch (err: any) { + message.error(t('settings.saveFailed')) + } + }, 300) } @@ -25,7 +44,7 @@ async function save(values: Record) { :value="settingsStore.agent.max_turns" :min="1" :max="200" :step="5" size="small" class="input-sm" - @update:value="v => v != null && save({ max_turns: v })" + @update:value="v => v != null && debouncedSave('max_turns', v)" /> @@ -33,7 +52,7 @@ async function save(values: Record) { :value="settingsStore.agent.gateway_timeout" :min="60" :max="7200" :step="60" size="small" class="input-sm" - @update:value="v => v != null && save({ gateway_timeout: v })" + @update:value="v => v != null && debouncedSave('gateway_timeout', v)" /> @@ -41,7 +60,7 @@ async function save(values: Record) { :value="settingsStore.agent.restart_drain_timeout" :min="10" :max="300" :step="10" size="small" class="input-sm" - @update:value="v => v != null && save({ restart_drain_timeout: v })" + @update:value="v => v != null && debouncedSave('restart_drain_timeout', v)" /> diff --git a/packages/client/src/components/hermes/settings/MemorySettings.vue b/packages/client/src/components/hermes/settings/MemorySettings.vue index ebea88c7..3167f56b 100644 --- a/packages/client/src/components/hermes/settings/MemorySettings.vue +++ b/packages/client/src/components/hermes/settings/MemorySettings.vue @@ -8,13 +8,32 @@ const settingsStore = useSettingsStore() const message = useMessage() const { t } = useI18n() -async function save(values: Record) { - try { - await settingsStore.saveSection('memory', values) +// 防抖保存:每个字段独立定时器,300ms 内只发最后一次 HTTP 请求 +const debounceTimers: Record> = {} + +function save(values: Record) { + // Switch 等一次性操作,直接保存,不需要防抖 + settingsStore.updateLocal('memory', values) + settingsStore.saveSection('memory', values).then(() => { message.success(t('settings.saved')) - } catch (err: any) { + }).catch(() => { message.error(t('settings.saveFailed')) - } + }) +} + +function debouncedSave(key: string, value: any) { + // 先立即更新本地 store(UI 即时响应) + settingsStore.updateLocal('memory', { [key]: value }) + // 再防抖发 HTTP 保存 + if (debounceTimers[key]) clearTimeout(debounceTimers[key]) + debounceTimers[key] = setTimeout(async () => { + try { + await settingsStore.saveSection('memory', { [key]: value }) + message.success(t('settings.saved')) + } catch (err: any) { + message.error(t('settings.saveFailed')) + } + }, 300) } @@ -31,7 +50,7 @@ async function save(values: Record) { :value="settingsStore.memory.memory_char_limit" :min="100" :max="10000" :step="100" size="small" class="input-sm" - @update:value="v => v != null && save({ memory_char_limit: v })" + @update:value="v => v != null && debouncedSave('memory_char_limit', v)" /> @@ -39,7 +58,7 @@ async function save(values: Record) { :value="settingsStore.memory.user_char_limit" :min="100" :max="10000" :step="100" size="small" class="input-sm" - @update:value="v => v != null && save({ user_char_limit: v })" + @update:value="v => v != null && debouncedSave('user_char_limit', v)" /> diff --git a/packages/client/src/components/hermes/settings/SessionSettings.vue b/packages/client/src/components/hermes/settings/SessionSettings.vue index 84df66eb..04a77168 100644 --- a/packages/client/src/components/hermes/settings/SessionSettings.vue +++ b/packages/client/src/components/hermes/settings/SessionSettings.vue @@ -10,13 +10,32 @@ const sessionBrowserPrefsStore = useSessionBrowserPrefsStore(); const message = useMessage(); const { t } = useI18n(); -async function save(values: Record) { - try { - await settingsStore.saveSection("session_reset", values); +// 防抖保存:每个字段独立定时器,300ms 内只发最后一次 HTTP 请求 +const debounceTimers: Record> = {}; + +function save(values: Record) { + // NSelect/NSwitch 等一次性操作,直接保存,不需要防抖 + settingsStore.updateLocal('session_reset', values) + settingsStore.saveSection('session_reset', values).then(() => { message.success(t("settings.saved")); - } catch (err: any) { + }).catch(() => { message.error(t("settings.saveFailed")); - } + }); +} + +function debouncedSave(key: string, value: any) { + // 先立即更新本地 store(UI 即时响应) + settingsStore.updateLocal('session_reset', { [key]: value }); + // 再防抖发 HTTP 保存 + if (debounceTimers[key]) clearTimeout(debounceTimers[key]) + debounceTimers[key] = setTimeout(async () => { + try { + await settingsStore.saveSection('session_reset', { [key]: value }); + message.success(t("settings.saved")); + } catch (err: any) { + message.error(t("settings.saveFailed")); + } + }, 300); } async function toggleRequireAuth(value: boolean) { @@ -64,7 +83,7 @@ async function toggleRequireAuth(value: boolean) { :step="30" size="small" class="input-sm" - @update:value="(v) => v != null && save({ idle_minutes: v })" + @update:value="(v) => v != null && debouncedSave('idle_minutes', v)" /> { } } + function updateLocal(section: string, values: Record) { + switch (section) { + case 'display': display.value = { ...display.value, ...values }; break + case 'agent': agent.value = { ...agent.value, ...values }; break + case 'memory': memory.value = { ...memory.value, ...values }; break + case 'session_reset': sessionReset.value = { ...sessionReset.value, ...values }; break + case 'privacy': privacy.value = { ...privacy.value, ...values }; break + case 'approvals': approvals.value = { ...approvals.value, ...values }; break + case 'telegram': telegram.value = { ...telegram.value, ...values }; break + case 'discord': discord.value = { ...discord.value, ...values }; break + case 'slack': slack.value = { ...slack.value, ...values }; break + case 'whatsapp': whatsapp.value = { ...whatsapp.value, ...values }; break + case 'matrix': matrix.value = { ...matrix.value, ...values }; break + case 'wecom': wecom.value = { ...wecom.value, ...values }; break + case 'feishu': feishu.value = { ...feishu.value, ...values }; break + case 'dingtalk': dingtalk.value = { ...dingtalk.value, ...values }; break + case 'weixin': weixin.value = { ...weixin.value, ...values }; break + case 'platforms': { + for (const [key, val] of Object.entries(values)) { + platforms.value = { + ...platforms.value, + [key]: { ...(platforms.value[key] || {}), ...(val as Record) }, + } + } + break + } + } + } + async function saveSection(section: string, values: Record) { saving.value = true try { @@ -91,6 +120,6 @@ export const useSettingsStore = defineStore('settings', () => { loading, saving, display, agent, memory, sessionReset, privacy, approvals, telegram, discord, slack, whatsapp, matrix, wecom, feishu, dingtalk, weixin, platforms, - fetchSettings, saveSection, + fetchSettings, saveSection, updateLocal, } })