mirror of
https://github.com/zyycn/codex-proxy-rs.git
synced 2026-11-06 16:06:08 +00:00
refactor(ui): standardize card spacing and layouts
Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
@@ -1,14 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
|
||||
type CardVariant = 'default' | 'dashboard' | 'elevated'
|
||||
type CardPadding = 'default' | 'compact'
|
||||
type CardLayout = 'content' | 'flush'
|
||||
type HeaderCollapseAt = 'sm' | 'lg' | 'none'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
as?: keyof HTMLElementTagNameMap
|
||||
padded?: boolean
|
||||
variant?: CardVariant
|
||||
padding?: CardPadding
|
||||
layout?: CardLayout
|
||||
title?: string
|
||||
description?: string
|
||||
headerCollapseAt?: HeaderCollapseAt
|
||||
@@ -17,8 +18,8 @@ const props = withDefaults(
|
||||
}>(),
|
||||
{
|
||||
as: 'section',
|
||||
padded: true,
|
||||
variant: 'default',
|
||||
padding: 'default',
|
||||
layout: 'content',
|
||||
title: undefined,
|
||||
description: undefined,
|
||||
headerCollapseAt: 'sm',
|
||||
@@ -36,16 +37,9 @@ const slots = defineSlots<{
|
||||
default?: () => unknown
|
||||
}>()
|
||||
|
||||
const paddingClasses: Record<CardVariant, string> = {
|
||||
default: 'px-5 py-3',
|
||||
dashboard: 'px-7 py-5.5',
|
||||
elevated: 'px-5 py-3',
|
||||
}
|
||||
|
||||
const shadowClasses: Record<CardVariant, string> = {
|
||||
default: 'shadow-(--cp-shadow-card)',
|
||||
dashboard: 'shadow-(--cp-shadow-card)',
|
||||
elevated: 'shadow-(--cp-shadow-popover)',
|
||||
const paddingClasses: Record<CardPadding, string> = {
|
||||
default: 'p-5.5',
|
||||
compact: 'p-4',
|
||||
}
|
||||
|
||||
const hasManagedHeader = computed(
|
||||
@@ -53,6 +47,12 @@ const hasManagedHeader = computed(
|
||||
!!props.title || !!props.description || !!slots.actions || !!slots.title || !!slots.description,
|
||||
)
|
||||
|
||||
const outerPaddingClass = computed(() => {
|
||||
if (props.layout === 'flush')
|
||||
return undefined
|
||||
return paddingClasses[props.padding]
|
||||
})
|
||||
|
||||
const managedHeaderLayoutClasses = computed(() => {
|
||||
if (props.headerCollapseAt === 'none') {
|
||||
return 'flex items-start justify-between gap-3'
|
||||
@@ -69,14 +69,17 @@ const managedHeaderLayoutClasses = computed(() => {
|
||||
<template>
|
||||
<component
|
||||
:is="props.as"
|
||||
class="[--cp-input-current-bg:var(--cp-input-soft-bg)] [--cp-input-current-bg-hover:var(--cp-input-soft-bg-hover)] overflow-hidden bg-(--cp-bg-surface) rounded-(--cp-card-radius)"
|
||||
class="[--cp-input-current-bg:var(--cp-input-soft-bg)] [--cp-input-current-bg-hover:var(--cp-input-soft-bg-hover)] overflow-hidden rounded-(--cp-card-radius) bg-(--cp-bg-surface) shadow-(--cp-shadow-card)"
|
||||
:class="[
|
||||
shadowClasses[props.variant],
|
||||
props.padded ? paddingClasses[props.variant] : undefined,
|
||||
outerPaddingClass,
|
||||
]"
|
||||
>
|
||||
<template v-if="$slots.header || hasManagedHeader || $slots.body">
|
||||
<header v-if="$slots.header || hasManagedHeader" class="shrink-0" :class="props.headerClass">
|
||||
<header
|
||||
v-if="$slots.header || hasManagedHeader"
|
||||
class="shrink-0"
|
||||
:class="props.headerClass"
|
||||
>
|
||||
<slot name="header">
|
||||
<div :class="managedHeaderLayoutClasses">
|
||||
<div class="min-w-0 pt-0.5">
|
||||
@@ -105,7 +108,10 @@ const managedHeaderLayoutClasses = computed(() => {
|
||||
</slot>
|
||||
</header>
|
||||
|
||||
<div v-if="$slots.body || $slots.default" :class="props.bodyClass">
|
||||
<div
|
||||
v-if="$slots.body || $slots.default"
|
||||
:class="props.bodyClass"
|
||||
>
|
||||
<slot name="body">
|
||||
<slot />
|
||||
</slot>
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
const props = defineProps<{
|
||||
title: string
|
||||
description?: string
|
||||
actionsInline?: boolean
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<header class="grid min-h-17 shrink-0 grid-cols-[minmax(0,1fr)_auto] items-start gap-x-4 gap-y-3">
|
||||
<header
|
||||
class="grid min-h-17 shrink-0 items-start"
|
||||
:class="
|
||||
props.actionsInline
|
||||
? 'grid-cols-[minmax(0,1fr)_auto] gap-x-4'
|
||||
: 'grid-cols-1 gap-y-3 sm:grid-cols-[minmax(0,1fr)_auto] sm:gap-x-4'
|
||||
"
|
||||
>
|
||||
<div class="min-w-0">
|
||||
<h1 class="m-0 text-[34px] leading-[1.15] font-extrabold text-(--cp-text-primary)">
|
||||
{{ title }}
|
||||
@@ -23,6 +31,7 @@ defineProps<{
|
||||
<div
|
||||
v-if="$slots.actions"
|
||||
class="mt-0.5 flex shrink-0 items-center justify-end gap-2"
|
||||
:class="props.actionsInline ? undefined : 'max-sm:w-full'"
|
||||
>
|
||||
<slot name="actions" />
|
||||
</div>
|
||||
|
||||
@@ -54,10 +54,13 @@ const provider = defineModel<string>('provider', { required: true })
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex shrink-0 self-end items-center justify-end gap-2 md:ml-auto">
|
||||
<div
|
||||
class="grid w-full grid-cols-2 gap-2 md:flex md:w-auto md:shrink-0 md:self-end md:items-center md:justify-end md:ml-auto"
|
||||
>
|
||||
<BaseButton
|
||||
v-if="selectedCount > 0"
|
||||
variant="danger"
|
||||
class="w-full whitespace-nowrap md:w-auto"
|
||||
:disabled="batchDeleting"
|
||||
@click="emit('deleteSelected')"
|
||||
>
|
||||
@@ -67,13 +70,19 @@ const provider = defineModel<string>('provider', { required: true })
|
||||
<BaseButton
|
||||
v-if="selectedCount > 0"
|
||||
variant="default"
|
||||
class="w-full whitespace-nowrap md:w-auto"
|
||||
:loading="exportingAccounts"
|
||||
@click="emit('exportSelected')"
|
||||
>
|
||||
<Download class="size-4" />
|
||||
导出选中 ({{ selectedCount }})
|
||||
</BaseButton>
|
||||
<BaseButton variant="primary" @click="emit('create')">
|
||||
<BaseButton
|
||||
variant="primary"
|
||||
class="whitespace-nowrap md:w-auto"
|
||||
:class="selectedCount > 0 ? 'col-span-2 w-full' : 'col-span-2 justify-self-end'"
|
||||
@click="emit('create')"
|
||||
>
|
||||
<Upload class="size-4" />
|
||||
导入账号
|
||||
</BaseButton>
|
||||
|
||||
@@ -65,16 +65,15 @@ function overviewIconClass(tone: string) {
|
||||
v-for="item in overviewItems"
|
||||
:key="item.label"
|
||||
as="article"
|
||||
:padded="false"
|
||||
class="h-24"
|
||||
padding="compact"
|
||||
>
|
||||
<div class="flex h-full items-stretch justify-between gap-3 px-5 py-3">
|
||||
<div class="flex min-w-0 flex-col justify-between">
|
||||
<div class="flex items-stretch justify-between gap-3">
|
||||
<div class="flex min-w-0 flex-col">
|
||||
<p class="m-0 text-[12px] leading-none font-heavy text-(--cp-text-secondary)">
|
||||
{{ item.label }}
|
||||
</p>
|
||||
<strong
|
||||
class="block font-mono text-[26px] leading-none font-extrabold text-(--cp-text-primary)"
|
||||
class="my-2 block font-mono text-[26px] leading-none font-extrabold text-(--cp-text-primary)"
|
||||
>
|
||||
{{ item.value }}
|
||||
</strong>
|
||||
|
||||
@@ -126,10 +126,8 @@ const {
|
||||
<AccountOverviewCards :summary="accountSummary" />
|
||||
|
||||
<BaseCard
|
||||
:padded="false"
|
||||
class="mt-4 flex h-[calc(100dvh-248px)] min-h-125 flex-col"
|
||||
header-class="px-4 pt-4 pb-2 md:px-5"
|
||||
body-class="flex min-h-0 flex-1 px-4 pb-3 md:px-5"
|
||||
class="mt-4 flex h-[calc(100dvh-250px)] min-h-125 flex-col"
|
||||
body-class="mt-3 flex min-h-0 flex-1"
|
||||
>
|
||||
<template #header>
|
||||
<AccountFilters
|
||||
|
||||
@@ -19,11 +19,11 @@ const search = defineModel<string>('search', { required: true })
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="flex w-full flex-col gap-3 md:flex-row md:flex-wrap md:items-center"
|
||||
class="flex w-full flex-wrap items-center gap-3"
|
||||
role="group"
|
||||
aria-label="API Key 筛选与操作"
|
||||
>
|
||||
<div class="min-w-0 w-full md:w-96 md:flex-none">
|
||||
<div class="min-w-0 flex-1 md:w-96 md:flex-none">
|
||||
<BaseInput v-model="search" placeholder="搜索名称、标签或 ID" class="w-full">
|
||||
<template #prefix>
|
||||
<Search class="size-4.5 text-(--cp-text-tertiary)" />
|
||||
@@ -31,7 +31,7 @@ const search = defineModel<string>('search', { required: true })
|
||||
</BaseInput>
|
||||
</div>
|
||||
|
||||
<div class="flex shrink-0 self-end items-center justify-end gap-2 md:ml-auto">
|
||||
<div class="flex shrink-0 items-center justify-end gap-2 md:ml-auto">
|
||||
<BaseButton
|
||||
v-if="selectedCount > 0"
|
||||
variant="danger"
|
||||
|
||||
@@ -88,10 +88,8 @@ const {
|
||||
/>
|
||||
|
||||
<BaseCard
|
||||
:padded="false"
|
||||
class="mt-5 flex h-[calc(100dvh-136px)] min-h-125 flex-col"
|
||||
header-class="px-5 pt-4"
|
||||
body-class="flex min-h-0 flex-1 px-5 py-3"
|
||||
body-class="mt-3 flex min-h-0 flex-1"
|
||||
>
|
||||
<template #header>
|
||||
<ApiKeyFilters
|
||||
|
||||
@@ -90,7 +90,9 @@ const statusRows = computed(() => {
|
||||
}
|
||||
const active = p?.active ?? 0
|
||||
const refreshing = p?.refreshing ?? 0
|
||||
const quota = p?.quotaExhausted ?? 0
|
||||
const rateLimited = p?.rateLimited ?? 0
|
||||
const quotaExhausted = p?.quotaExhausted ?? 0
|
||||
const quotaLimited = rateLimited + quotaExhausted
|
||||
const expired = p?.expired ?? 0
|
||||
const invalid = p?.invalid ?? 0
|
||||
const disabled = p?.disabled ?? 0
|
||||
@@ -113,16 +115,9 @@ const statusRows = computed(() => {
|
||||
icon: RefreshCw,
|
||||
},
|
||||
{
|
||||
label: '限流中',
|
||||
description: '任一额度窗口已触顶',
|
||||
value: String(p?.rateLimited ?? 0),
|
||||
tone: 'warning',
|
||||
icon: TriangleAlert,
|
||||
},
|
||||
{
|
||||
label: '配额耗尽',
|
||||
description: '已确认 402 耗尽',
|
||||
value: String(quota),
|
||||
label: '额度受限',
|
||||
description: `限流中 ${rateLimited} · 配额耗尽 ${quotaExhausted}`,
|
||||
value: String(quotaLimited),
|
||||
tone: 'warning',
|
||||
icon: TriangleAlert,
|
||||
},
|
||||
@@ -149,23 +144,21 @@ const statusBars = computed(() => {
|
||||
return []
|
||||
const active = (p.active / p.total) * 100
|
||||
const refreshing = ((p.refreshing ?? 0) / p.total) * 100
|
||||
const rateLimited = ((p.rateLimited ?? 0) / p.total) * 100
|
||||
const quota = (p.quotaExhausted / p.total) * 100
|
||||
const quotaLimited = (((p.rateLimited ?? 0) + p.quotaExhausted) / p.total) * 100
|
||||
const unavailable = ((p.expired + (p.invalid ?? 0) + p.disabled + p.banned) / p.total) * 100
|
||||
return [
|
||||
{ pct: active, cls: 'bg-(--cp-success)' },
|
||||
{ pct: refreshing, cls: 'bg-(--cp-normal)' },
|
||||
{ pct: rateLimited, cls: 'bg-(--cp-warning)' },
|
||||
{ pct: quota, cls: 'bg-(--cp-warning)' },
|
||||
{ pct: quotaLimited, cls: 'bg-(--cp-warning)' },
|
||||
{ pct: unavailable, cls: 'bg-(--cp-danger)' },
|
||||
].filter(b => b.pct > 0)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BaseCard as="article" :padded="false" class="w-full xl:h-112.5">
|
||||
<BaseCard as="article" class="w-full xl:h-112.5">
|
||||
<div
|
||||
class="grid px-4 pt-5 pb-6 lg:px-7 lg:pt-6 xl:grid-cols-[minmax(0,0.9fr)_minmax(0,1.28fr)_minmax(280px,0.9fr)] xl:gap-7 xl:pb-0"
|
||||
class="grid xl:grid-cols-[minmax(0,0.9fr)_minmax(0,1.28fr)_minmax(280px,0.9fr)] xl:gap-7"
|
||||
>
|
||||
<section class="min-w-0 w-full pb-6 xl:h-100.5 xl:pb-0">
|
||||
<h2 class="m-0 text-xl leading-[1.15] font-heavy text-(--cp-text-primary)">
|
||||
|
||||
@@ -69,7 +69,7 @@ const sparklineOption = computed<EChartsOption | null>(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BaseCard as="article" :padded="false" class="relative h-38.5 w-full px-6 pt-5">
|
||||
<BaseCard as="article" padding="compact" class="relative h-38.5 w-full">
|
||||
<div class="flex items-start gap-3">
|
||||
<BaseMotionIcon
|
||||
aria-hidden="true"
|
||||
|
||||
@@ -207,7 +207,6 @@ onBeforeUnmount(() => {
|
||||
<template>
|
||||
<BaseCard
|
||||
as="article"
|
||||
variant="dashboard"
|
||||
:title="timeline.title"
|
||||
:description="timeline.description"
|
||||
header-collapse-at="lg"
|
||||
|
||||
@@ -39,7 +39,7 @@ const {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BaseCard as="article" variant="dashboard" title="使用趋势" class="min-h-95 w-full">
|
||||
<BaseCard as="article" title="使用趋势" class="min-h-95 w-full">
|
||||
<template #actions>
|
||||
<BaseSegmented
|
||||
v-model="activeKind"
|
||||
|
||||
@@ -17,7 +17,6 @@ const dashboardUsageRecordColumns = usageRecordColumns.filter(column => column.k
|
||||
<template>
|
||||
<BaseCard
|
||||
as="article"
|
||||
variant="dashboard"
|
||||
title="使用记录"
|
||||
description="最近 10 条成功请求"
|
||||
class="h-117 w-full"
|
||||
|
||||
@@ -171,7 +171,6 @@ function providerLabel(provider: string) {
|
||||
<template>
|
||||
<BaseCard
|
||||
as="article"
|
||||
variant="dashboard"
|
||||
title="上游请求身份"
|
||||
body-class="flex min-h-0 flex-1 flex-col"
|
||||
class="flex min-h-95 w-full flex-col"
|
||||
|
||||
@@ -35,7 +35,7 @@ const {
|
||||
|
||||
<template>
|
||||
<div class="w-full">
|
||||
<BasePageHeader title="系统概览">
|
||||
<BasePageHeader title="系统概览" actions-inline>
|
||||
<template #description>
|
||||
<span>当日统计</span>
|
||||
<DashboardHeartbeat :updated-at="lastRefreshedAt" />
|
||||
|
||||
@@ -48,8 +48,7 @@ function togglePasswordVisible(): void {
|
||||
<template>
|
||||
<BaseCard
|
||||
as="form"
|
||||
:padded="false"
|
||||
variant="elevated"
|
||||
layout="flush"
|
||||
class="login-form relative grid min-h-129.25 w-[min(440px,100%)] gap-3 rounded-lg px-7.5 pt-6.5 pb-6 max-[560px]:min-h-auto max-[560px]:gap-3 max-[560px]:p-5.5"
|
||||
@submit.prevent="emit('submit')"
|
||||
>
|
||||
|
||||
@@ -26,11 +26,9 @@ const emit = defineEmits<{
|
||||
|
||||
<template>
|
||||
<BaseCard
|
||||
:padded="false"
|
||||
title="管理员 API Key"
|
||||
description="用于外部系统集成,具有管理员权限"
|
||||
header-class="px-5 pt-4"
|
||||
body-class="px-5 py-5"
|
||||
body-class="mt-5"
|
||||
>
|
||||
<template #actions>
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
|
||||
@@ -23,11 +23,9 @@ const emit = defineEmits<{
|
||||
|
||||
<template>
|
||||
<BaseCard
|
||||
:padded="false"
|
||||
title="模型映射"
|
||||
description="配置请求模型与上游模型的映射关系"
|
||||
header-class="px-5 pt-4"
|
||||
body-class="px-5 py-5"
|
||||
body-class="mt-5"
|
||||
>
|
||||
<div class="grid gap-4">
|
||||
<div class="flex flex-wrap items-center gap-3">
|
||||
|
||||
@@ -14,11 +14,9 @@ const model = defineModel<RotationStrategy | ''>({ required: true })
|
||||
|
||||
<template>
|
||||
<BaseCard
|
||||
:padded="false"
|
||||
title="调度策略"
|
||||
description="决定每次请求如何调度账号池"
|
||||
header-class="px-5 pt-4"
|
||||
body-class="px-5 py-5"
|
||||
body-class="mt-5"
|
||||
>
|
||||
<div class="grid max-w-6xl gap-3 lg:grid-cols-4">
|
||||
<button
|
||||
|
||||
@@ -14,11 +14,9 @@ const requestIntervalMs = defineModel<string>('requestIntervalMs', { required: t
|
||||
|
||||
<template>
|
||||
<BaseCard
|
||||
:padded="false"
|
||||
title="运行参数"
|
||||
description="请求节奏、账号并发和 Token 刷新"
|
||||
header-class="px-5 pt-4"
|
||||
body-class="px-5 py-5"
|
||||
body-class="mt-5"
|
||||
>
|
||||
<BaseForm :columns="2" class="max-w-6xl">
|
||||
<BaseFormItem
|
||||
|
||||
@@ -90,11 +90,9 @@ function canDelete(record: BackupRecord): boolean {
|
||||
|
||||
<template>
|
||||
<BaseCard
|
||||
:padded="false"
|
||||
title="备份记录"
|
||||
description="创建手动备份和管理已有备份记录"
|
||||
header-class="px-5 pt-4"
|
||||
body-class="px-5 py-3"
|
||||
body-class="mt-3"
|
||||
>
|
||||
<template #actions>
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
|
||||
@@ -40,11 +40,9 @@ const TIMEZONE_OPTIONS = [
|
||||
|
||||
<template>
|
||||
<BaseCard
|
||||
:padded="false"
|
||||
title="定时备份"
|
||||
description="配置自动定时备份"
|
||||
header-class="px-5 pt-4"
|
||||
body-class="px-5 py-5"
|
||||
body-class="mt-5"
|
||||
>
|
||||
<template #actions>
|
||||
<BaseButton variant="primary" :loading="saving" :disabled="loading" @click="emit('save')">
|
||||
|
||||
@@ -51,10 +51,8 @@ function toggleSecretVisible(): void {
|
||||
|
||||
<template>
|
||||
<BaseCard
|
||||
:padded="false"
|
||||
title="S3 存储配置"
|
||||
header-class="px-5 pt-4"
|
||||
body-class="px-5 py-5"
|
||||
body-class="mt-5"
|
||||
>
|
||||
<template #description>
|
||||
<span class="text-(--cp-text-secondary)">
|
||||
|
||||
@@ -71,24 +71,9 @@ watch(
|
||||
|
||||
<template>
|
||||
<div class="w-full">
|
||||
<BasePageHeader title="系统设置" description="管理运行参数、调度策略、模型映射与备份配置">
|
||||
<template #actions>
|
||||
<BaseButton
|
||||
v-if="section === 'runtime'"
|
||||
variant="primary"
|
||||
:loading="saving"
|
||||
:disabled="loading"
|
||||
@click="saveSettings"
|
||||
>
|
||||
<template #icon>
|
||||
<Save class="size-4" />
|
||||
</template>
|
||||
{{ saving ? '保存中...' : '保存' }}
|
||||
</BaseButton>
|
||||
</template>
|
||||
</BasePageHeader>
|
||||
<BasePageHeader title="系统设置" description="管理运行参数、调度策略、模型映射与备份配置" />
|
||||
|
||||
<div class="mt-4">
|
||||
<div class="mt-4 flex flex-wrap items-center justify-between gap-3">
|
||||
<BaseSegmented
|
||||
:model-value="section"
|
||||
class="bg-(--cp-input-soft-bg)!"
|
||||
@@ -99,6 +84,18 @@ watch(
|
||||
aria-label="设置分区"
|
||||
@update:model-value="switchSection"
|
||||
/>
|
||||
<BaseButton
|
||||
v-if="section === 'runtime'"
|
||||
variant="primary"
|
||||
:loading="saving"
|
||||
:disabled="loading"
|
||||
@click="saveSettings"
|
||||
>
|
||||
<template #icon>
|
||||
<Save class="size-4" />
|
||||
</template>
|
||||
{{ saving ? '保存中...' : '保存' }}
|
||||
</BaseButton>
|
||||
</div>
|
||||
|
||||
<div v-show="section === 'runtime'" class="mt-5 grid w-full gap-5">
|
||||
|
||||
@@ -239,12 +239,10 @@ function formatTooltip(params: unknown) {
|
||||
<template>
|
||||
<BaseCard
|
||||
as="article"
|
||||
:padded="false"
|
||||
title="成本效率"
|
||||
description="预估费用、Token 与缓存收益"
|
||||
header-collapse-at="none"
|
||||
header-class="px-5 pt-4"
|
||||
body-class="px-5 pt-3 pb-4"
|
||||
body-class="mt-3"
|
||||
class="h-full min-h-90"
|
||||
>
|
||||
<template #actions>
|
||||
|
||||
@@ -114,12 +114,10 @@ function diagnosticNameDisplay(name: string) {
|
||||
<template>
|
||||
<BaseCard
|
||||
as="article"
|
||||
:padded="false"
|
||||
title="热点诊断"
|
||||
:description="`按${selectedDimensionLabel}定位错误、慢请求与费用热点`"
|
||||
header-collapse-at="lg"
|
||||
header-class="px-5 pt-4"
|
||||
body-class="min-h-0 min-w-0 px-5 pt-3"
|
||||
body-class="mt-3 min-h-0 min-w-0"
|
||||
class="grid h-105 min-h-105 max-h-105 min-w-0 w-full grid-rows-[auto_minmax(0,1fr)] lg:h-90 lg:min-h-90 lg:max-h-90"
|
||||
>
|
||||
<template #actions>
|
||||
|
||||
@@ -146,11 +146,9 @@ function formatTooltip(params: unknown) {
|
||||
<template>
|
||||
<BaseCard
|
||||
as="article"
|
||||
:padded="false"
|
||||
title="请求健康"
|
||||
:description="`按${granularityText}观察请求量、成功率与失败时段`"
|
||||
header-class="px-5 pt-4"
|
||||
body-class="px-5 pt-3 pb-4"
|
||||
body-class="mt-3"
|
||||
class="h-full min-h-90"
|
||||
>
|
||||
<template #body>
|
||||
|
||||
@@ -167,12 +167,10 @@ function formatTooltip(params: unknown) {
|
||||
<template>
|
||||
<BaseCard
|
||||
as="article"
|
||||
:padded="false"
|
||||
title="响应速度"
|
||||
description="总耗时或首字的分位耗时,越低越快"
|
||||
header-collapse-at="none"
|
||||
header-class="px-5 pt-4"
|
||||
body-class="px-5 pt-3 pb-4"
|
||||
body-class="mt-3"
|
||||
class="h-full min-h-90"
|
||||
>
|
||||
<template #actions>
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { getUsageRecordSummary } from '@/api'
|
||||
import { Activity, Database, FileText, Timer } from '@lucide/vue'
|
||||
|
||||
import { computed } from 'vue'
|
||||
import BaseCard from '@/components/base/BaseCard.vue'
|
||||
import BaseMotionIcon from '@/components/base/BaseMotionIcon.vue'
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -54,10 +55,12 @@ const items = computed(() => [
|
||||
class="mt-5 grid shrink-0 grid-cols-1 gap-3 md:grid-cols-2 xl:grid-cols-4"
|
||||
aria-label="使用概览"
|
||||
>
|
||||
<article
|
||||
<BaseCard
|
||||
v-for="item in items"
|
||||
:key="item.key"
|
||||
class="grid min-h-23 grid-cols-[36px_minmax(0,1fr)] items-stretch gap-3 rounded-(--cp-card-radius) bg-(--cp-bg-surface) px-4 py-3 shadow-(--cp-shadow-card)"
|
||||
as="article"
|
||||
padding="compact"
|
||||
class="grid min-h-23 grid-cols-[36px_minmax(0,1fr)] items-stretch gap-3"
|
||||
>
|
||||
<BaseMotionIcon
|
||||
aria-hidden="true"
|
||||
@@ -79,6 +82,6 @@ const items = computed(() => [
|
||||
{{ item.detail }}
|
||||
</span>
|
||||
</div>
|
||||
</article>
|
||||
</BaseCard>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
@@ -5,6 +5,7 @@ export const usageRecordColumns = [
|
||||
key: 'accountEmail',
|
||||
label: '账号',
|
||||
width: '280px',
|
||||
fixed: false as const,
|
||||
ellipsis: true,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -79,10 +79,8 @@ watch(timeRange, () => {
|
||||
/>
|
||||
|
||||
<BaseCard
|
||||
:padded="false"
|
||||
class="mt-5 flex flex-col"
|
||||
header-class="px-5 pt-4"
|
||||
body-class="flex min-h-0 flex-col px-5 pt-3 pb-4"
|
||||
body-class="mt-3 flex min-h-0 flex-col"
|
||||
>
|
||||
<template #header>
|
||||
<div class="flex flex-wrap items-center justify-between gap-3">
|
||||
|
||||
Reference in New Issue
Block a user