mirror of
https://github.com/zyycn/codex-proxy-rs.git
synced 2026-11-06 08:06:19 +00:00
fix(frontend): refine analytics interactions
Align usage chart bounds and diagnostic loading states, simplify dashboard hover feedback, and improve User-Agent layout. Restore the login light palette to the primary accent color. Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
@@ -286,13 +286,6 @@ onBeforeUnmount(() => {
|
||||
: undefined,
|
||||
]"
|
||||
/>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="pointer-events-none absolute bottom-0 left-1/2 h-0.75 w-1.5 -translate-x-1/2 rounded-full bg-(--cp-text-primary) transition-[opacity,transform] duration-200 ease-[cubic-bezier(0.22,1,0.36,1)] motion-reduce:transition-none"
|
||||
:class="
|
||||
isActivePoint(point) ? 'translate-y-0 opacity-60' : 'translate-y-1 opacity-0'
|
||||
"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -29,9 +29,7 @@ const tabs = [
|
||||
const activeKind = defineModel<TrendKind>('kind', { required: true })
|
||||
const { themeRevision } = storeToRefs(useUiStore())
|
||||
const preferredMotion = usePreferredReducedMotion()
|
||||
const hoveredSummaryLabel = shallowRef<string>()
|
||||
const pinnedSummaryLabel = shallowRef<string>()
|
||||
const activeSummaryLabel = computed(() => hoveredSummaryLabel.value ?? pinnedSummaryLabel.value)
|
||||
|
||||
const hasSamples = computed(() =>
|
||||
props.points.some(
|
||||
@@ -390,12 +388,7 @@ function lineSeries(name: string, data: (number | null)[], color: string, option
|
||||
opacity: muted ? 0.18 : 1,
|
||||
},
|
||||
itemStyle: { color, opacity: muted ? 0.18 : 1 },
|
||||
emphasis: { focus: 'series' as const },
|
||||
blur: {
|
||||
lineStyle: { opacity: 0.2 },
|
||||
itemStyle: { opacity: 0.2 },
|
||||
areaStyle: { opacity: 0.05 },
|
||||
},
|
||||
emphasis: { disabled: true },
|
||||
areaStyle: area
|
||||
? {
|
||||
opacity: muted ? 0.12 : 1,
|
||||
@@ -432,8 +425,7 @@ function barSeries(name: string, data: (number | null)[], color: string, options
|
||||
opacity: muted ? 0.14 : (options.opacity ?? 0.72),
|
||||
borderRadius: options.borderRadius ?? [3, 3, 0, 0],
|
||||
},
|
||||
emphasis: { focus: 'series' as const },
|
||||
blur: { itemStyle: { opacity: 0.18 } },
|
||||
emphasis: { disabled: true },
|
||||
}
|
||||
}
|
||||
|
||||
@@ -450,28 +442,19 @@ function handleTrendChange(value: string) {
|
||||
emit('trendChange', value as TrendKind)
|
||||
}
|
||||
|
||||
function focusSummarySeries(label: string) {
|
||||
hoveredSummaryLabel.value = label
|
||||
}
|
||||
|
||||
function blurSummarySeries() {
|
||||
hoveredSummaryLabel.value = undefined
|
||||
}
|
||||
|
||||
function toggleSummarySeries(label: string) {
|
||||
pinnedSummaryLabel.value = pinnedSummaryLabel.value === label ? undefined : label
|
||||
}
|
||||
|
||||
function isSeriesMuted(name: string) {
|
||||
return Boolean(activeSummaryLabel.value && activeSummaryLabel.value !== name)
|
||||
return Boolean(pinnedSummaryLabel.value && pinnedSummaryLabel.value !== name)
|
||||
}
|
||||
|
||||
function isSummarySeriesActive(label: string) {
|
||||
return activeSummaryLabel.value === label
|
||||
return pinnedSummaryLabel.value === label
|
||||
}
|
||||
|
||||
watch(activeKind, () => {
|
||||
hoveredSummaryLabel.value = undefined
|
||||
pinnedSummaryLabel.value = undefined
|
||||
})
|
||||
</script>
|
||||
@@ -499,10 +482,6 @@ watch(activeKind, () => {
|
||||
:aria-label="`突出显示${item.label}曲线`"
|
||||
:aria-pressed="pinnedSummaryLabel === item.label"
|
||||
class="group grid min-w-0 grid-cols-[8px_minmax(0,1fr)] items-center gap-x-2 rounded-lg border-0 bg-transparent px-2.5 py-2 text-left outline-none focus-visible:ring-2 focus-visible:ring-(--cp-info-border)"
|
||||
@mouseenter="focusSummarySeries(item.label)"
|
||||
@mouseleave="blurSummarySeries"
|
||||
@focus="focusSummarySeries(item.label)"
|
||||
@blur="blurSummarySeries"
|
||||
@click="toggleSummarySeries(item.label)"
|
||||
>
|
||||
<i
|
||||
|
||||
@@ -77,7 +77,7 @@ async function handleSubmit(): Promise<void> {
|
||||
--login-route-stream: color-mix(in srgb, var(--cp-info) 17%, transparent);
|
||||
--login-route-audit: color-mix(in srgb, var(--cp-text-tertiary) 18%, transparent);
|
||||
--login-semantic: color-mix(in srgb, var(--cp-info) 26%, transparent);
|
||||
--login-particle: color-mix(in srgb, var(--cp-info) 76%, var(--cp-normal));
|
||||
--login-particle: var(--cp-accent-primary);
|
||||
--login-particle-glow: color-mix(in srgb, var(--cp-info) 62%, transparent);
|
||||
--login-watermark: color-mix(in srgb, var(--cp-text-primary) 55%, transparent);
|
||||
--login-stack-text: color-mix(in srgb, var(--cp-text-primary) 84%, transparent);
|
||||
@@ -94,7 +94,7 @@ async function handleSubmit(): Promise<void> {
|
||||
--login-form-shadow: color-mix(in srgb, var(--cp-text-primary) 20%, transparent);
|
||||
--login-form-line: color-mix(in srgb, var(--cp-info-border) 54%, transparent);
|
||||
--login-logo-bg: color-mix(in srgb, var(--cp-info-bg) 74%, var(--cp-bg-surface));
|
||||
--login-logo-text: color-mix(in srgb, var(--cp-info) 72%, var(--cp-normal));
|
||||
--login-logo-text: var(--cp-accent-primary);
|
||||
--login-title: var(--cp-text-primary);
|
||||
--login-brand-title: var(--cp-text-strong);
|
||||
--login-brand-caption: var(--cp-text-secondary);
|
||||
@@ -112,10 +112,10 @@ async function handleSubmit(): Promise<void> {
|
||||
--login-error-bg: var(--cp-danger-bg);
|
||||
--login-error-icon: var(--cp-danger);
|
||||
--login-error-text: var(--cp-danger-text);
|
||||
--login-button: color-mix(in srgb, var(--cp-info) 74%, var(--cp-normal));
|
||||
--login-button-hover: color-mix(in srgb, var(--cp-info-hover) 74%, var(--cp-normal-hover));
|
||||
--login-button-active: color-mix(in srgb, var(--cp-info-pressed) 74%, var(--cp-normal-pressed));
|
||||
--login-button-shadow: color-mix(in srgb, var(--cp-info) 30%, transparent);
|
||||
--login-button: var(--cp-accent-primary);
|
||||
--login-button-hover: var(--cp-info-hover);
|
||||
--login-button-active: var(--cp-info-pressed);
|
||||
--login-button-shadow: color-mix(in srgb, var(--cp-accent-primary) 30%, transparent);
|
||||
--login-footer: color-mix(in srgb, var(--cp-text-secondary) 82%, var(--cp-text-tertiary));
|
||||
--login-toggle-bg: color-mix(in srgb, var(--cp-bg-muted) 74%, transparent);
|
||||
--login-toggle-sun: var(--login-logo-text);
|
||||
|
||||
@@ -60,7 +60,7 @@ const chartOption = computed<EChartsOption>(() => {
|
||||
|
||||
return {
|
||||
animationDuration: 240,
|
||||
grid: { left: 52, right: 12, top: 40, bottom: 28 },
|
||||
grid: { left: 0, right: 0, top: 40, bottom: 0, containLabel: true },
|
||||
legend: {
|
||||
top: 0,
|
||||
right: 4,
|
||||
|
||||
@@ -3,6 +3,7 @@ import { CornerDownRight } from '@lucide/vue'
|
||||
import { computed } from 'vue'
|
||||
|
||||
import BaseCard from '@/components/base/BaseCard.vue'
|
||||
import BaseEmpty from '@/components/base/BaseEmpty.vue'
|
||||
import BaseSegmented from '@/components/base/BaseSegmented.vue'
|
||||
import BaseTable from '@/components/base/BaseTable/index.vue'
|
||||
|
||||
@@ -84,6 +85,7 @@ const displayItems = computed(() =>
|
||||
nameDisplay: diagnosticNameDisplay(item.name),
|
||||
})),
|
||||
)
|
||||
const hasData = computed(() => !props.loading && displayItems.value.length > 0)
|
||||
|
||||
function diagnosticNameDisplay(name: string) {
|
||||
const raw = name.trim() || '未知'
|
||||
@@ -117,17 +119,18 @@ function diagnosticNameDisplay(name: string) {
|
||||
<BaseSegmented
|
||||
v-model="dimension"
|
||||
:options="dimensionOptions"
|
||||
:disabled="loading"
|
||||
class="w-full min-w-0 lg:w-80"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #body>
|
||||
<BaseTable
|
||||
v-if="hasData"
|
||||
:key="resultDimension"
|
||||
class="min-h-0 w-full"
|
||||
:columns="diagnosticColumns"
|
||||
:rows="displayItems"
|
||||
:loading="loading"
|
||||
compact
|
||||
row-key="name"
|
||||
empty-text="暂无诊断数据"
|
||||
@@ -204,6 +207,14 @@ function diagnosticNameDisplay(name: string) {
|
||||
</span>
|
||||
</template>
|
||||
</BaseTable>
|
||||
<BaseEmpty
|
||||
v-else
|
||||
compact
|
||||
plain
|
||||
:title="loading ? '正在加载热点诊断数据' : '暂无诊断数据'"
|
||||
description="当前范围没有可诊断的请求记录"
|
||||
class="h-full place-content-center"
|
||||
/>
|
||||
</template>
|
||||
</BaseCard>
|
||||
</template>
|
||||
|
||||
@@ -56,7 +56,7 @@ const chartOption = computed<EChartsOption>(() => {
|
||||
|
||||
return {
|
||||
animationDuration: 240,
|
||||
grid: { left: 48, right: 44, top: 40, bottom: 28 },
|
||||
grid: { left: 0, right: 0, top: 40, bottom: 0, containLabel: true },
|
||||
legend: {
|
||||
top: 0,
|
||||
right: 4,
|
||||
|
||||
@@ -65,7 +65,7 @@ const chartOption = computed<EChartsOption>(() => {
|
||||
|
||||
return {
|
||||
animationDuration: 240,
|
||||
grid: { left: 52, right: 12, top: 40, bottom: 28 },
|
||||
grid: { left: 0, right: 0, top: 40, bottom: 0, containLabel: true },
|
||||
legend: {
|
||||
top: 0,
|
||||
right: 4,
|
||||
|
||||
@@ -72,7 +72,7 @@ export const usageRecordColumns = [
|
||||
{
|
||||
key: 'userAgent',
|
||||
label: 'User-Agent',
|
||||
width: '300px',
|
||||
width: '340px',
|
||||
ellipsis: false,
|
||||
cellClass:
|
||||
'whitespace-normal break-words text-[12px] leading-[1.45] font-[650] text-(--cp-text-secondary)',
|
||||
|
||||
Reference in New Issue
Block a user