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:
@@ -114,49 +114,67 @@ function formatAIPointDelta(item: AIPointUsageLog): string {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="ai-points-page">
|
||||
<header class="ai-points-header">
|
||||
<div>
|
||||
<h1>{{ t("route.aiPoints.title") }}</h1>
|
||||
<p>{{ t("route.aiPoints.description") }}</p>
|
||||
</div>
|
||||
<a-button :loading="refreshing" @click="refresh">
|
||||
<template #icon><ReloadOutlined /></template>
|
||||
{{ t("common.refresh") }}
|
||||
</a-button>
|
||||
</header>
|
||||
|
||||
<section class="ai-points-summary">
|
||||
<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 class="ai-points-view">
|
||||
<section class="ai-points-view__top-card">
|
||||
<div class="ai-points-view__header">
|
||||
<div class="ai-points-view__header-title">
|
||||
<h2>{{ t("route.aiPoints.title") }}</h2>
|
||||
<p>{{ t("route.aiPoints.description") }}</p>
|
||||
</div>
|
||||
<div class="ai-points-view__header-actions">
|
||||
<a-button :loading="refreshing" @click="refresh">
|
||||
<template #icon><ReloadOutlined /></template>
|
||||
{{ t("common.refresh") }}
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="summary-metric">
|
||||
<ThunderboltOutlined />
|
||||
<span>{{ t("common.used") }}</span>
|
||||
<strong>{{ quotaSummary?.ai_points_used ?? "--" }} / {{ quotaSummary?.ai_points_total ?? "--" }}</strong>
|
||||
</div>
|
||||
<a-divider style="margin: 0; border-color: #f0f0f0;" />
|
||||
|
||||
<div class="summary-metric">
|
||||
<CalculatorOutlined />
|
||||
<span>{{ t("aiPoints.baseChars") }}</span>
|
||||
<strong>{{ quotaSummary?.ai_point_base_chars ?? "--" }}</strong>
|
||||
</div>
|
||||
<div class="ai-points-view__summary">
|
||||
<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 class="summary-metric">
|
||||
<FieldTimeOutlined />
|
||||
<span>{{ t("shell.resetAt") }}</span>
|
||||
<strong>{{ formatDateTime(quotaSummary?.ai_points_reset_at) }}</strong>
|
||||
<div class="summary-metric">
|
||||
<div class="summary-metric-icon">
|
||||
<ThunderboltOutlined />
|
||||
</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>
|
||||
</section>
|
||||
|
||||
<section class="ai-points-ledger">
|
||||
<div class="ledger-head">
|
||||
<section class="ai-points-view__table-card">
|
||||
<div class="ai-points-view__table-head">
|
||||
<div>
|
||||
<h2>{{ t("aiPoints.ledgerTitle") }}</h2>
|
||||
<span>{{ t("aiPoints.ledgerTotal", { total: usageTotal }) }}</span>
|
||||
<p class="eyebrow">{{ t("aiPoints.ledgerTitle") }}</p>
|
||||
<h3>{{ t("aiPoints.ledgerTotal", { total: usageTotal }) }}</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -215,124 +233,136 @@ function formatAIPointDelta(item: AIPointUsageLog): string {
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.ai-points-page {
|
||||
max-width: 1180px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.ai-points-header {
|
||||
.ai-points-view {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 22px;
|
||||
}
|
||||
|
||||
.ai-points-view__top-card {
|
||||
background: #fff;
|
||||
border: 1px solid #e6edf5;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.ai-points-view__header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 24px;
|
||||
margin-bottom: 20px;
|
||||
align-items: flex-start;
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.ai-points-header h1,
|
||||
.ledger-head h2 {
|
||||
.ai-points-view__header-title h2 {
|
||||
margin: 0;
|
||||
color: #141414;
|
||||
font-weight: 750;
|
||||
letter-spacing: 0;
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
color: #1a1a1a;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.ai-points-header h1 {
|
||||
font-size: 24px;
|
||||
line-height: 32px;
|
||||
.ai-points-view__header-title p {
|
||||
margin: 6px 0 0 0;
|
||||
font-size: 13px;
|
||||
color: #8c8c8c;
|
||||
}
|
||||
|
||||
.ai-points-header p {
|
||||
margin: 6px 0 0;
|
||||
color: var(--muted);
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
.ai-points-view__header-actions {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.ai-points-summary {
|
||||
.ai-points-view__summary {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(260px, 1.5fr) repeat(3, minmax(160px, 1fr));
|
||||
gap: 12px;
|
||||
margin-bottom: 20px;
|
||||
gap: 16px;
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.ai-points-balance,
|
||||
.summary-metric,
|
||||
.ai-points-ledger {
|
||||
border: 1px solid #edf0f5;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
.summary-metric {
|
||||
border: 1px solid #e6edf5;
|
||||
border-radius: 10px;
|
||||
background: #fafcff;
|
||||
}
|
||||
|
||||
.ai-points-balance {
|
||||
padding: 18px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.summary-label,
|
||||
.summary-metric span,
|
||||
.ledger-head span,
|
||||
.usage-type-cell span,
|
||||
.usage-rule {
|
||||
color: var(--muted);
|
||||
color: #8c8c8c;
|
||||
}
|
||||
|
||||
.ai-points-balance strong {
|
||||
display: block;
|
||||
margin: 6px 0 12px;
|
||||
color: #141414;
|
||||
font-size: 36px;
|
||||
font-weight: 750;
|
||||
line-height: 42px;
|
||||
margin: 8px 0 16px;
|
||||
color: #1a1a1a;
|
||||
font-size: 32px;
|
||||
font-weight: 600;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.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;
|
||||
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;
|
||||
gap: 16px;
|
||||
padding: 18px 20px;
|
||||
border-bottom: 1px solid #edf0f5;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.ledger-head h2 {
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.ledger-head span {
|
||||
display: block;
|
||||
margin-top: 3px;
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
.ai-points-view__table-head h3 {
|
||||
margin: 6px 0 0;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
color: #1a1a1a;
|
||||
}
|
||||
|
||||
.usage-type-cell {
|
||||
@@ -341,8 +371,8 @@ function formatAIPointDelta(item: AIPointUsageLog): string {
|
||||
}
|
||||
|
||||
.usage-type-cell strong {
|
||||
color: #141414;
|
||||
font-weight: 650;
|
||||
color: #1a1a1a;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.usage-type-cell span,
|
||||
@@ -357,7 +387,7 @@ function formatAIPointDelta(item: AIPointUsageLog): string {
|
||||
|
||||
.points-delta {
|
||||
color: #cf1322;
|
||||
font-weight: 750;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.points-delta--refund {
|
||||
@@ -377,18 +407,24 @@ function formatAIPointDelta(item: AIPointUsageLog): string {
|
||||
}
|
||||
|
||||
@media (max-width: 1100px) {
|
||||
.ai-points-summary {
|
||||
.ai-points-view__summary {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 760px) {
|
||||
.ai-points-header {
|
||||
display: grid;
|
||||
.ai-points-view__header {
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.ai-points-summary {
|
||||
.ai-points-view__summary {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.ai-points-view__table-head {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user