fix(theme): restore semantic color roles

Separate semantic text on neutral surfaces from high-contrast text on tinted backgrounds, derive each role against its actual surfaces, and migrate badges and alerts to explicit on-background tokens.

Restore vivid usage-table accents and align input hover, active, and error outlines across light and dark themes.

Validated with semantic contrast checks across presets and custom seeds, frontend typecheck, lint and production build.

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
Zyy
2026-09-06 12:08:34 +08:00
co-authored by Codex
parent 2585d4cd7d
commit 4f434ec525
29 changed files with 126 additions and 71 deletions
+1 -1
View File
@@ -35,7 +35,7 @@ const variantClasses: Record<ButtonVariant, string> = {
'bg-cp-fill-tertiary text-cp-text shadow-cp-tertiary hover:bg-cp-bg-text-active active:bg-cp-bg-text-active',
ghost:
'bg-transparent text-cp-text-secondary shadow-none hover:bg-cp-fill-quaternary hover:text-cp-text active:bg-cp-fill-tertiary',
destructive: 'bg-cp-error-bg text-cp-error-text shadow-none hover:bg-cp-error-bg-hover active:bg-cp-error-bg-active',
destructive: 'bg-cp-error-bg text-cp-error-text-on-bg shadow-none hover:bg-cp-error-bg-hover active:bg-cp-error-bg-active',
}
const sizeClasses: Record<ButtonSize, string> = {
@@ -36,10 +36,10 @@ const variantClasses: Record<IconButtonVariant, string> = {
secondary:
'bg-cp-bg-container text-cp-text-secondary shadow-cp-tertiary hover:bg-cp-bg-text-hover hover:text-cp-text active:bg-cp-bg-text-active',
success:
'bg-cp-success-bg text-cp-success-text shadow-none hover:bg-cp-success-bg-hover active:bg-cp-success-bg-active',
'bg-cp-success-bg text-cp-success-text-on-bg shadow-none hover:bg-cp-success-bg-hover active:bg-cp-success-bg-active',
ghost:
'bg-transparent text-cp-text-secondary shadow-none hover:bg-cp-fill-quaternary hover:text-cp-text active:bg-cp-fill-tertiary',
destructive: 'bg-transparent text-cp-error-text shadow-none hover:bg-cp-error-bg-hover active:bg-cp-error-bg-active',
destructive: 'bg-transparent text-cp-error-text shadow-none hover:bg-cp-error-bg-hover hover:text-cp-error-text-on-bg active:bg-cp-error-bg-active active:text-cp-error-text-on-bg',
}
const sizeClasses: Record<IconButtonSize, string> = {
@@ -26,7 +26,7 @@ const classes = computed(() => [
'focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-cp-control-outline',
'disabled:cursor-not-allowed disabled:text-cp-text-disabled',
props.tone === 'destructive'
? 'text-cp-error-text hover:bg-cp-error-bg-hover'
? 'text-cp-error-text hover:bg-cp-error-bg-hover hover:text-cp-error-text-on-bg'
: 'text-cp-text hover:bg-cp-bg-text-hover',
])
</script>
+1 -1
View File
@@ -98,7 +98,7 @@ const triggerClasses = computed(() => [
props.disabled
? 'cursor-not-allowed bg-cp-bg-container-disabled text-cp-text-disabled shadow-none'
: invalid.value
? 'cursor-pointer bg-(--cp-input-error-active-bg) text-cp-error-text shadow-cp-input-error-active'
? 'cursor-pointer bg-(--cp-input-error-active-bg) text-cp-error-text-on-bg shadow-cp-input-error-active'
: open.value
? 'cursor-pointer bg-(--cp-input-active-bg) text-cp-text shadow-cp-input-active'
: [
@@ -342,7 +342,7 @@ onBeforeUnmount(() => {
class="inline-flex h-4.5 min-w-0 cursor-pointer items-center gap-1 rounded-cp-sm border-0 px-1.5 font-mono text-[10px] leading-none font-bold transition-colors outline-none focus-visible:ring-2 focus-visible:ring-cp-control-outline focus-visible:ring-offset-2 focus-visible:ring-offset-cp-bg-container"
:class="[
hasUpdate
? 'bg-cp-success-bg text-cp-success-text hover:bg-cp-success-bg-hover'
? 'bg-cp-success-bg text-cp-success-text-on-bg hover:bg-cp-success-bg-hover'
: 'bg-cp-fill-quaternary text-cp-text-quaternary hover:bg-cp-fill-tertiary hover:text-cp-text-secondary',
]"
:title="updateButtonLabel"
@@ -399,7 +399,7 @@ onBeforeUnmount(() => {
>
<span
v-if="!isCollapsed"
class="inline-flex whitespace-nowrap h-7 items-center gap-1.5 rounded-lg bg-cp-success-bg px-2.5 text-xs leading-none font-emphasis text-cp-success-text"
class="inline-flex whitespace-nowrap h-7 items-center gap-1.5 rounded-lg bg-cp-success-bg px-2.5 text-xs leading-none font-emphasis text-cp-success-text-on-bg"
>
<i class="size-1.5 rounded-full bg-cp-success" />
在线
@@ -257,7 +257,7 @@ watch(
<p
v-if="updateError || updateInfo?.warning"
class="m-0 rounded-cp bg-cp-error-bg px-3 py-2 text-cp-sm leading-normal font-bold text-cp-error-text"
class="m-0 rounded-cp bg-cp-error-bg px-3 py-2 text-cp-sm leading-normal font-bold text-cp-error-text-on-bg"
>
{{ updateError || updateInfo?.warning }}
</p>
@@ -47,7 +47,7 @@ function resolveStatus(input: SystemUpdatePresentationInput) {
return {
label: input.restarting ? '重启中' : '更新中',
icon: RefreshCw,
badge: 'bg-cp-info-bg text-cp-info-text',
badge: 'bg-cp-info-bg text-cp-info-text-on-bg',
iconClass: 'text-cp-info',
}
}
@@ -55,7 +55,7 @@ function resolveStatus(input: SystemUpdatePresentationInput) {
return {
label: '异常',
icon: XCircle,
badge: 'bg-cp-error-bg text-cp-error-text',
badge: 'bg-cp-error-bg text-cp-error-text-on-bg',
iconClass: 'text-cp-error',
}
}
@@ -63,7 +63,7 @@ function resolveStatus(input: SystemUpdatePresentationInput) {
return {
label: input.updateSuccess ? '已更新' : input.hasUpdate ? '有新版本' : '已是最新',
icon: input.hasUpdate ? ArrowUpCircle : CheckCircle2,
badge: 'bg-cp-success-bg text-cp-success-text',
badge: 'bg-cp-success-bg text-cp-success-text-on-bg',
iconClass: 'text-cp-success',
}
}
+4
View File
@@ -72,6 +72,7 @@
--color-cp-info-active: var(--cp-color-info-active);
--color-cp-info-text-hover: var(--cp-color-info-text-hover);
--color-cp-info-text: var(--cp-color-info-text);
--color-cp-info-text-on-bg: var(--cp-color-info-text-on-bg);
--color-cp-info-text-active: var(--cp-color-info-text-active);
--color-cp-success-bg: var(--cp-color-success-bg);
--color-cp-success-bg-hover: var(--cp-color-success-bg-hover);
@@ -83,6 +84,7 @@
--color-cp-success-active: var(--cp-color-success-active);
--color-cp-success-text-hover: var(--cp-color-success-text-hover);
--color-cp-success-text: var(--cp-color-success-text);
--color-cp-success-text-on-bg: var(--cp-color-success-text-on-bg);
--color-cp-success-text-active: var(--cp-color-success-text-active);
--color-cp-warning-bg: var(--cp-color-warning-bg);
--color-cp-warning-bg-hover: var(--cp-color-warning-bg-hover);
@@ -94,6 +96,7 @@
--color-cp-warning-active: var(--cp-color-warning-active);
--color-cp-warning-text-hover: var(--cp-color-warning-text-hover);
--color-cp-warning-text: var(--cp-color-warning-text);
--color-cp-warning-text-on-bg: var(--cp-color-warning-text-on-bg);
--color-cp-warning-text-active: var(--cp-color-warning-text-active);
--color-cp-error-bg: var(--cp-color-error-bg);
--color-cp-error-bg-hover: var(--cp-color-error-bg-hover);
@@ -105,6 +108,7 @@
--color-cp-error-active: var(--cp-color-error-active);
--color-cp-error-text-hover: var(--cp-color-error-text-hover);
--color-cp-error-text: var(--cp-color-error-text);
--color-cp-error-text-on-bg: var(--cp-color-error-text-on-bg);
--color-cp-error-text-active: var(--cp-color-error-text-active);
/* 颜色 · 预设(色系按字母排序,角色顺序固定) */
+1 -1
View File
@@ -100,7 +100,7 @@ function deriveThemeMap(
const aliases = deriveThemeAliasMap(surfaces)
const primary = deriveThemePrimaryMap(seed, name, surfaces.colorBgContainer)
const link = deriveThemeLinkMap(seedTokens.colorLink, name, surfaces.colorBgContainer)
const semantics = deriveThemeSemanticMap(name, surfaces.colorBgContainer, seedTokens)
const semantics = deriveThemeSemanticMap(name, surfaces, seedTokens)
return {
surfaces,
+1
View File
@@ -38,6 +38,7 @@ const FUNCTIONAL_TOKEN_ROLES = [
['active', '-active'],
['textHover', '-text-hover'],
['text', '-text'],
['textOnBackground', '-text-on-bg'],
['textActive', '-text-active'],
] as const satisfies readonly (readonly [keyof FunctionalColorMap, FunctionalTokenSuffix])[]
+51 -8
View File
@@ -133,16 +133,51 @@ export function deriveThemeLinkMap(
export function deriveThemeSemanticMap(
theme: ThemeName,
containerBg: string,
surfaces: ThemeSurfaceMap,
seedTokens: ResolvedThemeSeedTokens,
): ThemeSemanticMap {
const recipe = THEME_COLOR_ROLE_RECIPES[theme].semantic
const containerBg = surfaces.colorBgContainer
const textBackgrounds = [
surfaces.colorBgLayout,
surfaces.colorBgContainer,
surfaces.colorBgElevated,
surfaces.colorBgTextHover,
surfaces.colorBgTextActive,
surfaces.colorFillSecondary,
surfaces.colorFillTertiary,
surfaces.colorFillQuaternary,
]
return {
info: deriveColorRoleMap(seedTokens.colorInfo, theme, containerBg, recipe),
success: deriveColorRoleMap(seedTokens.colorSuccess, theme, containerBg, recipe),
warning: deriveColorRoleMap(seedTokens.colorWarning, theme, containerBg, recipe),
error: deriveColorRoleMap(seedTokens.colorError, theme, containerBg, recipe),
info: deriveColorRoleMap(
seedTokens.colorInfo,
theme,
containerBg,
recipe,
textBackgrounds,
),
success: deriveColorRoleMap(
seedTokens.colorSuccess,
theme,
containerBg,
recipe,
textBackgrounds,
),
warning: deriveColorRoleMap(
seedTokens.colorWarning,
theme,
containerBg,
recipe,
textBackgrounds,
),
error: deriveColorRoleMap(
seedTokens.colorError,
theme,
containerBg,
recipe,
textBackgrounds,
),
}
}
@@ -409,6 +444,7 @@ function deriveColorRoleMap(
theme: ThemeName,
containerBg: string,
recipe: ThemeColorRoleRecipe,
textBackgrounds: string | readonly string[] = containerBg,
): FunctionalColorMap {
const normalizedSeed = normalizeHexColor(seed) ?? DEFAULT_CUSTOM_THEME_COLOR
const palette = generateColorPalette(normalizedSeed, theme, containerBg)
@@ -421,13 +457,16 @@ function deriveColorRoleMap(
const background = mix(containerBg, color, recipe.backgroundMix)
const backgroundHover = mix(containerBg, hover, recipe.backgroundHoverMix)
const backgroundActive = mix(containerBg, active, recipe.backgroundActiveMix)
const textBackgrounds = [containerBg, background, backgroundHover, backgroundActive]
const resolveText = (source: ThemePaletteSource): string => {
const semanticBackgrounds = [background, backgroundHover, backgroundActive]
const resolveText = (
source: ThemePaletteSource,
backgrounds: string | readonly string[] = textBackgrounds,
): string => {
const value = resolve(source)
const toned = recipe.minimumTextLightness === undefined
? value
: ensureLightness(value, recipe.minimumTextLightness)
return ensureContrast(toned, textBackgrounds, 4.5)
return ensureContrast(toned, backgrounds, 4.5)
}
return {
@@ -445,6 +484,10 @@ function deriveColorRoleMap(
: ensureContrast(active, containerBg, recipe.activeContrast),
textHover: resolveText(recipe.textHover),
text: resolveText(recipe.text),
textOnBackground: resolveText(
recipe.textOnBackground ?? recipe.text,
semanticBackgrounds,
),
textActive: resolveText(recipe.textActive),
}
}
+4 -4
View File
@@ -184,8 +184,8 @@ function deriveDarkComponentMap(
`0 0 0 3px ${inputOutlineColor}, 0 14px 28px -22px ${withAlpha(BLACK, 0.72)}`,
shadowStrength,
),
inputActiveShadow: '0 0 0 3px var(--cp-control-outline)',
inputErrorActiveShadow: `0 0 0 3px ${error.border}`,
inputActiveShadow: `0 0 0 3px ${inputOutlineColor}`,
inputErrorActiveShadow: `0 0 0 3px ${withAlpha(error.color, 0.28)}`,
layoutSiderShadow: scaleShadowAlpha(`2px 0 18px -14px ${withAlpha(BLACK, 0.72)}`, shadowStrength),
scrollbarThumbBg: mix(surfaces.colorBorderSecondary, surfaces.colorTextSecondary, 0.16),
scrollbarThumbHoverBg: mix(surfaces.colorBorderSecondary, surfaces.colorTextSecondary, 0.3),
@@ -237,8 +237,8 @@ function deriveLightComponentMap(
`0 0 0 3px ${inputOutlineColor}, 0 12px 24px -16px ${withAlpha(LIGHT_SHADOW_BASE, 0.12)}`,
shadowStrength,
),
inputActiveShadow: '0 0 0 3px var(--cp-control-outline)',
inputErrorActiveShadow: `0 0 0 3px ${error.border}`,
inputActiveShadow: `0 0 0 3px ${inputOutlineColor}`,
inputErrorActiveShadow: `0 0 0 3px ${withAlpha(error.color, 0.18)}`,
layoutSiderShadow: scaleShadowAlpha(`2px 0 12px -12px ${withAlpha(LIGHT_SHADOW_BASE, 0.027)}`, shadowStrength),
scrollbarThumbBg: mix(surfaces.colorBgLayout, surfaces.colorTextSecondary, 0.2),
scrollbarThumbHoverBg: mix(surfaces.colorBgLayout, surfaces.colorTextSecondary, 0.34),
+4 -2
View File
@@ -24,7 +24,8 @@ export const THEME_COLOR_ROLE_RECIPES = {
hover: 5,
active: 7,
textHover: 5,
text: 6,
text: 7,
textOnBackground: 6,
textActive: 7,
backgroundMix: 0.08,
backgroundHoverMix: 0.12,
@@ -62,7 +63,8 @@ export const THEME_COLOR_ROLE_RECIPES = {
hover: 8,
active: 6,
textHover: 8,
text: 9,
text: 7,
textOnBackground: 9,
textActive: 10,
backgroundMix: 0.18,
backgroundHoverMix: 0.24,
+5
View File
@@ -90,6 +90,7 @@ export interface ThemeColorRoleRecipe {
active: ThemePaletteSource
textHover: ThemePaletteSource
text: ThemePaletteSource
textOnBackground?: ThemePaletteSource
textActive: ThemePaletteSource
backgroundMix: number
backgroundHoverMix: number
@@ -211,7 +212,10 @@ export interface FunctionalColorMap {
border: string
borderHover: string
textHover: string
/** 语义色在中性表面上的强调文字。 */
text: string
/** 语义色在自身背景上的高对比文字。 */
textOnBackground: string
textActive: string
}
@@ -308,6 +312,7 @@ export type FunctionalTokenSuffix
| '-border-hover'
| '-text-hover'
| '-text'
| '-text-on-bg'
| '-text-active'
type SemanticColorName = Extract<keyof ThemeSemanticMap, string>
+1 -1
View File
@@ -121,7 +121,7 @@ const { allSelected, indeterminate, selectedRowKeys, toggleSelection, toggleAll
<span
class="inline-flex h-6 items-center rounded-lg px-2 text-cp-xs font-bold"
:class="row.enabled
? 'bg-cp-success-bg text-cp-success-text'
? 'bg-cp-success-bg text-cp-success-text-on-bg'
: 'bg-cp-fill-tertiary text-cp-text-quaternary'"
>
{{ row.enabled ? '已启用' : '已禁用' }}
@@ -50,7 +50,7 @@ function overviewIconClass(tone: string) {
return 'bg-cp-orange-bg text-cp-orange-text-on-bg'
}
if (tone === 'danger') {
return 'bg-cp-error-bg text-cp-error-text'
return 'bg-cp-error-bg text-cp-error-text-on-bg'
}
return 'bg-cp-blue-bg text-cp-blue-text-on-bg'
}
@@ -140,9 +140,9 @@ async function handleConfirmConsume() {
<div v-else class="grid gap-4">
<section v-if="ambiguous" class="flex items-start gap-3 rounded-cp bg-cp-warning-bg px-4 py-3.5" role="status">
<AlertTriangle class="mt-0.5 size-4 shrink-0 text-cp-warning-text" />
<AlertTriangle class="mt-0.5 size-4 shrink-0 text-cp-warning-text-on-bg" />
<div class="min-w-0">
<p class="m-0 text-cp-sm font-heavy text-cp-warning-text">
<p class="m-0 text-cp-sm font-heavy text-cp-warning-text-on-bg">
上次操作结果待确认
</p>
<p class="mt-1 mb-0 text-cp-xs leading-normal font-emphasis text-cp-text-secondary">
@@ -193,7 +193,7 @@ async function handleConfirmConsume() {
<p
v-if="loadError"
class="m-0 rounded-cp bg-cp-error-bg px-4 py-3 text-cp-xs leading-normal font-emphasis text-cp-error-text"
class="m-0 rounded-cp bg-cp-error-bg px-4 py-3 text-cp-xs leading-normal font-emphasis text-cp-error-text-on-bg"
role="status"
>
{{ loadError }},请刷新重试。
@@ -53,26 +53,26 @@ const statusStyles: Record<AccountStatusTone, AccountStatusStyle> = {
success: {
text: 'text-cp-success-text',
dot: 'bg-cp-success',
badge: 'bg-cp-success-bg text-cp-success-text',
icon: 'bg-cp-success-bg text-cp-success-text',
badge: 'bg-cp-success-bg text-cp-success-text-on-bg',
icon: 'bg-cp-success-bg text-cp-success-text-on-bg',
},
danger: {
text: 'text-cp-error-text',
dot: 'bg-cp-error',
badge: 'bg-cp-error-bg text-cp-error-text',
icon: 'bg-cp-error-bg text-cp-error-text',
badge: 'bg-cp-error-bg text-cp-error-text-on-bg',
icon: 'bg-cp-error-bg text-cp-error-text-on-bg',
},
warning: {
text: 'text-cp-warning-text',
dot: 'bg-cp-warning',
badge: 'bg-cp-warning-bg text-cp-warning-text',
icon: 'bg-cp-warning-bg text-cp-warning-text',
badge: 'bg-cp-warning-bg text-cp-warning-text-on-bg',
icon: 'bg-cp-warning-bg text-cp-warning-text-on-bg',
},
info: {
text: 'text-cp-info-text',
dot: 'bg-cp-info',
badge: 'bg-cp-info-bg text-cp-info-text',
icon: 'bg-cp-info-bg text-cp-info-text',
badge: 'bg-cp-info-bg text-cp-info-text-on-bg',
icon: 'bg-cp-info-bg text-cp-info-text-on-bg',
},
normal: {
text: 'text-cp-text-secondary',
@@ -191,7 +191,7 @@ export function useAccountConnectionTest(options: { reload: () => Promise<unknow
label: '正在测试',
description: '正在向所选模型发送请求并接收流式响应',
icon: Clock3,
badge: 'bg-cp-info-bg text-cp-info-text',
badge: 'bg-cp-info-bg text-cp-info-text-on-bg',
iconClass: 'text-cp-info',
}
}
@@ -200,7 +200,7 @@ export function useAccountConnectionTest(options: { reload: () => Promise<unknow
label: '连接正常',
description: '请求已完成,可在下方查看模型、耗时和事件轨迹',
icon: CheckCircle2,
badge: 'bg-cp-success-bg text-cp-success-text',
badge: 'bg-cp-success-bg text-cp-success-text-on-bg',
iconClass: 'text-cp-success',
}
}
@@ -209,7 +209,7 @@ export function useAccountConnectionTest(options: { reload: () => Promise<unknow
label: '测试失败',
description: '请求未完成,请在下方查看失败来源与原始诊断',
icon: XCircle,
badge: 'bg-cp-error-bg text-cp-error-text',
badge: 'bg-cp-error-bg text-cp-error-text-on-bg',
iconClass: 'text-cp-error',
}
}
@@ -111,7 +111,7 @@ const title = computed(() => props.editing ? '编辑 API Key' : '创建 API Key'
>
<div class="flex flex-col gap-4">
<div class="rounded-cp border border-cp-warning-border bg-cp-warning-bg px-4 py-3">
<p class="m-0 text-cp font-semibold text-cp-warning-text">
<p class="m-0 text-cp font-semibold text-cp-warning-text-on-bg">
该密钥具有网关访问权限,请仅发送给可信调用方
</p>
</div>
@@ -11,7 +11,7 @@ defineProps<{
<template>
<div class="grid w-full justify-items-center gap-1.5">
<div v-if="apiKey.routingScope === 'all'" class="flex items-center justify-center gap-2">
<span class="inline-flex h-6 items-center rounded-lg bg-cp-warning-bg px-2 text-cp-xs font-bold text-cp-warning-text">
<span class="inline-flex h-6 items-center rounded-lg bg-cp-warning-bg px-2 text-cp-xs font-bold text-cp-warning-text-on-bg">
全部账号权限
</span>
</div>
@@ -10,7 +10,7 @@ defineProps<{
<span
class="inline-flex h-6 min-w-14 items-center justify-center rounded-full px-2 text-cp-sm leading-none font-bold"
:class="{
'bg-cp-success-bg text-cp-success-text': apiKey.enabled,
'bg-cp-success-bg text-cp-success-text-on-bg': apiKey.enabled,
'bg-cp-fill-quaternary text-cp-text-secondary': !apiKey.enabled,
}"
>
@@ -75,7 +75,7 @@ const releaseStatus = computed(() => {
return {
label: '当前生效',
title: '当前 Provider 请求正在使用此运行时画像',
tone: 'bg-cp-info-bg text-cp-info-text',
tone: 'bg-cp-info-bg text-cp-info-text-on-bg',
icon: ShieldCheck,
}
}
@@ -85,7 +85,7 @@ const releaseStatus = computed(() => {
return {
label: '制品一致',
title: '当前生效版本与官方最新发布一致',
tone: 'bg-cp-success-bg text-cp-success-text',
tone: 'bg-cp-success-bg text-cp-success-text-on-bg',
icon: CheckCircle2,
}
}
@@ -93,7 +93,7 @@ const releaseStatus = computed(() => {
return {
label: '发现新版',
title: `官方最新版本 ${releaseLabel.value}`,
tone: 'bg-cp-warning-bg text-cp-warning-text',
tone: 'bg-cp-warning-bg text-cp-warning-text-on-bg',
icon: TriangleAlert,
}
}
@@ -101,7 +101,7 @@ const releaseStatus = computed(() => {
return {
label: '检查失败',
title: current.release.error || '官方版本检查失败',
tone: 'bg-cp-error-bg text-cp-error-text',
tone: 'bg-cp-error-bg text-cp-error-text-on-bg',
icon: TriangleAlert,
}
}
+4 -4
View File
@@ -7,7 +7,7 @@ export const metricToneIconClasses: Record<MetricTone, string> = {
info: 'bg-cp-blue-bg text-cp-blue-text-on-bg',
success: 'bg-cp-green-bg text-cp-green-text-on-bg',
warning: 'bg-cp-orange-bg text-cp-orange-text-on-bg',
danger: 'bg-cp-error-bg text-cp-error-text',
danger: 'bg-cp-error-bg text-cp-error-text-on-bg',
}
export const metricToneValueClasses: Record<MetricTone, string> = {
@@ -71,12 +71,12 @@ export const healthStatusMeta: Record<HealthStatus, HealthStatusMeta> = {
unavailable: {
label: '不可达',
cellClass: 'bg-cp-error',
badgeClass: 'bg-cp-error-bg text-cp-error-text',
badgeClass: 'bg-cp-error-bg text-cp-error-text-on-bg',
},
unstable: {
label: '不稳定',
cellClass: 'bg-cp-warning',
badgeClass: 'bg-cp-warning-bg text-cp-warning-text',
badgeClass: 'bg-cp-warning-bg text-cp-warning-text-on-bg',
},
low_sample: {
label: '低样本',
@@ -86,7 +86,7 @@ export const healthStatusMeta: Record<HealthStatus, HealthStatusMeta> = {
stable: {
label: '稳定',
cellClass: 'bg-cp-success',
badgeClass: 'bg-cp-success-bg text-cp-success-text',
badgeClass: 'bg-cp-success-bg text-cp-success-text-on-bg',
},
}
@@ -92,14 +92,14 @@ function formatFileSize(value: number | null): string {
<p
v-if="downloads?.warning"
class="m-0 rounded-cp bg-cp-warning-bg px-3 py-2.5 text-cp-sm leading-[1.45] font-bold text-cp-warning-text"
class="m-0 rounded-cp bg-cp-warning-bg px-3 py-2.5 text-cp-sm leading-[1.45] font-bold text-cp-warning-text-on-bg"
>
{{ downloads.warning }}
</p>
<p
v-if="error && downloads"
class="m-0 rounded-cp bg-cp-error-bg px-3 py-2.5 text-cp-sm leading-[1.45] font-bold text-cp-error-text"
class="m-0 rounded-cp bg-cp-error-bg px-3 py-2.5 text-cp-sm leading-[1.45] font-bold text-cp-error-text-on-bg"
role="alert"
>
{{ error }},当前仍显示上次成功结果。
@@ -112,11 +112,11 @@ const pagination: BaseTablePaginationState = {
function statusClass(status: PreviewTableRow['status']) {
if (status === '正常')
return 'bg-cp-success-bg text-cp-success-text'
return 'bg-cp-success-bg text-cp-success-text-on-bg'
if (status === '限流')
return 'bg-cp-warning-bg text-cp-warning-text'
return 'bg-cp-warning-bg text-cp-warning-text-on-bg'
if (status === '异常')
return 'bg-cp-error-bg text-cp-error-text'
return 'bg-cp-error-bg text-cp-error-text-on-bg'
return 'bg-cp-fill-tertiary text-cp-text-secondary'
}
</script>
@@ -300,16 +300,16 @@ function statusClass(status: PreviewTableRow['status']) {
<BaseCard padding="compact" class="col-span-4 h-full min-w-0 bg-cp-fill-quaternary! shadow-none!" title="反馈语义" description="Success · Warning · Error · Info">
<template #body>
<div class="grid gap-2.5">
<div class="flex items-center gap-3 rounded-cp bg-cp-success-bg px-3.5 py-3 text-cp-success-text">
<div class="flex items-center gap-3 rounded-cp bg-cp-success-bg px-3.5 py-3 text-cp-success-text-on-bg">
<Check class="size-4 shrink-0" /><strong class="text-cp-sm">主题设置已保存</strong>
</div>
<div class="flex items-center gap-3 rounded-cp bg-cp-warning-bg px-3.5 py-3 text-cp-warning-text">
<div class="flex items-center gap-3 rounded-cp bg-cp-warning-bg px-3.5 py-3 text-cp-warning-text-on-bg">
<TriangleAlert class="size-4 shrink-0" /><strong class="text-cp-sm">3 个账号额度接近上限</strong>
</div>
<div class="flex items-center gap-3 rounded-cp bg-cp-error-bg px-3.5 py-3 text-cp-error-text">
<div class="flex items-center gap-3 rounded-cp bg-cp-error-bg px-3.5 py-3 text-cp-error-text-on-bg">
<CircleAlert class="size-4 shrink-0" /><strong class="text-cp-sm">连接测试未通过</strong>
</div>
<div class="flex items-center gap-3 rounded-cp bg-cp-info-bg px-3.5 py-3 text-cp-info-text">
<div class="flex items-center gap-3 rounded-cp bg-cp-info-bg px-3.5 py-3 text-cp-info-text-on-bg">
<Info class="size-4 shrink-0" /><strong class="text-cp-sm">调度规则将在下次请求生效</strong>
</div>
</div>
@@ -320,10 +320,10 @@ function statusClass(status: PreviewTableRow['status']) {
<template #body>
<div class="grid gap-4">
<div class="flex flex-wrap gap-2">
<span class="rounded-full bg-cp-success-bg px-3 py-1.5 text-cp-xs font-heavy text-cp-success-text">运行正常</span>
<span class="rounded-full bg-cp-warning-bg px-3 py-1.5 text-cp-xs font-heavy text-cp-warning-text">额度受限</span>
<span class="rounded-full bg-cp-error-bg px-3 py-1.5 text-cp-xs font-heavy text-cp-error-text">需要处理</span>
<span class="rounded-full bg-cp-info-bg px-3 py-1.5 text-cp-xs font-heavy text-cp-info-text">同步中</span>
<span class="rounded-full bg-cp-success-bg px-3 py-1.5 text-cp-xs font-heavy text-cp-success-text-on-bg">运行正常</span>
<span class="rounded-full bg-cp-warning-bg px-3 py-1.5 text-cp-xs font-heavy text-cp-warning-text-on-bg">额度受限</span>
<span class="rounded-full bg-cp-error-bg px-3 py-1.5 text-cp-xs font-heavy text-cp-error-text-on-bg">需要处理</span>
<span class="rounded-full bg-cp-info-bg px-3 py-1.5 text-cp-xs font-heavy text-cp-info-text-on-bg">同步中</span>
</div>
<div v-for="item in progressItems" :key="item.label" class="grid gap-2">
<div class="flex justify-between text-cp-sm font-heavy text-cp-text-secondary">
@@ -130,7 +130,7 @@ function upstreamSendStateText(value: string | null | undefined) {
</code>
<span
v-if="row.metadata.recoveredAt"
class="inline-flex h-5 shrink-0 items-center rounded-full bg-cp-success-bg px-2 text-cp-xs leading-none font-heavy text-cp-success-text"
class="inline-flex h-5 shrink-0 items-center rounded-full bg-cp-success-bg px-2 text-cp-xs leading-none font-heavy text-cp-success-text-on-bg"
>
已自动恢复
</span>
@@ -45,7 +45,7 @@ const latencyDetails = computed(() => usageLatencyDetails(props.record))
<div class="flex justify-between border-t border-cp-split pt-2">
<span class="whitespace-nowrap text-cp-text-secondary">总耗时</span>
<span class="whitespace-nowrap font-mono font-heavy text-cp-blue-text">
<span class="whitespace-nowrap font-mono font-heavy text-cp-info-text">
{{ latencyDetails.totalDisplay }}
</span>
</div>
@@ -9,14 +9,14 @@ function statusClass(statusCode?: number | null) {
}
if (statusCode >= 200 && statusCode < 300) {
return 'bg-cp-success-bg text-cp-success-text'
return 'bg-cp-success-bg text-cp-success-text-on-bg'
}
if (statusCode >= 300 && statusCode < 400) {
return 'bg-cp-warning-bg text-cp-warning-text'
return 'bg-cp-warning-bg text-cp-warning-text-on-bg'
}
return 'bg-cp-error-bg text-cp-error-text'
return 'bg-cp-error-bg text-cp-error-text-on-bg'
}
</script>