feat(sync): full code sync from release

This commit is contained in:
yangjianbo
2026-02-28 15:01:20 +08:00
parent bfc7b339f7
commit bb664d9bbf
338 changed files with 54513 additions and 2011 deletions

View File

@@ -184,7 +184,11 @@
</button>
</template>
<template #cell-today_stats="{ row }">
<AccountTodayStatsCell :account="row" />
<AccountTodayStatsCell
:stats="todayStatsByAccountId[String(row.id)] ?? null"
:loading="todayStatsLoading"
:error="todayStatsError"
/>
</template>
<template #cell-groups="{ row }">
<AccountGroupsCell :groups="row.groups" :max-display="4" />
@@ -273,7 +277,7 @@
</template>
<script setup lang="ts">
import { ref, reactive, computed, onMounted, onUnmounted, toRaw } from 'vue'
import { ref, reactive, computed, onMounted, onUnmounted, toRaw, watch } from 'vue'
import { useIntervalFn } from '@vueuse/core'
import { useI18n } from 'vue-i18n'
import { useAppStore } from '@/stores/app'
@@ -303,7 +307,7 @@ import PlatformTypeBadge from '@/components/common/PlatformTypeBadge.vue'
import Icon from '@/components/icons/Icon.vue'
import ErrorPassthroughRulesModal from '@/components/admin/ErrorPassthroughRulesModal.vue'
import { formatDateTime, formatRelativeTime } from '@/utils/format'
import type { Account, AccountPlatform, Proxy, AdminGroup } from '@/types'
import type { Account, AccountPlatform, Proxy, AdminGroup, WindowStats } from '@/types'
const { t } = useI18n()
const appStore = useAppStore()
@@ -366,6 +370,59 @@ const autoRefreshFetching = ref(false)
const AUTO_REFRESH_SILENT_WINDOW_MS = 15000
const autoRefreshSilentUntil = ref(0)
const hasPendingListSync = ref(false)
const todayStatsByAccountId = ref<Record<string, WindowStats>>({})
const todayStatsLoading = ref(false)
const todayStatsError = ref<string | null>(null)
const todayStatsReqSeq = ref(0)
const pendingTodayStatsRefresh = ref(false)
const buildDefaultTodayStats = (): WindowStats => ({
requests: 0,
tokens: 0,
cost: 0,
standard_cost: 0,
user_cost: 0
})
const refreshTodayStatsBatch = async () => {
if (hiddenColumns.has('today_stats')) {
todayStatsLoading.value = false
todayStatsError.value = null
return
}
const accountIDs = accounts.value.map(account => account.id)
const reqSeq = ++todayStatsReqSeq.value
if (accountIDs.length === 0) {
todayStatsByAccountId.value = {}
todayStatsError.value = null
todayStatsLoading.value = false
return
}
todayStatsLoading.value = true
todayStatsError.value = null
try {
const result = await adminAPI.accounts.getBatchTodayStats(accountIDs)
if (reqSeq !== todayStatsReqSeq.value) return
const serverStats = result.stats ?? {}
const nextStats: Record<string, WindowStats> = {}
for (const accountID of accountIDs) {
const key = String(accountID)
nextStats[key] = serverStats[key] ?? buildDefaultTodayStats()
}
todayStatsByAccountId.value = nextStats
} catch (error) {
if (reqSeq !== todayStatsReqSeq.value) return
todayStatsError.value = 'Failed'
console.error('Failed to load account today stats:', error)
} finally {
if (reqSeq === todayStatsReqSeq.value) {
todayStatsLoading.value = false
}
}
}
const autoRefreshIntervalLabel = (sec: number) => {
if (sec === 5) return t('admin.accounts.refreshInterval5s')
@@ -453,12 +510,18 @@ const setAutoRefreshInterval = (seconds: (typeof autoRefreshIntervals)[number])
}
const toggleColumn = (key: string) => {
const wasHidden = hiddenColumns.has(key)
if (hiddenColumns.has(key)) {
hiddenColumns.delete(key)
} else {
hiddenColumns.add(key)
}
saveColumnsToStorage()
if (key === 'today_stats' && wasHidden) {
refreshTodayStatsBatch().catch((error) => {
console.error('Failed to load account today stats after showing column:', error)
})
}
}
const isColumnVisible = (key: string) => !hiddenColumns.has(key)
@@ -485,33 +548,49 @@ const resetAutoRefreshCache = () => {
const load = async () => {
hasPendingListSync.value = false
resetAutoRefreshCache()
pendingTodayStatsRefresh.value = false
await baseLoad()
await refreshTodayStatsBatch()
}
const reload = async () => {
hasPendingListSync.value = false
resetAutoRefreshCache()
pendingTodayStatsRefresh.value = false
await baseReload()
await refreshTodayStatsBatch()
}
const debouncedReload = () => {
hasPendingListSync.value = false
resetAutoRefreshCache()
pendingTodayStatsRefresh.value = true
baseDebouncedReload()
}
const handlePageChange = (page: number) => {
hasPendingListSync.value = false
resetAutoRefreshCache()
pendingTodayStatsRefresh.value = true
baseHandlePageChange(page)
}
const handlePageSizeChange = (size: number) => {
hasPendingListSync.value = false
resetAutoRefreshCache()
pendingTodayStatsRefresh.value = true
baseHandlePageSizeChange(size)
}
watch(loading, (isLoading, wasLoading) => {
if (wasLoading && !isLoading && pendingTodayStatsRefresh.value) {
pendingTodayStatsRefresh.value = false
refreshTodayStatsBatch().catch((error) => {
console.error('Failed to refresh account today stats after table load:', error)
})
}
})
const isAnyModalOpen = computed(() => {
return (
showCreate.value ||
@@ -609,14 +688,14 @@ const refreshAccountsIncrementally = async () => {
if (result.etag) {
autoRefreshETag.value = result.etag
}
if (result.notModified || !result.data) {
return
if (!result.notModified && result.data) {
pagination.total = result.data.total || 0
pagination.pages = result.data.pages || 0
mergeAccountsIncrementally(result.data.items || [])
hasPendingListSync.value = false
}
pagination.total = result.data.total || 0
pagination.pages = result.data.pages || 0
mergeAccountsIncrementally(result.data.items || [])
hasPendingListSync.value = false
await refreshTodayStatsBatch()
} catch (error) {
console.error('Auto refresh failed:', error)
} finally {