refactor(admin-web/ai-points): rework usage page into card layout

把页面改造成两张白底卡片:顶部卡片承载页头 + 概览四宫格,下面卡片承载用量
台账。指标格子改成圆形图标 + 文字两栏布局,排版更接近现有控制台其它视图。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-28 18:35:58 +08:00
parent fa905519f6
commit 9683a53403
+158 -122
View File
@@ -114,49 +114,67 @@ function formatAIPointDelta(item: AIPointUsageLog): string {
</script> </script>
<template> <template>
<div class="ai-points-page"> <div class="ai-points-view">
<header class="ai-points-header"> <section class="ai-points-view__top-card">
<div> <div class="ai-points-view__header">
<h1>{{ t("route.aiPoints.title") }}</h1> <div class="ai-points-view__header-title">
<p>{{ t("route.aiPoints.description") }}</p> <h2>{{ t("route.aiPoints.title") }}</h2>
</div> <p>{{ t("route.aiPoints.description") }}</p>
<a-button :loading="refreshing" @click="refresh"> </div>
<template #icon><ReloadOutlined /></template> <div class="ai-points-view__header-actions">
{{ t("common.refresh") }} <a-button :loading="refreshing" @click="refresh">
</a-button> <template #icon><ReloadOutlined /></template>
</header> {{ t("common.refresh") }}
</a-button>
<section class="ai-points-summary"> </div>
<div class="ai-points-balance">
<span class="summary-label">{{ t("aiPoints.balance") }}</span>
<strong>{{ quotaSummary?.ai_points_balance ?? "--" }}</strong>
<a-progress :percent="aiPointUsedPercent" :show-info="false" stroke-color="#1677ff" />
</div> </div>
<div class="summary-metric"> <a-divider style="margin: 0; border-color: #f0f0f0;" />
<ThunderboltOutlined />
<span>{{ t("common.used") }}</span>
<strong>{{ quotaSummary?.ai_points_used ?? "--" }} / {{ quotaSummary?.ai_points_total ?? "--" }}</strong>
</div>
<div class="summary-metric"> <div class="ai-points-view__summary">
<CalculatorOutlined /> <div class="ai-points-balance">
<span>{{ t("aiPoints.baseChars") }}</span> <span class="summary-label">{{ t("aiPoints.balance") }}</span>
<strong>{{ quotaSummary?.ai_point_base_chars ?? "--" }}</strong> <strong>{{ quotaSummary?.ai_points_balance ?? "--" }}</strong>
</div> <a-progress :percent="aiPointUsedPercent" :show-info="false" stroke-color="#1677ff" />
</div>
<div class="summary-metric"> <div class="summary-metric">
<FieldTimeOutlined /> <div class="summary-metric-icon">
<span>{{ t("shell.resetAt") }}</span> <ThunderboltOutlined />
<strong>{{ formatDateTime(quotaSummary?.ai_points_reset_at) }}</strong> </div>
<div class="summary-metric-content">
<span>{{ t("common.used") }}</span>
<strong>{{ quotaSummary?.ai_points_used ?? "--" }} / {{ quotaSummary?.ai_points_total ?? "--" }}</strong>
</div>
</div>
<div class="summary-metric">
<div class="summary-metric-icon">
<CalculatorOutlined />
</div>
<div class="summary-metric-content">
<span>{{ t("aiPoints.baseChars") }}</span>
<strong>{{ quotaSummary?.ai_point_base_chars ?? "--" }}</strong>
</div>
</div>
<div class="summary-metric">
<div class="summary-metric-icon">
<FieldTimeOutlined />
</div>
<div class="summary-metric-content">
<span>{{ t("shell.resetAt") }}</span>
<strong>{{ formatDateTime(quotaSummary?.ai_points_reset_at) }}</strong>
</div>
</div>
</div> </div>
</section> </section>
<section class="ai-points-ledger"> <section class="ai-points-view__table-card">
<div class="ledger-head"> <div class="ai-points-view__table-head">
<div> <div>
<h2>{{ t("aiPoints.ledgerTitle") }}</h2> <p class="eyebrow">{{ t("aiPoints.ledgerTitle") }}</p>
<span>{{ t("aiPoints.ledgerTotal", { total: usageTotal }) }}</span> <h3>{{ t("aiPoints.ledgerTotal", { total: usageTotal }) }}</h3>
</div> </div>
</div> </div>
@@ -215,124 +233,136 @@ function formatAIPointDelta(item: AIPointUsageLog): string {
</template> </template>
<style scoped> <style scoped>
.ai-points-page { .ai-points-view {
max-width: 1180px; display: flex;
margin: 0 auto; flex-direction: column;
} gap: 22px;
}
.ai-points-header {
.ai-points-view__top-card {
background: #fff;
border: 1px solid #e6edf5;
border-radius: 12px;
overflow: hidden;
}
.ai-points-view__header {
display: flex; display: flex;
align-items: flex-start;
justify-content: space-between; justify-content: space-between;
gap: 24px; align-items: flex-start;
margin-bottom: 20px; padding: 24px;
} }
.ai-points-header h1, .ai-points-view__header-title h2 {
.ledger-head h2 {
margin: 0; margin: 0;
color: #141414; font-size: 20px;
font-weight: 750; font-weight: 600;
letter-spacing: 0; color: #1a1a1a;
line-height: 1.4;
} }
.ai-points-header h1 { .ai-points-view__header-title p {
font-size: 24px; margin: 6px 0 0 0;
line-height: 32px; font-size: 13px;
color: #8c8c8c;
} }
.ai-points-header p { .ai-points-view__header-actions {
margin: 6px 0 0; display: flex;
color: var(--muted); gap: 12px;
font-size: 14px;
line-height: 22px;
} }
.ai-points-summary { .ai-points-view__summary {
display: grid; display: grid;
grid-template-columns: minmax(260px, 1.5fr) repeat(3, minmax(160px, 1fr)); grid-template-columns: minmax(260px, 1.5fr) repeat(3, minmax(160px, 1fr));
gap: 12px; gap: 16px;
margin-bottom: 20px; padding: 24px;
} }
.ai-points-balance, .ai-points-balance,
.summary-metric, .summary-metric {
.ai-points-ledger { border: 1px solid #e6edf5;
border: 1px solid #edf0f5; border-radius: 10px;
border-radius: 8px; background: #fafcff;
background: #fff;
} }
.ai-points-balance { .ai-points-balance {
padding: 18px; padding: 20px;
} }
.summary-label, .summary-label,
.summary-metric span, .summary-metric span,
.ledger-head span,
.usage-type-cell span, .usage-type-cell span,
.usage-rule { .usage-rule {
color: var(--muted); color: #8c8c8c;
} }
.ai-points-balance strong { .ai-points-balance strong {
display: block; display: block;
margin: 6px 0 12px; margin: 8px 0 16px;
color: #141414; color: #1a1a1a;
font-size: 36px; font-size: 32px;
font-weight: 750; font-weight: 600;
line-height: 42px; line-height: 1.2;
} }
.summary-metric { .summary-metric {
display: grid;
align-content: center;
gap: 6px;
min-height: 112px;
padding: 16px;
}
.summary-metric :deep(.anticon) {
color: #1677ff;
font-size: 18px;
}
.summary-metric span,
.summary-label {
font-size: 12px;
line-height: 18px;
}
.summary-metric strong {
color: #141414;
font-size: 16px;
font-weight: 650;
line-height: 22px;
}
.ai-points-ledger {
overflow: hidden;
}
.ledger-head {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 16px;
padding: 20px;
}
.summary-metric-icon {
display: flex;
align-items: center;
justify-content: center;
width: 48px;
height: 48px;
border-radius: 50%;
background: #e6f4ff;
color: #1677ff;
font-size: 20px;
}
.summary-metric-content {
display: flex;
flex-direction: column;
gap: 4px;
}
.summary-metric-content span {
font-size: 13px;
line-height: 1.4;
}
.summary-metric-content strong {
color: #1a1a1a;
font-size: 18px;
font-weight: 600;
line-height: 1.2;
}
.ai-points-view__table-card {
padding: 24px;
background: #fff;
border: 1px solid #e6edf5;
border-radius: 12px;
}
.ai-points-view__table-head {
display: flex;
align-items: flex-start;
justify-content: space-between; justify-content: space-between;
gap: 16px; gap: 16px;
padding: 18px 20px; margin-bottom: 18px;
border-bottom: 1px solid #edf0f5;
} }
.ledger-head h2 { .ai-points-view__table-head h3 {
font-size: 16px; margin: 6px 0 0;
line-height: 24px; font-size: 24px;
} font-weight: 600;
color: #1a1a1a;
.ledger-head span {
display: block;
margin-top: 3px;
font-size: 12px;
line-height: 18px;
} }
.usage-type-cell { .usage-type-cell {
@@ -341,8 +371,8 @@ function formatAIPointDelta(item: AIPointUsageLog): string {
} }
.usage-type-cell strong { .usage-type-cell strong {
color: #141414; color: #1a1a1a;
font-weight: 650; font-weight: 600;
} }
.usage-type-cell span, .usage-type-cell span,
@@ -357,7 +387,7 @@ function formatAIPointDelta(item: AIPointUsageLog): string {
.points-delta { .points-delta {
color: #cf1322; color: #cf1322;
font-weight: 750; font-weight: 600;
} }
.points-delta--refund { .points-delta--refund {
@@ -377,18 +407,24 @@ function formatAIPointDelta(item: AIPointUsageLog): string {
} }
@media (max-width: 1100px) { @media (max-width: 1100px) {
.ai-points-summary { .ai-points-view__summary {
grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-columns: repeat(2, minmax(0, 1fr));
} }
} }
@media (max-width: 760px) { @media (max-width: 760px) {
.ai-points-header { .ai-points-view__header {
display: grid; flex-direction: column;
gap: 16px;
} }
.ai-points-summary { .ai-points-view__summary {
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }
.ai-points-view__table-head {
flex-direction: column;
align-items: stretch;
}
} }
</style> </style>