mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-09 20:50:20 +08:00
style(frontend): refine badges and cycle stats
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
import { afterEach, describe, expect, it } from 'vitest'
|
||||
import { createApp, h, type App } from 'vue'
|
||||
|
||||
import Badge from '../badge.vue'
|
||||
|
||||
const mountedApps: Array<{ app: App, root: HTMLElement }> = []
|
||||
|
||||
afterEach(() => {
|
||||
for (const { app, root } of mountedApps.splice(0)) {
|
||||
app.unmount()
|
||||
root.remove()
|
||||
}
|
||||
})
|
||||
|
||||
describe('Badge', () => {
|
||||
it('renders the transparent outline variant without the card background', () => {
|
||||
const root = document.createElement('div')
|
||||
document.body.appendChild(root)
|
||||
const app = createApp({
|
||||
render: () => h(Badge, { variant: 'outline-transparent' }, () => 'Fast'),
|
||||
})
|
||||
|
||||
app.mount(root)
|
||||
mountedApps.push({ app, root })
|
||||
|
||||
const badge = root.firstElementChild
|
||||
expect(badge?.classList.contains('border-border')).toBe(true)
|
||||
expect(badge?.classList.contains('bg-transparent')).toBe(true)
|
||||
expect(badge?.classList.contains('bg-card/50')).toBe(false)
|
||||
})
|
||||
})
|
||||
@@ -20,6 +20,7 @@ const badgeVariants = cva(
|
||||
destructive:
|
||||
'border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80',
|
||||
outline: 'text-foreground border-border bg-card/50',
|
||||
'outline-transparent': 'text-foreground border-border bg-transparent',
|
||||
success:
|
||||
'border-transparent bg-primary text-primary-foreground hover:bg-primary/80',
|
||||
warning:
|
||||
@@ -35,7 +36,7 @@ const badgeVariants = cva(
|
||||
)
|
||||
|
||||
interface Props {
|
||||
variant?: 'default' | 'secondary' | 'destructive' | 'outline' | 'success' | 'warning' | 'dark'
|
||||
variant?: 'default' | 'secondary' | 'destructive' | 'outline' | 'outline-transparent' | 'success' | 'warning' | 'dark'
|
||||
class?: string
|
||||
}
|
||||
|
||||
|
||||
@@ -7,15 +7,13 @@
|
||||
<div
|
||||
v-for="row in cycleMetricRows"
|
||||
:key="`${row.key}-${variant}-cycle-row`"
|
||||
class="flex items-center justify-between gap-2"
|
||||
class="truncate"
|
||||
:title="`${row.label} ${row.valueText}`"
|
||||
>
|
||||
<span class="shrink-0 text-muted-foreground">{{ row.label }}</span>
|
||||
<span
|
||||
class="min-w-0 truncate text-right font-medium tabular-nums text-foreground/90"
|
||||
:data-testid="variant === 'desktop' ? `pool-stats-cycle-${row.key}` : undefined"
|
||||
:title="row.valueText"
|
||||
>
|
||||
{{ row.valueText }}
|
||||
{{ row.label }} {{ row.valueText }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -116,7 +114,7 @@ const cycleMetricRows = computed(() => {
|
||||
})
|
||||
|
||||
const cycleContainerClass = computed(() => [
|
||||
'mx-auto w-[132px] space-y-1 text-[9px] leading-3',
|
||||
'mx-auto w-[132px] space-y-0.5 text-[10px] leading-4 text-foreground/90 tabular-nums',
|
||||
props.variant === 'mobile' ? 'py-0.5' : '',
|
||||
].filter(Boolean).join(' '))
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ describe('pool key display panels', () => {
|
||||
|
||||
expect(root.querySelector('[data-testid="pool-stats-cycle-text"]')).toBeTruthy()
|
||||
expect(root.querySelector('[data-testid="pool-stats-cycle-text"]')?.className).toContain('w-[132px]')
|
||||
expect(root.querySelector('[data-testid="pool-stats-cycle-request_count"]')?.textContent?.trim()).toBe('12/88')
|
||||
expect(root.querySelector('[data-testid="pool-stats-cycle-request_count"]')?.textContent?.trim()).toBe('请求 12/88')
|
||||
expect(root.querySelector('[data-testid="pool-stats-cycle-small-overlay"]')).toBeNull()
|
||||
expect(root.querySelector('[data-testid="pool-stats-cycle-large-base"]')).toBeNull()
|
||||
expect(root.textContent).not.toContain('5H')
|
||||
@@ -98,7 +98,7 @@ describe('pool key display panels', () => {
|
||||
app.use(createI18n())
|
||||
app.mount(root)
|
||||
|
||||
expect(root.querySelector('[data-testid="pool-stats-cycle-request_count"]')?.textContent?.trim()).toBe('31')
|
||||
expect(root.querySelector('[data-testid="pool-stats-cycle-request_count"]')?.textContent?.trim()).toBe('请求 31')
|
||||
expect(root.querySelector('[data-testid="pool-stats-cycle-single-marker"]')).toBeNull()
|
||||
expect(root.querySelector('[data-testid="pool-stats-cycle-bar-request_count"]')).toBeNull()
|
||||
expect(root.textContent).not.toContain('月')
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
:key="badge.key"
|
||||
:data-usage-model-badge="badge.key"
|
||||
:data-request-detail-model-badge="context === 'detail' ? badge.key : undefined"
|
||||
variant="outline"
|
||||
:variant="badge.variant"
|
||||
class="h-4 shrink-0 whitespace-nowrap rounded-full px-1.5 text-[10px] leading-4"
|
||||
:class="badge.className"
|
||||
:title="badge.title"
|
||||
@@ -52,7 +52,7 @@
|
||||
:key="badge.key"
|
||||
:data-usage-model-badge="badge.key"
|
||||
:data-request-detail-model-badge="context === 'detail' ? badge.key : undefined"
|
||||
variant="outline"
|
||||
:variant="badge.variant"
|
||||
class="h-4 shrink-0 whitespace-nowrap rounded-full px-1.5 text-[10px] leading-4"
|
||||
:class="badge.className"
|
||||
:title="badge.title"
|
||||
@@ -76,6 +76,7 @@ type ModelBadgeKey = 'reasoning' | 'fast' | 'cyber'
|
||||
interface ModelBadgePresentation {
|
||||
key: ModelBadgeKey
|
||||
label: string
|
||||
variant: 'outline' | 'outline-transparent'
|
||||
className: string
|
||||
title: string
|
||||
ariaLabel: string
|
||||
@@ -130,6 +131,7 @@ const modelBadges = computed<ModelBadgePresentation[]>(() => {
|
||||
badges.push({
|
||||
key: 'reasoning',
|
||||
label: reasoningLabel.value,
|
||||
variant: 'outline',
|
||||
className: 'border-primary/30 bg-primary/5 text-primary',
|
||||
title: `Reasoning: ${reasoningLabel.value}`,
|
||||
ariaLabel: `Reasoning: ${reasoningLabel.value}`,
|
||||
@@ -140,7 +142,8 @@ const modelBadges = computed<ModelBadgePresentation[]>(() => {
|
||||
badges.push({
|
||||
key: 'fast',
|
||||
label: 'Fast',
|
||||
className: 'border-amber-400/50 !bg-transparent text-amber-700 dark:border-amber-300/40 dark:text-amber-300',
|
||||
variant: 'outline-transparent',
|
||||
className: 'text-amber-700 dark:text-amber-300',
|
||||
title: '上游请求档位:Fast\n计费档位:Fast',
|
||||
ariaLabel: '上游请求档位:Fast,计费档位:Fast',
|
||||
})
|
||||
@@ -150,6 +153,7 @@ const modelBadges = computed<ModelBadgePresentation[]>(() => {
|
||||
badges.push({
|
||||
key: 'cyber',
|
||||
label: 'Cyber',
|
||||
variant: 'outline',
|
||||
className: 'border-primary/30 bg-primary/5 text-rose-600 dark:text-rose-300',
|
||||
title: '上游 Cyber Policy 拒绝',
|
||||
ariaLabel: '上游 Cyber Policy 拒绝',
|
||||
|
||||
@@ -1579,7 +1579,7 @@ function buildServiceTierBadgePresentation(
|
||||
const title = titleLines.join('\n')
|
||||
return {
|
||||
label: 'Fast',
|
||||
className: 'border-amber-400/50 !bg-transparent text-amber-700 dark:border-amber-300/40 dark:text-amber-300',
|
||||
className: '!bg-transparent text-amber-700 dark:text-amber-300',
|
||||
title,
|
||||
ariaLabel: titleLines.join(','),
|
||||
}
|
||||
|
||||
@@ -339,9 +339,9 @@ describe('UsageRecordsTable', () => {
|
||||
expect(reasoningBadge?.classList.contains('border-primary/30')).toBe(true)
|
||||
expect(reasoningBadge?.classList.contains('bg-primary/5')).toBe(true)
|
||||
expect(reasoningBadge?.classList.contains('text-primary')).toBe(true)
|
||||
expect(fastBadge?.classList.contains('border-amber-400/50')).toBe(true)
|
||||
expect(fastBadge?.classList.contains('!bg-transparent')).toBe(true)
|
||||
expect(fastBadge?.classList.contains('bg-transparent')).toBe(false)
|
||||
expect(fastBadge?.getAttribute('variant')).toBe('outline-transparent')
|
||||
expect(fastBadge?.classList.contains('border-amber-400/50')).toBe(false)
|
||||
expect(fastBadge?.classList.contains('!bg-transparent')).toBe(false)
|
||||
expect(fastBadge?.classList.contains('bg-amber-400/10')).toBe(false)
|
||||
expect(fastBadge?.classList.contains('text-amber-700')).toBe(true)
|
||||
expect(cyberBadge?.classList.contains('border-primary/30')).toBe(true)
|
||||
|
||||
@@ -577,8 +577,8 @@ describe('PoolManagement Codex cycle stats mode', () => {
|
||||
|
||||
expect(root.querySelector('[data-testid="pool-stats-mode-control"]')).toBeNull()
|
||||
expect(root.querySelector('[data-testid="pool-stats-cycle-text"]')).not.toBeNull()
|
||||
expect(root.querySelector('[data-testid="pool-stats-cycle-request_count"]')?.textContent?.trim()).toBe('7/12')
|
||||
expect(root.querySelector('[data-testid="pool-stats-cycle-total_tokens"]')?.textContent?.trim()).toBe('2.5K/5K')
|
||||
expect(root.querySelector('[data-testid="pool-stats-cycle-request_count"]')?.textContent?.trim()).toBe('请求 7/12')
|
||||
expect(root.querySelector('[data-testid="pool-stats-cycle-total_tokens"]')?.textContent?.trim()).toBe('Token 2.5K/5K')
|
||||
expect(root.querySelector('[data-testid="pool-stats-cycle-small-overlay"]')).toBeNull()
|
||||
expect(root.querySelector('[data-testid="pool-stats-cycle-large-base"]')).toBeNull()
|
||||
expect(endpointMocks.listPoolKeys).toHaveBeenLastCalledWith(
|
||||
@@ -707,7 +707,7 @@ describe('PoolManagement Codex cycle stats mode', () => {
|
||||
expect(periodLabels).toContain('月')
|
||||
expect(periodLabels).not.toContain('5H')
|
||||
expect(periodLabels).not.toContain('周')
|
||||
expect(root.querySelector('[data-testid="pool-stats-cycle-request_count"]')?.textContent?.trim()).toBe('23')
|
||||
expect(root.querySelector('[data-testid="pool-stats-cycle-request_count"]')?.textContent?.trim()).toBe('请求 23')
|
||||
expect(root.querySelector('[data-testid="pool-stats-cycle-small-overlay"]')).toBeNull()
|
||||
expect(root.querySelector('[data-testid="pool-stats-cycle-bar-request_count"]')).toBeNull()
|
||||
expect(root.querySelector('[data-testid="pool-stats-cycle-single-marker"]')).toBeNull()
|
||||
|
||||
Reference in New Issue
Block a user