fix(admin): empty web-search config on missing setting and reset dialog scroll

Return a disabled empty web-search-emulation config when the setting key is
absent, and reset BaseDialog body scroll on open for long modals.
This commit is contained in:
IanShaw027
2026-08-08 01:07:27 +08:00
parent 68faeac837
commit 2526a04226
3 changed files with 52 additions and 1 deletions
@@ -3,6 +3,7 @@ package service
import (
"context"
"encoding/json"
"errors"
"fmt"
"log/slog"
"sync/atomic"
@@ -106,6 +107,15 @@ func (s *SettingService) loadWebSearchConfigFromDB() (*WebSearchEmulationConfig,
raw, err := s.settingRepo.GetValue(dbCtx, SettingKeyWebSearchEmulationConfig)
if err != nil {
// Missing key is the normal first-boot state: return empty disabled config.
if errors.Is(err, ErrSettingNotFound) {
cfg := &WebSearchEmulationConfig{}
webSearchEmulationCache.Store(&cachedWebSearchEmulationConfig{
config: cfg,
expiresAt: time.Now().Add(webSearchEmulationCacheTTL).UnixNano(),
})
return cfg, nil
}
webSearchEmulationCache.Store(&cachedWebSearchEmulationConfig{
config: &WebSearchEmulationConfig{},
expiresAt: time.Now().Add(webSearchEmulationErrorTTL).UnixNano(),
@@ -28,7 +28,7 @@
</div>
<!-- Body -->
<div class="modal-body">
<div ref="modalBodyRef" class="modal-body">
<slot></slot>
</div>
@@ -52,6 +52,7 @@ const dialogId = `modal-title-${++dialogIdCounter}`
// 焦点管理
const dialogRef = ref<HTMLElement | null>(null)
const modalBodyRef = ref<HTMLElement | null>(null)
let previousActiveElement: HTMLElement | null = null
type DialogWidth = 'narrow' | 'normal' | 'wide' | 'extra-wide' | 'full'
@@ -123,6 +124,9 @@ watch(
// 等待DOM更新后设置焦点到对话框
await nextTick()
if (modalBodyRef.value) {
modalBodyRef.value.scrollTop = 0
}
if (dialogRef.value) {
const firstFocusable = dialogRef.value.querySelector<HTMLElement>(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
@@ -0,0 +1,37 @@
import { afterEach, describe, expect, it, vi } from 'vitest'
import { mount } from '@vue/test-utils'
import { nextTick } from 'vue'
import BaseDialog from '../BaseDialog.vue'
vi.mock('vue-i18n', () => ({
useI18n: () => ({ t: (key: string) => key })
}))
describe('BaseDialog', () => {
afterEach(() => {
document.body.innerHTML = ''
document.body.classList.remove('modal-open')
})
it('resets body scroll position when reopened', async () => {
const wrapper = mount(BaseDialog, {
attachTo: document.body,
props: { show: false, title: 'Details' },
slots: { default: '<div style="height: 2000px">content</div>' },
global: { stubs: { Icon: true } }
})
await wrapper.setProps({ show: true })
await nextTick()
const body = document.body.querySelector<HTMLElement>('.modal-body')
expect(body).not.toBeNull()
body!.scrollTop = 480
await wrapper.setProps({ show: false })
await wrapper.setProps({ show: true })
await nextTick()
expect(document.body.querySelector<HTMLElement>('.modal-body')?.scrollTop).toBe(0)
wrapper.unmount()
})
})