diff --git a/src/pages/UsageRecordsPage.tsx b/src/pages/UsageRecordsPage.tsx index d7b211c..3cc9bab 100644 --- a/src/pages/UsageRecordsPage.tsx +++ b/src/pages/UsageRecordsPage.tsx @@ -993,21 +993,21 @@ type EventColumnDef = { }; const EVENT_COLUMNS: readonly EventColumnDef[] = [ - { key: 'time', labelKey: 'usage.column.time', defaultWidth: 135, minWidth: 90, align: 'center' }, - { key: 'model', labelKey: 'usage.column.model', defaultWidth: 175, minWidth: 110, align: 'center' }, - { key: 'provider', labelKey: 'usage.column.provider', defaultWidth: 110, minWidth: 70, align: 'center' }, - { key: 'source', labelKey: 'usage.column.source', defaultWidth: 110, minWidth: 70, align: 'center' }, - { key: 'key', labelKey: 'usage.column.key', defaultWidth: 130, minWidth: 80, align: 'center' }, - { key: 'input', labelKey: 'usage.column.input', defaultWidth: 76, minWidth: 50, align: 'center' }, - { key: 'output', labelKey: 'usage.column.output', defaultWidth: 76, minWidth: 50, align: 'center' }, - { key: 'reasoning', labelKey: 'usage.column.reasoning', defaultWidth: 76, minWidth: 50, align: 'center' }, - { key: 'cache', labelKey: 'usage.column.cache', defaultWidth: 76, minWidth: 50, align: 'center' }, - { key: 'cacheRate', labelKey: 'usage.column.cacheRate', defaultWidth: 88, minWidth: 65, align: 'center' }, - { key: 'total', labelKey: 'usage.column.total', defaultWidth: 84, minWidth: 60, align: 'center' }, - { key: 'result', labelKey: 'usage.column.result', defaultWidth: 160, minWidth: 90, align: 'center' }, - { key: 'latency', labelKey: 'usage.column.latency', defaultWidth: 88, minWidth: 65, align: 'center' }, - { key: 'ttft', labelKey: 'usage.column.ttft', defaultWidth: 88, minWidth: 65, align: 'center' }, - { key: 'speed', labelKey: 'usage.column.speed', defaultWidth: 92, minWidth: 70, align: 'center' }, + { key: 'time', labelKey: 'usage.column.time', defaultWidth: 150, minWidth: 110, align: 'left' }, + { key: 'model', labelKey: 'usage.column.model', defaultWidth: 190, minWidth: 120, align: 'left' }, + { key: 'provider', labelKey: 'usage.column.provider', defaultWidth: 120, minWidth: 80, align: 'left' }, + { key: 'source', labelKey: 'usage.column.source', defaultWidth: 120, minWidth: 80, align: 'left' }, + { key: 'key', labelKey: 'usage.column.key', defaultWidth: 145, minWidth: 95, align: 'left' }, + { key: 'input', labelKey: 'usage.column.input', defaultWidth: 84, minWidth: 60, align: 'right' }, + { key: 'output', labelKey: 'usage.column.output', defaultWidth: 84, minWidth: 60, align: 'right' }, + { key: 'reasoning', labelKey: 'usage.column.reasoning', defaultWidth: 84, minWidth: 60, align: 'right' }, + { key: 'cache', labelKey: 'usage.column.cache', defaultWidth: 84, minWidth: 60, align: 'right' }, + { key: 'cacheRate', labelKey: 'usage.column.cacheRate', defaultWidth: 92, minWidth: 70, align: 'right' }, + { key: 'total', labelKey: 'usage.column.total', defaultWidth: 90, minWidth: 65, align: 'right' }, + { key: 'result', labelKey: 'usage.column.result', defaultWidth: 150, minWidth: 100, align: 'left' }, + { key: 'latency', labelKey: 'usage.column.latency', defaultWidth: 100, minWidth: 75, align: 'right' }, + { key: 'ttft', labelKey: 'usage.column.ttft', defaultWidth: 100, minWidth: 75, align: 'right' }, + { key: 'speed', labelKey: 'usage.column.speed', defaultWidth: 104, minWidth: 80, align: 'right' }, ] as const; const EVENT_COL_WIDTHS_STORAGE_KEY = 'cpa-gui.usage-events-col-widths.v1'; @@ -1076,7 +1076,7 @@ function UsageResultCell({ record }: { record: UsageRecord }) { .filter(Boolean) .join(' · '); return ( - + {t(`usage.result.${state}`)} @@ -1098,13 +1098,13 @@ function UsageEventCell({ switch (columnKey) { case 'time': return ( - + {formatTime(record.timestamp)} ); case 'model': return ( - + {record.alias || record.model} {record.alias || record.reasoning_effort ? ( @@ -1117,45 +1117,45 @@ function UsageEventCell({ ); case 'provider': return ( - + {record.provider || '—'} ); case 'source': return ( - + {record.source_display || record.source || '—'} ); case 'key': return ( - + {record.api_key_remark || noRemarkLabel} {record.api_key_display || '—'} ); case 'input': return ( - + {compactNumber(record.tokens.input_tokens)} ); case 'output': return ( - + {compactNumber(record.tokens.output_tokens)} ); case 'reasoning': return ( - + {compactNumber(record.tokens.reasoning_tokens)} ); case 'cache': return ( 0 ? ` / Creation: ${record.tokens.cache_creation_tokens.toLocaleString()} tokens` @@ -1170,11 +1170,11 @@ function UsageEventCell({ inputTokens: record.tokens.input_tokens, cacheReadTokens: record.tokens.cache_read_tokens, }); - return {value}; + return {value}; } case 'total': return ( - + {compactNumber(record.tokens.total_tokens)} ); @@ -1182,13 +1182,13 @@ function UsageEventCell({ return ; case 'latency': return ( - + {compactNumber(record.latency_ms)} ms ); case 'ttft': return ( - + {record.ttft_ms == null ? '—' : `${compactNumber(record.ttft_ms)} ms`} ); @@ -1198,7 +1198,7 @@ function UsageEventCell({ latencyMs: record.latency_ms, ttftMs: record.ttft_ms, }); - return {value}; + return {value}; } } } @@ -1348,9 +1348,6 @@ function EventsView({ events, onPage }: { events: UsageEventPage; onPage: (page: {t('usage.events.resetColumns')} ) : null} - - {t('usage.events.page', { page: events.page, total: events.totalPages })} - diff --git a/src/styles.css b/src/styles.css index 52ca83f..13e7408 100644 --- a/src/styles.css +++ b/src/styles.css @@ -7254,11 +7254,23 @@ textarea:focus-visible { } /* Events Tab (请求明细) */ +.usage-events-panel { + display: flex; + min-height: 0; + padding: 0; + flex-direction: column; + overflow: hidden; +} + .usage-events-summary { display: flex; + min-height: 58px; align-items: center; justify-content: space-between; gap: 12px; + padding: 11px 16px; + border-bottom: 1px solid var(--theme-e3e1db); + background: var(--theme-fffdf8); color: var(--theme-8a847c); font-size: var(--font-size-caption); flex-wrap: wrap; @@ -7275,7 +7287,8 @@ textarea:focus-visible { display: inline-flex; align-items: center; padding: 3px 8px; - border-radius: 6px; + border: 1px solid var(--theme-e3e1db); + border-radius: 999px; background: var(--theme-f4f1ea); color: var(--theme-2d2a26); font-weight: 600; @@ -7457,15 +7470,18 @@ textarea:focus-visible { .usage-table-wrap { overflow: auto; - min-height: 380px; - max-height: 620px; - border: 1px solid var(--theme-e3e1db); - border-radius: 8px; + height: clamp(480px, 62vh, 700px); + min-height: 0; + border: 0; + border-radius: 0; background: var(--theme-fffdf8); + overscroll-behavior: contain; + scrollbar-gutter: stable; } .usage-events-table { - border-collapse: collapse; + border-collapse: separate; + border-spacing: 0; table-layout: fixed; font-size: var(--font-size-caption); width: 100%; @@ -7473,18 +7489,17 @@ textarea:focus-visible { .usage-events-table th, .usage-events-table td { - height: 42px; - padding: 6px 10px; + height: 46px; + padding: 8px 12px; border-bottom: 1px solid var(--theme-eeeae2); color: var(--theme-5f5a53); white-space: nowrap; vertical-align: middle; box-sizing: border-box; - text-align: center; } .usage-events-table tr:nth-child(even) td { - background-color: var(--theme-faf8f4); + background-color: transparent; } .usage-events-table tr:hover td { @@ -7495,20 +7510,20 @@ textarea:focus-visible { position: sticky; z-index: 2; top: 0; - background: var(--theme-f4f1ea); + background: var(--theme-f8f6f1); color: var(--theme-7a746c); font-weight: 700; padding: 0; user-select: none; border-bottom: 1px solid var(--theme-e3e1db); - box-shadow: 0 1px 2px rgba(0, 0, 0, 0.03); + box-shadow: 0 1px 0 rgba(45, 42, 38, 0.04); } .usage-th-content { display: flex; align-items: center; - height: 42px; - padding: 6px 10px; + height: 44px; + padding: 8px 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; @@ -7684,7 +7699,7 @@ body.table-col-resizing * { .usage-events-table .usage-result-cell { overflow: hidden; - text-align: center; + text-align: left; } .usage-result-cell > small { @@ -7698,9 +7713,13 @@ body.table-col-resizing * { .usage-pagination { display: flex; + min-height: 52px; align-items: center; justify-content: flex-end; gap: 8px; + padding: 0 16px; + border-top: 1px solid var(--theme-e3e1db); + background: var(--theme-fffdf8); color: var(--theme-8a847c); font-size: var(--font-size-caption); } @@ -7720,6 +7739,16 @@ body.table-col-resizing * { font-size: var(--font-size-caption); } +.usage-events-panel > .usage-empty { + min-height: 480px; +} + +@media (max-height: 760px) { + .usage-table-wrap { + height: 440px; + } +} + /* Pricing Tab */ .usage-pricing-panel { display: grid;