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:
zyycn
2026-07-16 20:03:51 +08:00
co-authored by Codex
parent 549bb9f62b
commit f0543b637e
8 changed files with 26 additions and 43 deletions
@@ -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
+6 -6
View File
@@ -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,
+1 -1
View File
@@ -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)',