Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
陈曦
sub2api
Commits
3c243c47
Commit
3c243c47
authored
Apr 13, 2026
by
qingyuzhang
Committed by
陈曦
Apr 14, 2026
Browse files
fix(frontend): lazy load mobile account usage cells
parent
8b4fc76b
Changes
1
Show whitespace changes
Inline
Side-by-side
frontend/src/components/account/AccountUsageCell.vue
View file @
3c243c47
<
template
>
<div
v-if=
"showUsageWindows"
>
<div
ref=
"rootRef"
v-if=
"showUsageWindows"
>
<!-- Anthropic OAuth and Setup Token accounts: fetch real usage data -->
<template
v-if=
"
...
...
@@ -371,7 +371,7 @@
</div>
<!-- Non-OAuth/Setup-Token accounts -->
<div
v-else
>
<div
ref=
"rootRef"
v-else
>
<!-- Gemini API Key accounts: show quota info -->
<AccountQuotaInfo
v-if=
"account.platform === 'gemini'"
:account=
"account"
/>
<!-- Key/Bedrock accounts: show today stats + optional quota bars -->
...
...
@@ -439,7 +439,7 @@
</template>
<
script
setup
lang=
"ts"
>
import
{
ref
,
computed
,
onMounted
,
watch
}
from
'
vue
'
import
{
ref
,
computed
,
onMounted
,
onUnmounted
,
watch
}
from
'
vue
'
import
{
useI18n
}
from
'
vue-i18n
'
import
{
adminAPI
}
from
'
@/api/admin
'
import
type
{
Account
,
AccountUsageInfo
,
GeminiCredentials
,
WindowStats
}
from
'
@/types
'
...
...
@@ -463,11 +463,23 @@ const props = withDefaults(
)
const
{
t
}
=
useI18n
()
const
desktopViewportQuery
=
'
(min-width: 768px)
'
const
loading
=
ref
(
false
)
const
activeQueryLoading
=
ref
(
false
)
const
error
=
ref
<
string
|
null
>
(
null
)
const
usageInfo
=
ref
<
AccountUsageInfo
|
null
>
(
null
)
const
rootRef
=
ref
<
HTMLElement
|
null
>
(
null
)
const
isDesktopViewport
=
ref
(
typeof
window
===
'
undefined
'
?
true
:
window
.
matchMedia
(
desktopViewportQuery
).
matches
)
const
hasEnteredViewport
=
ref
(
false
)
const
pendingAutoLoad
=
ref
(
false
)
const
pendingAutoLoadSource
=
ref
<
'
passive
'
|
'
active
'
|
undefined
>
(
undefined
)
let
desktopViewportMediaQuery
:
MediaQueryList
|
null
=
null
let
desktopViewportListener
:
((
event
:
MediaQueryListEvent
)
=>
void
)
|
null
=
null
let
visibilityObserver
:
IntersectionObserver
|
null
=
null
// Show usage windows for OAuth and Setup Token accounts
const
showUsageWindows
=
computed
(()
=>
{
...
...
@@ -514,6 +526,10 @@ const shouldAutoLoadUsageOnMount = computed(() => {
return
shouldFetchUsage
.
value
})
const
shouldLazyLoadOnMobile
=
computed
(()
=>
{
return
shouldFetchUsage
.
value
&&
!
isDesktopViewport
.
value
})
// Antigravity quota types (用于 API 返回的数据)
interface
AntigravityUsageResult
{
utilization
:
number
...
...
@@ -941,6 +957,56 @@ const loadUsage = async (source?: 'passive' | 'active') => {
}
}
const
flushPendingAutoLoad
=
()
=>
{
if
(
!
pendingAutoLoad
.
value
)
return
const
source
=
pendingAutoLoadSource
.
value
pendingAutoLoad
.
value
=
false
pendingAutoLoadSource
.
value
=
undefined
loadUsage
(
source
).
catch
((
e
)
=>
{
console
.
error
(
'
Failed to load deferred usage:
'
,
e
)
})
}
const
requestAutoLoad
=
(
source
?:
'
passive
'
|
'
active
'
)
=>
{
if
(
!
shouldFetchUsage
.
value
)
return
if
(
shouldLazyLoadOnMobile
.
value
&&
!
hasEnteredViewport
.
value
)
{
pendingAutoLoad
.
value
=
true
pendingAutoLoadSource
.
value
=
source
return
}
loadUsage
(
source
).
catch
((
e
)
=>
{
console
.
error
(
'
Failed to auto load usage:
'
,
e
)
})
}
const
detachVisibilityObserver
=
()
=>
{
visibilityObserver
?.
disconnect
()
visibilityObserver
=
null
}
const
attachVisibilityObserver
=
()
=>
{
detachVisibilityObserver
()
if
(
!
shouldLazyLoadOnMobile
.
value
||
hasEnteredViewport
.
value
)
return
if
(
typeof
window
===
'
undefined
'
||
typeof
IntersectionObserver
===
'
undefined
'
)
{
hasEnteredViewport
.
value
=
true
flushPendingAutoLoad
()
return
}
if
(
!
rootRef
.
value
)
return
visibilityObserver
=
new
IntersectionObserver
((
entries
)
=>
{
if
(
!
entries
.
some
((
entry
)
=>
entry
.
isIntersecting
))
return
hasEnteredViewport
.
value
=
true
detachVisibilityObserver
()
flushPendingAutoLoad
()
},
{
root
:
null
,
rootMargin
:
'
200px 0px
'
,
threshold
:
0.01
})
visibilityObserver
.
observe
(
rootRef
.
value
)
}
const
loadActiveUsage
=
async
()
=>
{
activeQueryLoading
.
value
=
true
try
{
...
...
@@ -1040,18 +1106,29 @@ const formatKeyUserCost = computed(() => {
})
onMounted
(()
=>
{
if
(
typeof
window
!==
'
undefined
'
)
{
desktopViewportMediaQuery
=
window
.
matchMedia
(
desktopViewportQuery
)
isDesktopViewport
.
value
=
desktopViewportMediaQuery
.
matches
desktopViewportListener
=
(
event
:
MediaQueryListEvent
)
=>
{
isDesktopViewport
.
value
=
event
.
matches
}
if
(
typeof
desktopViewportMediaQuery
.
addEventListener
===
'
function
'
)
{
desktopViewportMediaQuery
.
addEventListener
(
'
change
'
,
desktopViewportListener
)
}
else
{
desktopViewportMediaQuery
.
addListener
(
desktopViewportListener
)
}
}
if
(
!
shouldAutoLoadUsageOnMount
.
value
)
return
const
source
=
isAnthropicOAuthOrSetupToken
.
value
?
'
passive
'
:
undefined
loadUsage
(
source
)
requestAutoLoad
(
source
)
})
watch
(
openAIUsageRefreshKey
,
(
nextKey
,
prevKey
)
=>
{
if
(
!
prevKey
||
nextKey
===
prevKey
)
return
if
(
props
.
account
.
platform
!==
'
openai
'
||
props
.
account
.
type
!==
'
oauth
'
)
return
loadUsage
().
catch
((
e
)
=>
{
console
.
error
(
'
Failed to refresh OpenAI usage:
'
,
e
)
})
requestAutoLoad
()
})
watch
(
...
...
@@ -1061,9 +1138,43 @@ watch(
if
(
!
shouldFetchUsage
.
value
)
return
const
source
=
isAnthropicOAuthOrSetupToken
.
value
?
'
passive
'
:
undefined
loadUsage
(
source
).
catch
((
e
)
=>
{
console
.
error
(
'
Failed to refresh usage after manual refresh:
'
,
e
)
})
requestAutoLoad
(
source
)
}
)
watch
(
[
rootRef
,
shouldLazyLoadOnMobile
],
()
=>
{
if
(
shouldLazyLoadOnMobile
.
value
)
{
attachVisibilityObserver
()
return
}
detachVisibilityObserver
()
},
{
immediate
:
true
,
flush
:
'
post
'
}
)
watch
(
isDesktopViewport
,
(
isDesktop
)
=>
{
if
(
isDesktop
)
{
detachVisibilityObserver
()
hasEnteredViewport
.
value
=
true
flushPendingAutoLoad
()
return
}
hasEnteredViewport
.
value
=
false
attachVisibilityObserver
()
})
onUnmounted
(()
=>
{
detachVisibilityObserver
()
if
(
desktopViewportMediaQuery
&&
desktopViewportListener
)
{
if
(
typeof
desktopViewportMediaQuery
.
removeEventListener
===
'
function
'
)
{
desktopViewportMediaQuery
.
removeEventListener
(
'
change
'
,
desktopViewportListener
)
}
else
{
desktopViewportMediaQuery
.
removeListener
(
desktopViewportListener
)
}
}
desktopViewportListener
=
null
desktopViewportMediaQuery
=
null
})
</
script
>
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment