feat(webui): add subscription tier and quota visualization

Backend:
This commit is contained in:
Wha1eChai
2026-01-10 06:04:51 +08:00
parent 369a66e8cf
commit ee6d222e4d
11 changed files with 277 additions and 18 deletions

View File

@@ -50,6 +50,8 @@
<th class="pl-6 py-3 text-left text-[10px] font-bold text-gray-500 uppercase tracking-wider w-16" x-text="$store.global.t('enabled')">Enabled</th>
<th class="py-3 text-left text-[10px] font-bold text-gray-500 uppercase tracking-wider flex-1 min-w-[200px]" x-text="$store.global.t('accountEmail')">Account (Email)</th>
<th class="py-3 text-left text-[10px] font-bold text-gray-500 uppercase tracking-wider w-20" x-text="$store.global.t('source')">Source</th>
<th class="py-3 text-left text-[10px] font-bold text-gray-500 uppercase tracking-wider w-16">Tier</th>
<th class="py-3 text-left text-[10px] font-bold text-gray-500 uppercase tracking-wider w-32">Quota</th>
<th class="py-3 text-left text-[10px] font-bold text-gray-500 uppercase tracking-wider w-24" x-text="$store.global.t('health')">Health</th>
<th class="py-3 pr-6 text-right text-[10px] font-bold text-gray-500 uppercase tracking-wider w-32" x-text="$store.global.t('operations')">Operations</th>
</tr>
@@ -112,6 +114,38 @@
x-text="acc.source || 'oauth'">
</span>
</td>
<td class="py-4">
<span class="px-2 py-1 text-[10px] font-mono font-bold uppercase rounded"
:class="{
'bg-yellow-500/10 text-yellow-400 border border-yellow-500/30': acc.subscription?.tier === 'ultra',
'bg-blue-500/10 text-blue-400 border border-blue-500/30': acc.subscription?.tier === 'pro',
'bg-gray-500/10 text-gray-400 border border-gray-500/30': !acc.subscription || acc.subscription.tier === 'free' || acc.subscription.tier === 'unknown'
}"
x-text="(acc.subscription?.tier || 'free').toUpperCase()">
</span>
</td>
<td class="py-4">
<div x-data="{ quota: getMainModelQuota(acc) }">
<template x-if="quota.percent !== null">
<div class="flex items-center gap-2">
<div class="w-16 bg-gray-700 rounded-full h-2 overflow-hidden">
<div class="h-full rounded-full transition-all"
:class="{
'bg-neon-green': quota.percent > 50,
'bg-yellow-500': quota.percent > 20 && quota.percent <= 50,
'bg-red-500': quota.percent <= 20
}"
:style="`width: ${quota.percent}%`">
</div>
</div>
<span class="text-xs font-mono text-gray-400" x-text="quota.percent + '%'"></span>
</div>
</template>
<template x-if="quota.percent === null">
<span class="text-xs text-gray-600">-</span>
</template>
</div>
</td>
<td class="py-4">
<div class="flex items-center gap-2">
<div class="w-2 h-2 rounded-full flex-shrink-0"

View File

@@ -50,6 +50,24 @@
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
</svg>
</div>
<!-- Subscription Tier Distribution -->
<div class="flex items-center gap-2 mt-2 text-[10px] font-mono" x-show="stats.subscription">
<template x-if="stats.subscription?.ultra > 0">
<span class="px-1.5 py-0.5 rounded bg-yellow-500/10 text-yellow-400 border border-yellow-500/30">
<span x-text="stats.subscription.ultra"></span> Ultra
</span>
</template>
<template x-if="stats.subscription?.pro > 0">
<span class="px-1.5 py-0.5 rounded bg-blue-500/10 text-blue-400 border border-blue-500/30">
<span x-text="stats.subscription.pro"></span> Pro
</span>
</template>
<template x-if="stats.subscription?.free > 0">
<span class="px-1.5 py-0.5 rounded bg-gray-500/10 text-gray-400 border border-gray-500/30">
<span x-text="stats.subscription.free"></span> Free
</span>
</template>
</div>
</div>
<div
@@ -377,7 +395,7 @@
<!-- Chart -->
<div class="h-48 w-full relative">
<canvas id="usageTrendChart"></canvas>
<!-- Overall Loading State -->
<div x-show="!stats.hasTrendData"
class="absolute inset-0 flex items-center justify-center bg-space-900/50 backdrop-blur-sm z-10"
@@ -395,7 +413,7 @@
<div class="flex flex-col items-center gap-4 animate-fade-in">
<div class="w-12 h-12 rounded-full bg-space-850 flex items-center justify-center text-gray-600 border border-space-border/50">
<svg class="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
</svg>
</div>