Files
Cli-Proxy-API-Management-Ce…/src/pages/UsagePage.module.scss
Supra4E8C 340c1f1ae5 fix(i18n): correct interpolation syntax and add missing translation keys
- Fix i18next interpolation from {var} to {{var}} format in en.json
  - Add gemini_base_url_label translation key for better form labeling
  - Add virtual auth file and model list related translations
  - Adjust UsagePage title font size to 28px for consistency
2025-12-14 23:44:25 +08:00

679 lines
10 KiB
SCSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
@use '../styles/variables' as *;
@use '../styles/mixins' as *;
.container {
width: 100%;
display: flex;
flex-direction: column;
gap: 16px;
// 覆盖Card组件样式 (80%比例)
:global(.card) {
padding: 12px;
border-radius: $radius-md;
}
:global(.card-header) {
margin-bottom: 10px;
.title {
font-size: 14px;
}
}
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 10px;
}
.pageTitle {
font-size: 28px;
font-weight: 700;
color: var(--text-primary);
margin: 0;
}
.errorBox {
padding: 10px;
background-color: rgba(239, 68, 68, 0.1);
border: 1px solid var(--danger-color);
border-radius: $radius-sm;
color: var(--danger-color);
font-size: 12px;
}
.hint {
color: var(--text-secondary);
font-size: 12px;
text-align: center;
padding: 16px;
}
// Stats Grid - 五个卡片并排显示 (88%比例放大10%)
.statsGrid {
display: grid;
gap: 8px;
grid-template-columns: repeat(5, minmax(0, 1fr));
@include tablet {
grid-template-columns: repeat(3, 1fr);
}
@include mobile {
grid-template-columns: 1fr;
}
}
.statCard {
padding: 13px;
background-color: var(--bg-primary);
border-radius: $radius-md;
border: 1px solid var(--border-color);
display: flex;
flex-direction: column;
gap: 5px;
min-height: 143px;
box-shadow: $shadow-sm;
transition: transform $transition-fast, box-shadow $transition-fast, border-color $transition-fast;
overflow: hidden;
&:hover {
transform: translateY(-2px);
box-shadow: $shadow-md;
border-color: rgba(37, 99, 235, 0.2);
}
}
.statCardHeader {
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: 5px;
}
.statLabelGroup {
display: flex;
flex-direction: column;
gap: 1px;
}
.statIconBadge {
width: 29px;
height: 29px;
border-radius: $radius-sm;
display: grid;
place-items: center;
color: #fff;
font-size: 13px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.08);
flex-shrink: 0;
svg {
display: block;
}
}
.statHeader {
display: flex;
align-items: center;
gap: $spacing-sm;
}
.statIcon {
font-size: 18px;
}
.statLabel {
font-size: 11px;
color: var(--text-secondary);
font-weight: 600;
letter-spacing: 0.01em;
text-transform: uppercase;
}
.statValue {
font-size: 20px;
font-weight: 700;
color: var(--text-primary);
line-height: 1.2;
}
.statValueRow {
display: flex;
gap: $spacing-lg;
}
.statValueSmall {
display: flex;
flex-direction: column;
gap: 2px;
}
.statValueLabel {
font-size: 12px;
color: var(--text-secondary);
}
.statValueNum {
font-size: 20px;
font-weight: 700;
color: var(--text-primary);
}
.statMeta {
display: flex;
flex-direction: column;
gap: 4px;
font-size: 12px;
}
.statSuccess {
color: var(--success-color, #22c55e);
}
.statFailure {
color: var(--danger-color, #ef4444);
}
.statNeutral {
color: var(--text-secondary);
}
.statMetaRow {
display: flex;
flex-wrap: wrap;
gap: 4px;
font-size: 10px;
color: var(--text-secondary);
}
.statMetaItem {
display: inline-flex;
align-items: center;
gap: 4px;
}
.statMetaDot {
width: 6px;
height: 6px;
border-radius: 50%;
background-color: var(--text-secondary);
}
.statSubtle {
color: var(--text-tertiary);
}
.statTrend {
margin-top: auto;
background: var(--bg-secondary, #f6f8fb);
border-radius: $radius-sm;
padding: 4px;
height: 44px;
border: 1px solid var(--border-color);
}
.statTrendPlaceholder {
width: 100%;
height: 100%;
background: var(--bg-tertiary, #eef1f6);
border-radius: $radius-sm;
}
.sparkline {
width: 100%;
height: 100% !important;
}
.statHint {
color: var(--text-tertiary);
font-style: italic;
}
// API List (80%比例)
.apiList {
display: flex;
flex-direction: column;
gap: 6px;
}
.apiItem {
background-color: var(--bg-secondary);
border-radius: $radius-sm;
border: 1px solid var(--border-color);
overflow: hidden;
}
.apiHeader {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px;
cursor: pointer;
transition: background-color 0.15s ease;
&:hover {
background-color: var(--bg-hover);
}
}
.apiInfo {
display: flex;
flex-direction: column;
gap: 3px;
min-width: 0;
flex: 1;
}
.apiEndpoint {
font-weight: 600;
color: var(--text-primary);
font-size: 12px;
word-break: break-all;
}
.apiStats {
display: flex;
flex-wrap: wrap;
gap: 3px;
}
.apiBadge {
font-size: 10px;
color: var(--text-secondary);
background-color: var(--bg-tertiary);
padding: 1px 6px;
border-radius: $radius-sm;
}
.expandIcon {
color: var(--text-secondary);
font-size: 10px;
margin-left: 6px;
}
.apiModels {
padding: 10px;
padding-top: 0;
display: flex;
flex-direction: column;
gap: 3px;
border-top: 1px solid var(--border-color);
margin-top: 0;
padding-top: 10px;
}
.modelRow {
display: grid;
grid-template-columns: 1fr auto auto;
gap: 10px;
padding: 3px 6px;
background-color: var(--bg-primary);
border-radius: $radius-sm;
font-size: 11px;
@include mobile {
grid-template-columns: 1fr;
gap: 3px;
}
}
.modelName {
color: var(--text-primary);
font-weight: 500;
word-break: break-all;
}
.modelStat {
color: var(--text-secondary);
text-align: right;
@include mobile {
text-align: left;
}
}
// Table (80%比例)
.tableWrapper {
overflow-x: auto;
}
.table {
width: 100%;
border-collapse: collapse;
font-size: 11px;
th, td {
padding: 6px 10px;
text-align: left;
border-bottom: 1px solid var(--border-color);
}
th {
font-weight: 600;
color: var(--text-secondary);
background-color: var(--bg-secondary);
white-space: nowrap;
}
td {
color: var(--text-primary);
}
tbody tr:hover {
background-color: var(--bg-hover);
}
}
.modelCell {
font-weight: 500;
max-width: 240px;
word-break: break-all;
}
// Pricing Section (80%比例)
.pricingSection {
display: flex;
flex-direction: column;
gap: 16px;
}
.priceForm {
padding: 10px;
background-color: var(--bg-secondary);
border-radius: $radius-sm;
border: 1px solid var(--border-color);
}
.formRow {
display: flex;
gap: 10px;
align-items: flex-end;
flex-wrap: wrap;
@include mobile {
flex-direction: column;
align-items: stretch;
}
}
.formField {
display: flex;
flex-direction: column;
gap: 3px;
flex: 1;
min-width: 120px;
label {
font-size: 10px;
color: var(--text-secondary);
font-weight: 500;
}
// 确保 Input 组件的 form-group 包装器不影响布局
:global(.form-group) {
margin: 0;
> label {
display: none; // 隐藏 Input 自带的 label使用外层的
}
}
:global(.input) {
height: 40px;
box-sizing: border-box;
}
}
.select {
padding: 10px 12px;
border: 1px solid var(--border-color);
border-radius: $radius-md;
background-color: var(--bg-primary);
color: var(--text-primary);
font-size: 14px;
cursor: pointer;
height: 40px;
box-sizing: border-box;
&:focus {
outline: none;
border-color: var(--primary-color);
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15);
}
}
.pricesList {
display: flex;
flex-direction: column;
gap: 10px;
}
.pricesTitle {
font-size: 12px;
font-weight: 600;
color: var(--text-primary);
margin: 0;
}
.pricesGrid {
display: flex;
flex-direction: column;
gap: 6px;
}
.priceItem {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px;
background-color: var(--bg-secondary);
border-radius: $radius-sm;
border: 1px solid var(--border-color);
gap: 10px;
@include mobile {
flex-direction: column;
align-items: flex-start;
}
}
.priceInfo {
display: flex;
flex-direction: column;
gap: 3px;
min-width: 0;
flex: 1;
}
.priceModel {
font-weight: 600;
color: var(--text-primary);
font-size: 11px;
word-break: break-all;
}
.priceMeta {
display: flex;
gap: 10px;
font-size: 10px;
color: var(--text-secondary);
@include mobile {
flex-direction: column;
gap: 3px;
}
}
.priceActions {
display: flex;
gap: 3px;
flex-shrink: 0;
}
// Chart Section (80%比例)
.chartSection {
display: flex;
flex-direction: column;
gap: 10px;
}
.chartControls {
display: flex;
justify-content: space-between;
align-items: center;
gap: 10px;
@include mobile {
flex-direction: column;
align-items: stretch;
}
}
.chartWrapper {
padding: 12px;
background-color: var(--bg-primary);
border-radius: $radius-md;
border: 1px solid var(--border-color);
display: flex;
flex-direction: column;
gap: 10px;
}
.chartLegend {
display: flex;
flex-wrap: wrap;
gap: 6px 12px;
align-items: center;
min-width: 0;
@include mobile {
flex-wrap: nowrap;
overflow-x: auto;
overflow-y: hidden;
-webkit-overflow-scrolling: touch;
padding-bottom: 4px;
}
}
.legendItem {
display: inline-flex;
align-items: center;
gap: 6px;
min-width: 0;
max-width: 240px;
font-size: 11px;
color: var(--text-secondary);
@include mobile {
max-width: 180px;
}
}
.legendDot {
width: 10px;
height: 10px;
border-radius: 999px;
flex-shrink: 0;
}
.legendLabel {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.chartArea {
height: 240px;
@include mobile {
height: 280px;
}
}
.chartScroller {
width: 100%;
height: 100%;
overflow-x: auto;
overflow-y: hidden;
-webkit-overflow-scrolling: touch;
// Chart.js 默认会设置 canvas 的 touch-action: none导致移动端无法横向滚动
:global(canvas) {
touch-action: pan-x pan-y !important;
}
}
.chartCanvas {
position: relative;
height: 100%;
width: 100%;
}
.periodButtons {
display: flex;
gap: 3px;
}
// Chart Line Controls (80%比例)
.chartLineControls {
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: 16px;
flex-wrap: wrap;
@include mobile {
flex-direction: column;
}
}
.chartLineList {
display: flex;
flex-direction: column;
gap: 6px;
flex: 1;
}
.chartLineItem {
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
@include mobile {
flex-direction: column;
align-items: flex-start;
}
}
.chartLineLabel {
font-size: 11px;
color: var(--text-secondary);
min-width: 48px;
}
.chartLineActions {
display: flex;
align-items: center;
gap: 6px;
flex-shrink: 0;
}
.chartLineCount {
font-size: 11px;
color: var(--text-secondary);
font-weight: 500;
}
.chartLineHint {
font-size: 10px;
color: var(--text-tertiary);
margin: 6px 0 0 0;
font-style: italic;
}