feat(sync): full code sync from release
This commit is contained in:
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user