mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 17:30:23 +08:00
perf: 并行化 admin 聚合路由并完善前端缓存预取
- gateway: usage detail / provider summary / pool overview / users list 改为 tokio join 并行拉取依赖数据 - usage: interval timeline 支持自动刷新并按查询区间动态展示,取消服务端 120 分钟过滤并在 ScatterChart 统一封顶 - frontend: 新增管理端导航预取工具及 SidebarNav/MainLayout 触发,admin 读接口统一走 cachedRequest 的短期缓存 - dashboard: request detail 支持短 TTL 缓存并在 UsageRecordsTable mousedown 时预取 - data: migrate 测试在 wait_for_postgres 失败时清理子进程,避免遗留
This commit is contained in:
@@ -66,14 +66,18 @@ const props = withDefaults(defineProps<{
|
||||
title: string
|
||||
isAdmin: boolean
|
||||
hours?: number
|
||||
refreshIntervalMs?: number
|
||||
}>(), {
|
||||
hours: 24 // 默认当天
|
||||
hours: 24, // 默认当天
|
||||
refreshIntervalMs: 30000
|
||||
})
|
||||
|
||||
const loading = ref(false)
|
||||
const timelineData = ref<IntervalTimelineResponse | null>(null)
|
||||
const primaryColor = ref('201, 100, 66') // 默认主题色
|
||||
let loadRequestId = 0
|
||||
let refreshTimer: ReturnType<typeof setTimeout> | null = null
|
||||
const isPageVisible = ref(typeof document === 'undefined' ? true : !document.hidden)
|
||||
|
||||
const ADMIN_TIMELINE_LIMIT = 1500
|
||||
const USER_TIMELINE_LIMIT = 1200
|
||||
@@ -90,7 +94,11 @@ function getPrimaryColor(): string {
|
||||
|
||||
onMounted(() => {
|
||||
primaryColor.value = getPrimaryColor()
|
||||
if (typeof document !== 'undefined') {
|
||||
document.addEventListener('visibilitychange', handleVisibilityChange)
|
||||
}
|
||||
void loadData()
|
||||
scheduleNextRefresh()
|
||||
})
|
||||
|
||||
// 预定义的颜色列表(用于区分不同用户/模型)
|
||||
@@ -315,11 +323,45 @@ async function loadData() {
|
||||
}
|
||||
}
|
||||
|
||||
function stopRefreshTimer() {
|
||||
if (refreshTimer) {
|
||||
clearTimeout(refreshTimer)
|
||||
refreshTimer = null
|
||||
}
|
||||
}
|
||||
|
||||
function scheduleNextRefresh() {
|
||||
if (refreshTimer) return
|
||||
if (!isPageVisible.value) return
|
||||
if (!props.refreshIntervalMs || props.refreshIntervalMs <= 0) return
|
||||
refreshTimer = setTimeout(async () => {
|
||||
refreshTimer = null
|
||||
await loadData()
|
||||
scheduleNextRefresh()
|
||||
}, props.refreshIntervalMs)
|
||||
}
|
||||
|
||||
function handleVisibilityChange() {
|
||||
isPageVisible.value = !document.hidden
|
||||
if (!isPageVisible.value) {
|
||||
stopRefreshTimer()
|
||||
return
|
||||
}
|
||||
void loadData()
|
||||
scheduleNextRefresh()
|
||||
}
|
||||
|
||||
watch([() => props.hours, () => props.isAdmin], () => {
|
||||
void loadData()
|
||||
stopRefreshTimer()
|
||||
scheduleNextRefresh()
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
loadRequestId++
|
||||
if (typeof document !== 'undefined') {
|
||||
document.removeEventListener('visibilitychange', handleVisibilityChange)
|
||||
}
|
||||
stopRefreshTimer()
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -1664,7 +1664,10 @@ async function loadDetail(id: string, silent = false) {
|
||||
}
|
||||
error.value = null
|
||||
try {
|
||||
const response = await dashboardApi.getRequestDetail(id, { includeBodies: false })
|
||||
const response = await dashboardApi.getRequestDetail(id, {
|
||||
includeBodies: false,
|
||||
cacheTtlMs: silent ? 0 : 5_000
|
||||
})
|
||||
if (requestId !== loadDetailRequestId) return
|
||||
|
||||
const previousDetail = detail.value
|
||||
|
||||
@@ -341,7 +341,7 @@
|
||||
v-else
|
||||
:key="record.id"
|
||||
:class="isAdmin ? 'cursor-pointer border-b border-border/40 hover:bg-muted/30 transition-colors h-[72px]' : 'border-b border-border/40 hover:bg-muted/30 transition-colors h-[72px]'"
|
||||
@mousedown="handleMouseDown"
|
||||
@mousedown="handleRowMouseDown($event, record.id)"
|
||||
@click="handleRowClick($event, record.id)"
|
||||
>
|
||||
<TableCell class="text-xs py-4 w-[70px]">
|
||||
@@ -730,6 +730,7 @@ const emit = defineEmits<{
|
||||
'update:autoRefresh': [value: boolean]
|
||||
'refresh': []
|
||||
'showDetail': [id: string]
|
||||
'prefetchDetail': [id: string]
|
||||
}>()
|
||||
|
||||
// 静态常量(放在 defineProps/defineEmits 之后)
|
||||
@@ -776,6 +777,13 @@ watch(localSearch, (value) => {
|
||||
// 使用复用的行点击逻辑
|
||||
const { handleMouseDown, shouldTriggerRowClick } = useRowClick()
|
||||
|
||||
function handleRowMouseDown(event: MouseEvent, id: string) {
|
||||
handleMouseDown(event)
|
||||
if (!props.isAdmin) return
|
||||
if (event.button !== 0) return
|
||||
emit('prefetchDetail', id)
|
||||
}
|
||||
|
||||
// 处理行点击,排除文本选择操作
|
||||
function handleRowClick(event: MouseEvent, id: string) {
|
||||
if (!props.isAdmin) return
|
||||
|
||||
Reference in New Issue
Block a user