mirror of
https://github.com/EKKOLearnAI/hermes-web-ui.git
synced 2026-05-27 06:20:15 +00:00
feat(settings): add debounce to NInputNumber in Memory/Agent/Session settings (#718)
This commit is contained in:
@@ -8,13 +8,32 @@ const settingsStore = useSettingsStore()
|
||||
const message = useMessage()
|
||||
const { t } = useI18n()
|
||||
|
||||
async function save(values: Record<string, any>) {
|
||||
try {
|
||||
await settingsStore.saveSection('agent', values)
|
||||
// 防抖保存:每个字段独立定时器,300ms 内只发最后一次 HTTP 请求
|
||||
const debounceTimers: Record<string, ReturnType<typeof setTimeout>> = {}
|
||||
|
||||
function save(values: Record<string, any>) {
|
||||
// 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)
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -25,7 +44,7 @@ async function save(values: Record<string, any>) {
|
||||
: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)"
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingRow :label="t('settings.agent.gatewayTimeout')" :hint="t('settings.agent.gatewayTimeoutHint')">
|
||||
@@ -33,7 +52,7 @@ async function save(values: Record<string, any>) {
|
||||
: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)"
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingRow :label="t('settings.agent.restartDrainTimeout')" :hint="t('settings.agent.restartDrainTimeoutHint')">
|
||||
@@ -41,7 +60,7 @@ async function save(values: Record<string, any>) {
|
||||
: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)"
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingRow :label="t('settings.agent.toolEnforcement')" :hint="t('settings.agent.toolEnforcementHint')">
|
||||
|
||||
@@ -8,13 +8,32 @@ const settingsStore = useSettingsStore()
|
||||
const message = useMessage()
|
||||
const { t } = useI18n()
|
||||
|
||||
async function save(values: Record<string, any>) {
|
||||
try {
|
||||
await settingsStore.saveSection('memory', values)
|
||||
// 防抖保存:每个字段独立定时器,300ms 内只发最后一次 HTTP 请求
|
||||
const debounceTimers: Record<string, ReturnType<typeof setTimeout>> = {}
|
||||
|
||||
function save(values: Record<string, any>) {
|
||||
// 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)
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -31,7 +50,7 @@ async function save(values: Record<string, any>) {
|
||||
: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)"
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingRow :label="t('settings.memory.userCharLimit')" :hint="t('settings.memory.userCharLimitHint')">
|
||||
@@ -39,7 +58,7 @@ async function save(values: Record<string, any>) {
|
||||
: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)"
|
||||
/>
|
||||
</SettingRow>
|
||||
</section>
|
||||
|
||||
@@ -10,13 +10,32 @@ const sessionBrowserPrefsStore = useSessionBrowserPrefsStore();
|
||||
const message = useMessage();
|
||||
const { t } = useI18n();
|
||||
|
||||
async function save(values: Record<string, any>) {
|
||||
try {
|
||||
await settingsStore.saveSection("session_reset", values);
|
||||
// 防抖保存:每个字段独立定时器,300ms 内只发最后一次 HTTP 请求
|
||||
const debounceTimers: Record<string, ReturnType<typeof setTimeout>> = {};
|
||||
|
||||
function save(values: Record<string, any>) {
|
||||
// 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)"
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingRow
|
||||
@@ -78,7 +97,7 @@ async function toggleRequireAuth(value: boolean) {
|
||||
:step="1"
|
||||
size="small"
|
||||
class="input-sm"
|
||||
@update:value="(v) => v != null && save({ at_hour: v })"
|
||||
@update:value="(v) => v != null && debouncedSave('at_hour', v)"
|
||||
/>
|
||||
</SettingRow>
|
||||
<SettingRow
|
||||
|
||||
@@ -51,6 +51,35 @@ export const useSettingsStore = defineStore('settings', () => {
|
||||
}
|
||||
}
|
||||
|
||||
function updateLocal(section: string, values: Record<string, any>) {
|
||||
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<string, any>) },
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function saveSection(section: string, values: Record<string, any>) {
|
||||
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,
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user