Files
Nexus/frontend/src/composables/useServerSearchHistory.ts
T
Nexus Agent af912662de fix: combobox 清空 null 导致服务器列表加载失败
v-combobox 清空时 search 为 null 使 trim() 抛错;422 响应剥离不可序列化的 ctx 避免 500。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-11 17:52:49 +08:00

91 lines
2.5 KiB
TypeScript

import { ref, type Ref } from 'vue'
import { http } from '@/api'
const LEGACY_HISTORY = 'nexus_servers_search_history'
const LEGACY_LAST = 'nexus_servers_search_last'
export interface ServerSearchHistoryResponse {
history: string[]
last: string | null
}
function readLegacyLocal(): { history: string[]; last: string } | null {
try {
const raw = localStorage.getItem(LEGACY_HISTORY)
const last = localStorage.getItem(LEGACY_LAST) || ''
if (!raw) return last.trim() ? { history: [], last } : null
const parsed = JSON.parse(raw) as unknown
if (!Array.isArray(parsed)) return last.trim() ? { history: [], last } : null
const history = parsed.filter((x): x is string => typeof x === 'string' && x.trim().length > 0)
if (!history.length && !last.trim()) return null
return { history, last }
} catch {
return null
}
}
function clearLegacyLocal() {
try {
localStorage.removeItem(LEGACY_HISTORY)
localStorage.removeItem(LEGACY_LAST)
} catch {
// ignore
}
}
/** Servers page search history — persisted per admin in MySQL. */
export function useServerSearchHistory() {
const items = ref<string[]>([])
const bootstrapped = ref(false)
async function load(): Promise<string> {
const res = await http.get<ServerSearchHistoryResponse>('/servers/search-history')
items.value = res.history || []
return res.last || ''
}
async function migrateLegacyIfEmpty(): Promise<boolean> {
const legacy = readLegacyLocal()
if (!legacy) return false
if (items.value.length > 0) {
clearLegacyLocal()
return false
}
await http.put<ServerSearchHistoryResponse>('/servers/search-history', {
history: legacy.history,
last: legacy.last || legacy.history[0] || '',
})
clearLegacyLocal()
return true
}
async function bootstrap(searchRef: Ref<string>) {
if (bootstrapped.value) return
try {
let last = await load()
if (!items.value.length && (await migrateLegacyIfEmpty())) {
last = await load()
}
if (last) searchRef.value = last
} catch {
// offline / auth — keep empty search
} finally {
bootstrapped.value = true
}
}
async function record(query: string | null | undefined) {
const normalized = String(query ?? '').trim()
try {
const res = await http.put<ServerSearchHistoryResponse>('/servers/search-history', {
query: normalized,
})
items.value = res.history || []
} catch {
// non-blocking
}
}
return { items, bootstrapped, load, bootstrap, record }
}