mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-08 02:17:46 +08:00
修复前端调试与基础交互问题
This commit is contained in:
@@ -12,6 +12,7 @@ describe('useSiteInfo', () => {
|
||||
beforeEach(() => {
|
||||
vi.resetModules()
|
||||
apiClientMocks.get.mockReset()
|
||||
document.title = ''
|
||||
})
|
||||
|
||||
it('loads public site info', async () => {
|
||||
@@ -23,10 +24,57 @@ describe('useSiteInfo', () => {
|
||||
})
|
||||
|
||||
const { useSiteInfo } = await import('../useSiteInfo')
|
||||
const { siteName, siteSubtitle, refreshSiteInfo } = useSiteInfo()
|
||||
const { siteName, siteSubtitle, siteInfoLoaded, refreshSiteInfo } = useSiteInfo()
|
||||
await refreshSiteInfo()
|
||||
|
||||
expect(siteName.value).toBe('Custom Aether')
|
||||
expect(siteSubtitle.value).toBe('Gateway')
|
||||
expect(siteInfoLoaded.value).toBe(true)
|
||||
expect(document.title).toBe('Custom Aether')
|
||||
})
|
||||
|
||||
it('keeps default site text hidden until public site info resolves', async () => {
|
||||
let resolveRequest: (value: { data: { site_name: string; site_subtitle: string } }) => void = () => {}
|
||||
apiClientMocks.get.mockReturnValue(new Promise((resolve) => {
|
||||
resolveRequest = resolve
|
||||
}))
|
||||
|
||||
const { useSiteInfo } = await import('../useSiteInfo')
|
||||
const { siteName, siteSubtitle, siteInfoLoaded } = useSiteInfo()
|
||||
|
||||
expect(siteName.value).toBe('')
|
||||
expect(siteSubtitle.value).toBe('')
|
||||
expect(siteInfoLoaded.value).toBe(false)
|
||||
expect(document.title).toBe('')
|
||||
|
||||
resolveRequest({
|
||||
data: {
|
||||
site_name: 'Configured Aether',
|
||||
site_subtitle: 'Configured Gateway',
|
||||
},
|
||||
})
|
||||
await new Promise(resolve => setTimeout(resolve, 0))
|
||||
|
||||
expect(siteName.value).toBe('Configured Aether')
|
||||
expect(siteSubtitle.value).toBe('Configured Gateway')
|
||||
expect(siteInfoLoaded.value).toBe(true)
|
||||
expect(document.title).toBe('Configured Aether')
|
||||
})
|
||||
|
||||
it('uses upstream defaults only after public site info fails', async () => {
|
||||
apiClientMocks.get.mockRejectedValue(new Error('network unavailable'))
|
||||
|
||||
const { useSiteInfo } = await import('../useSiteInfo')
|
||||
const { siteName, siteSubtitle, siteInfoLoaded, refreshSiteInfo } = useSiteInfo()
|
||||
|
||||
expect(siteName.value).toBe('')
|
||||
expect(siteSubtitle.value).toBe('')
|
||||
|
||||
await refreshSiteInfo()
|
||||
|
||||
expect(siteName.value).toBe('Aether')
|
||||
expect(siteSubtitle.value).toBe('AI Gateway')
|
||||
expect(siteInfoLoaded.value).toBe(true)
|
||||
expect(document.title).toBe('Aether')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ref, watch } from 'vue'
|
||||
import { readonly, ref, watch } from 'vue'
|
||||
import apiClient from '@/api/client'
|
||||
|
||||
interface SiteInfo {
|
||||
@@ -6,21 +6,42 @@ interface SiteInfo {
|
||||
site_subtitle: string
|
||||
}
|
||||
|
||||
const DEFAULT_SITE_INFO: SiteInfo = {
|
||||
site_name: 'Aether',
|
||||
site_subtitle: 'AI Gateway',
|
||||
}
|
||||
|
||||
// 模块级缓存,所有组件共享同一份数据
|
||||
const siteName = ref('Aether')
|
||||
const siteSubtitle = ref('AI Gateway')
|
||||
const siteName = ref('')
|
||||
const siteSubtitle = ref('')
|
||||
const loaded = ref(false)
|
||||
let fetchPromise: Promise<void> | null = null
|
||||
|
||||
function normalizeSiteInfo(data: Partial<SiteInfo> | null | undefined): SiteInfo {
|
||||
return {
|
||||
site_name: data?.site_name?.trim() || DEFAULT_SITE_INFO.site_name,
|
||||
site_subtitle: data?.site_subtitle?.trim() || DEFAULT_SITE_INFO.site_subtitle,
|
||||
}
|
||||
}
|
||||
|
||||
function applySiteInfo(data: Partial<SiteInfo> | null | undefined): void {
|
||||
const normalized = normalizeSiteInfo(data)
|
||||
siteName.value = normalized.site_name
|
||||
siteSubtitle.value = normalized.site_subtitle
|
||||
}
|
||||
|
||||
async function fetchSiteInfo() {
|
||||
try {
|
||||
const response = await apiClient.get<SiteInfo>('/api/public/site-info')
|
||||
siteName.value = response.data.site_name
|
||||
siteSubtitle.value = response.data.site_subtitle
|
||||
loaded.value = true
|
||||
applySiteInfo(response.data)
|
||||
} catch {
|
||||
// 加载失败时保持默认值,允许后续重试
|
||||
// 加载失败时才使用 upstream 默认站点信息,避免配置加载前闪出默认品牌文案
|
||||
if (!siteName.value || !siteSubtitle.value) {
|
||||
applySiteInfo(DEFAULT_SITE_INFO)
|
||||
}
|
||||
fetchPromise = null
|
||||
} finally {
|
||||
loaded.value = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,10 +56,12 @@ export function useSiteInfo() {
|
||||
if (!loaded.value && !fetchPromise) {
|
||||
fetchPromise = fetchSiteInfo()
|
||||
}
|
||||
return { siteName, siteSubtitle, refreshSiteInfo }
|
||||
return { siteName, siteSubtitle, siteInfoLoaded: readonly(loaded), refreshSiteInfo }
|
||||
}
|
||||
|
||||
// 站点名称变化时同步更新 document.title
|
||||
watch(siteName, (name) => {
|
||||
document.title = name
|
||||
if (name) {
|
||||
document.title = name
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
Reference in New Issue
Block a user